Merge pull request #206 from SChernykh/reduce_memory

Reduced memory usage
This commit is contained in:
SChernykh 2022-10-06 20:01:55 +03:00 committed by GitHub
commit 3e655961e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 367 additions and 216 deletions

View file

@ -165,8 +165,11 @@ BlockCache::~BlockCache()
void BlockCache::store(const PoolBlock& block) void BlockCache::store(const PoolBlock& block)
{ {
const size_t n1 = block.m_mainChainData.size(); const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data();
const size_t n2 = block.m_sideChainData.size(); const std::vector<uint8_t> sidechain_data = block.serialize_sidechain_data();
const size_t n1 = mainchain_data.size();
const size_t n2 = sidechain_data.size();
if (!m_impl->m_data || (sizeof(uint32_t) + n1 + n2 > BLOCK_SIZE)) { if (!m_impl->m_data || (sizeof(uint32_t) + n1 + n2 > BLOCK_SIZE)) {
return; return;
@ -175,8 +178,8 @@ void BlockCache::store(const PoolBlock& block)
uint8_t* data = m_impl->m_data + (static_cast<size_t>((m_storeIndex++) % NUM_BLOCKS) * BLOCK_SIZE); uint8_t* data = m_impl->m_data + (static_cast<size_t>((m_storeIndex++) % NUM_BLOCKS) * BLOCK_SIZE);
*reinterpret_cast<uint32_t*>(data) = static_cast<uint32_t>(n1 + n2); *reinterpret_cast<uint32_t*>(data) = static_cast<uint32_t>(n1 + n2);
memcpy(data + sizeof(uint32_t), block.m_mainChainData.data(), n1); memcpy(data + sizeof(uint32_t), mainchain_data.data(), n1);
memcpy(data + sizeof(uint32_t) + n1, block.m_sideChainData.data(), n2); memcpy(data + sizeof(uint32_t) + n1, sidechain_data.data(), n2);
} }
void BlockCache::load_all(SideChain& side_chain, P2PServer& server) void BlockCache::load_all(SideChain& side_chain, P2PServer& server)

View file

@ -528,15 +528,16 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE);
m_poolBlockTemplate->serialize_mainchain_data(0, 0, m_poolBlockTemplate->m_sidechainId);
#if POOL_BLOCK_DEBUG #if POOL_BLOCK_DEBUG
if (m_poolBlockTemplate->m_mainChainData != m_blockTemplateBlob) { const std::vector<uint8_t> mainchain_data = m_poolBlockTemplate->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
if (mainchain_data != m_blockTemplateBlob) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it! "); LOGERR(1, "serialize_mainchain_data() has a bug, fix it! ");
LOGERR(1, "m_poolBlockTemplate->m_mainChainData.size() = " << m_poolBlockTemplate->m_mainChainData.size()); LOGERR(1, "m_poolBlockTemplate->m_mainChainData.size() = " << mainchain_data.size());
LOGERR(1, "m_blockTemplateBlob.size() = " << m_blockTemplateBlob.size()); LOGERR(1, "m_blockTemplateBlob.size() = " << m_blockTemplateBlob.size());
for (size_t i = 0, n = std::min(m_poolBlockTemplate->m_mainChainData.size(), m_blockTemplateBlob.size()); i < n; ++i) { for (size_t i = 0, n = std::min(mainchain_data.size(), m_blockTemplateBlob.size()); i < n; ++i) {
if (m_poolBlockTemplate->m_mainChainData[i] != m_blockTemplateBlob[i]) { if (mainchain_data[i] != m_blockTemplateBlob[i]) {
LOGERR(1, "m_poolBlockTemplate->m_mainChainData is different at offset " << i); LOGERR(1, "m_poolBlockTemplate->m_mainChainData is different at offset " << i);
break; break;
} }
@ -545,7 +546,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
{ {
std::vector<uint8_t> buf = m_blockTemplateBlob; std::vector<uint8_t> buf = m_blockTemplateBlob;
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end()); buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
PoolBlock check; PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain(), nullptr); const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain(), nullptr);
@ -712,7 +713,7 @@ int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<Mine
LOGERR(1, "get_eph_public_key failed at index " << i); LOGERR(1, "get_eph_public_key failed at index " << i);
} }
m_minerTx.insert(m_minerTx.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE); m_minerTx.insert(m_minerTx.end(), eph_public_key.h, eph_public_key.h + HASH_SIZE);
m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, tx_type, view_tag); m_poolBlockTemplate->m_outputs.emplace_back(m_rewards[i], eph_public_key, view_tag);
} }
if (tx_type == TXOUT_TO_TAGGED_KEY) { if (tx_type == TXOUT_TO_TAGGED_KEY) {
@ -784,8 +785,9 @@ hash BlockTemplate::calc_sidechain_hash() const
const int blob_size = static_cast<int>(m_blockTemplateBlob.size()); const int blob_size = static_cast<int>(m_blockTemplateBlob.size());
const std::vector<uint8_t>& consensus_id = m_pool->side_chain().consensus_id(); const std::vector<uint8_t>& consensus_id = m_pool->side_chain().consensus_id();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id](int offset) -> uint8_t { keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id, &sidechain_data](int offset) -> uint8_t {
uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(m_nonceOffset)); uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(m_nonceOffset));
if (k < NONCE_SIZE) { if (k < NONCE_SIZE) {
return 0; return 0;
@ -806,15 +808,15 @@ hash BlockTemplate::calc_sidechain_hash() const
} }
const int side_chain_data_offsset = offset - blob_size; const int side_chain_data_offsset = offset - blob_size;
const int side_chain_data_size = static_cast<int>(m_poolBlockTemplate->m_sideChainData.size()); const int side_chain_data_size = static_cast<int>(sidechain_data.size());
if (side_chain_data_offsset < side_chain_data_size) { if (side_chain_data_offsset < side_chain_data_size) {
return m_poolBlockTemplate->m_sideChainData[side_chain_data_offsset]; return sidechain_data[side_chain_data_offsset];
} }
const int consensus_id_offset = side_chain_data_offsset - side_chain_data_size; const int consensus_id_offset = side_chain_data_offsset - side_chain_data_size;
return consensus_id[consensus_id_offset]; return consensus_id[consensus_id_offset];
}, },
static_cast<int>(m_blockTemplateBlob.size() + m_poolBlockTemplate->m_sideChainData.size() + consensus_id.size()), sidechain_hash.h, HASH_SIZE); static_cast<int>(m_blockTemplateBlob.size() + sidechain_data.size() + consensus_id.size()), sidechain_hash.h, HASH_SIZE);
return sidechain_hash; return sidechain_hash;
} }
@ -1067,15 +1069,18 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
if (template_id == m_templateId) { if (template_id == m_templateId) {
m_poolBlockTemplate->m_nonce = nonce; m_poolBlockTemplate->m_nonce = nonce;
m_poolBlockTemplate->m_extraNonce = extra_nonce; m_poolBlockTemplate->m_extraNonce = extra_nonce;
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_nonceOffset, &nonce, NONCE_SIZE);
memcpy(m_poolBlockTemplate->m_mainChainData.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
SideChain& side_chain = m_pool->side_chain(); SideChain& side_chain = m_pool->side_chain();
#if POOL_BLOCK_DEBUG #if POOL_BLOCK_DEBUG
{ {
std::vector<uint8_t> buf = m_poolBlockTemplate->m_mainChainData; std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data();
buf.insert(buf.end(), m_poolBlockTemplate->m_sideChainData.begin(), m_poolBlockTemplate->m_sideChainData.end()); const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
memcpy(buf.data() + m_nonceOffset, &nonce, NONCE_SIZE);
memcpy(buf.data() + m_extraNonceOffsetInTemplate, &extra_nonce, EXTRA_NONCE_SIZE);
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
PoolBlock check; PoolBlock check;
const int result = check.deserialize(buf.data(), buf.size(), side_chain, nullptr); const int result = check.deserialize(buf.data(), buf.size(), side_chain, nullptr);

View file

@ -154,10 +154,13 @@ static FORCEINLINE void derivation_to_scalar(const hash& derivation, size_t outp
hash_to_scalar(data, static_cast<int>(p - data), res); hash_to_scalar(data, static_cast<int>(p - data), res);
} }
class Cache class Cache : public nocopy_nomove
{ {
public: public:
Cache() Cache()
: derivations(new DerivationsMap())
, public_keys(new PublicKeysMap())
, tx_keys(new TxKeysMap())
{ {
uv_rwlock_init_checked(&derivations_lock); uv_rwlock_init_checked(&derivations_lock);
uv_rwlock_init_checked(&public_keys_lock); uv_rwlock_init_checked(&public_keys_lock);
@ -166,6 +169,10 @@ public:
~Cache() ~Cache()
{ {
delete derivations;
delete public_keys;
delete tx_keys;
uv_rwlock_destroy(&derivations_lock); uv_rwlock_destroy(&derivations_lock);
uv_rwlock_destroy(&public_keys_lock); uv_rwlock_destroy(&public_keys_lock);
uv_rwlock_destroy(&tx_keys_lock); uv_rwlock_destroy(&tx_keys_lock);
@ -180,8 +187,8 @@ public:
derivation = {}; derivation = {};
{ {
ReadLock lock(derivations_lock); ReadLock lock(derivations_lock);
auto it = derivations.find(index); auto it = derivations->find(index);
if (it != derivations.end()) { if (it != derivations->end()) {
const DerivationEntry& entry = it->second; const DerivationEntry& entry = it->second;
derivation = entry.m_derivation; derivation = entry.m_derivation;
if (entry.find_view_tag(output_index, view_tag)) { if (entry.find_view_tag(output_index, view_tag)) {
@ -210,7 +217,7 @@ public:
{ {
WriteLock lock(derivations_lock); WriteLock lock(derivations_lock);
DerivationEntry& entry = derivations.emplace(index, DerivationEntry{ derivation, {} }).first->second; DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, {} }).first->second;
const uint32_t k = static_cast<uint32_t>(output_index << 8) | view_tag; const uint32_t k = static_cast<uint32_t>(output_index << 8) | view_tag;
if (std::find(entry.m_viewTags.begin(), entry.m_viewTags.end(), k) == entry.m_viewTags.end()) { if (std::find(entry.m_viewTags.begin(), entry.m_viewTags.end(), k) == entry.m_viewTags.end()) {
@ -230,8 +237,8 @@ public:
{ {
ReadLock lock(public_keys_lock); ReadLock lock(public_keys_lock);
auto it = public_keys.find(index); auto it = public_keys->find(index);
if (it != public_keys.end()) { if (it != public_keys->end()) {
derived_key = it->second; derived_key = it->second;
return true; return true;
} }
@ -257,7 +264,7 @@ public:
{ {
WriteLock lock(public_keys_lock); WriteLock lock(public_keys_lock);
public_keys.emplace(index, derived_key); public_keys->emplace(index, derived_key);
} }
return true; return true;
@ -271,8 +278,8 @@ public:
{ {
ReadLock lock(tx_keys_lock); ReadLock lock(tx_keys_lock);
auto it = tx_keys.find(index); auto it = tx_keys->find(index);
if (it != tx_keys.end()) { if (it != tx_keys->end()) {
pub = it->second.first; pub = it->second.first;
sec = it->second.second; sec = it->second.second;
return; return;
@ -291,15 +298,27 @@ public:
{ {
WriteLock lock(tx_keys_lock); WriteLock lock(tx_keys_lock);
tx_keys.emplace(index, std::pair<hash, hash>(pub, sec)); tx_keys->emplace(index, std::pair<hash, hash>(pub, sec));
} }
} }
void clear() void clear()
{ {
{ WriteLock lock(derivations_lock); derivations.clear(); } {
{ WriteLock lock(public_keys_lock); public_keys.clear(); } WriteLock lock(derivations_lock);
{ WriteLock lock(tx_keys_lock); tx_keys.clear(); } delete derivations;
derivations = new DerivationsMap();
}
{
WriteLock lock(public_keys_lock);
delete public_keys;
public_keys = new PublicKeysMap();
}
{
WriteLock lock(tx_keys_lock);
delete tx_keys;
tx_keys = new TxKeysMap();
}
} }
private: private:
@ -319,14 +338,18 @@ private:
} }
}; };
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> DerivationsMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> PublicKeysMap;
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> TxKeysMap;
uv_rwlock_t derivations_lock; uv_rwlock_t derivations_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> derivations; DerivationsMap* derivations;
uv_rwlock_t public_keys_lock; uv_rwlock_t public_keys_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys; PublicKeysMap* public_keys;
uv_rwlock_t tx_keys_lock; uv_rwlock_t tx_keys_lock;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> tx_keys; TxKeysMap* tx_keys;
}; };
static Cache* cache = nullptr; static Cache* cache = nullptr;
@ -379,7 +402,9 @@ void destroy_crypto_cache()
void clear_crypto_cache() void clear_crypto_cache()
{ {
cache->clear(); if (cache) {
cache->clear();
}
} }
} // namespace p2pool } // namespace p2pool

View file

@ -32,15 +32,48 @@ namespace p2pool {
static bool track_memory = false; static bool track_memory = false;
constexpr size_t N = 2097152; constexpr size_t N = 1 << 22;
constexpr size_t MAX_FRAMES = 30; constexpr size_t MAX_FRAMES = 29;
struct TrackedAllocation struct TrackedAllocation
{ {
void* p; void* p;
void* stack_trace[MAX_FRAMES]; void* stack_trace[MAX_FRAMES];
uint64_t allocated_size;
uint32_t thread_id; uint32_t thread_id;
uint32_t allocated_size;
FORCEINLINE bool operator<(const TrackedAllocation& rhs) { return memcmp(stack_trace, rhs.stack_trace, sizeof(stack_trace)) < 0; }
FORCEINLINE bool operator==(const TrackedAllocation& rhs) { return memcmp(stack_trace, rhs.stack_trace, sizeof(stack_trace)) == 0; }
void print(HANDLE h) const
{
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {};
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
IMAGEHLP_LINE64 line{};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for (size_t j = 0; j < MAX_FRAMES; ++j) {
const DWORD64 address = reinterpret_cast<DWORD64>(stack_trace[j]);
DWORD64 t1 = 0;
DWORD t2 = 0;
if (SymFromAddr(h, address, &t1, pSymbol) && SymGetLineFromAddr64(h, address, &t2, &line)) {
const char* s = line.FileName;
const char* file_name = nullptr;
while (*s) {
if ((*s == '\\') || (*s == '/')) {
file_name = s + 1;
}
++s;
}
printf("%-25s %s (line %lu)\n", file_name ? file_name : line.FileName, pSymbol->Name, line.LineNumber);
}
}
printf("\n");
}
}; };
static_assert(sizeof(TrackedAllocation) == 256, ""); static_assert(sizeof(TrackedAllocation) == 256, "");
@ -51,15 +84,62 @@ uint32_t first[N];
uint32_t next[N]; uint32_t next[N];
TrackedAllocation allocations[N]; TrackedAllocation allocations[N];
uint32_t num_allocations = 0; uint32_t num_allocations = 0;
uint64_t total_allocated = 0;
uint32_t cur_allocation_index = 1; uint32_t cur_allocation_index = 1;
void show_top_10()
{
TrackedAllocation* buf = reinterpret_cast<TrackedAllocation*>(VirtualAlloc(nullptr, sizeof(TrackedAllocation) * N, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
if (!buf) {
return;
}
const HANDLE h = GetCurrentProcess();
{
p2pool::MutexLock lock(allocation_lock);
TrackedAllocation* end = buf;
for (size_t i = 0; i < N; ++i) {
if (allocations[i].allocated_size) {
*(end++) = allocations[i];
}
}
std::sort(buf, end);
TrackedAllocation* prev = buf;
for (TrackedAllocation* p = buf + 1; p < end; ++p) {
if (*p == *prev) {
prev->allocated_size += p->allocated_size;
}
else {
++prev;
*prev = *p;
}
}
end = prev + 1;
std::sort(buf, end, [](const auto& a, const auto& b) { return a.allocated_size > b.allocated_size; });
printf("%I64u total bytes allocated\n", total_allocated);
for (TrackedAllocation* p = buf; (p < buf + 10) && (p < end); ++p) {
printf("%I64u bytes allocated at:\n", p->allocated_size);
p->print(h);
}
}
VirtualFree(buf, 0, MEM_RELEASE);
}
FORCEINLINE static void add_alocation(void* p, size_t size) FORCEINLINE static void add_alocation(void* p, size_t size)
{ {
if (!track_memory) { if (!track_memory) {
return; return;
} }
void* stack_trace[MAX_FRAMES]; void* stack_trace[MAX_FRAMES] = {};
DWORD hash; DWORD hash;
CaptureStackBackTrace(1, MAX_FRAMES, stack_trace, &hash); CaptureStackBackTrace(1, MAX_FRAMES, stack_trace, &hash);
@ -74,6 +154,7 @@ FORCEINLINE static void add_alocation(void* p, size_t size)
// Make N two times bigger if this triggers // Make N two times bigger if this triggers
__debugbreak(); __debugbreak();
} }
total_allocated += size;
for (uint64_t i = cur_allocation_index;; i = (i + 1) & (N - 1)) { for (uint64_t i = cur_allocation_index;; i = (i + 1) & (N - 1)) {
if (i && !allocations[i].allocated_size) { if (i && !allocations[i].allocated_size) {
@ -105,6 +186,7 @@ FORCEINLINE static void remove_allocation(void* p)
for (uint32_t prev = 0, k = first[index]; k != 0; prev = k, k = next[k]) { for (uint32_t prev = 0, k = first[index]; k != 0; prev = k, k = next[k]) {
if (allocations[k].p == p) { if (allocations[k].p == p) {
total_allocated -= allocations[k].allocated_size;
allocations[k].allocated_size = 0; allocations[k].allocated_size = 0;
if (prev) { if (prev) {
next[prev] = next[k]; next[prev] = next[k];
@ -181,6 +263,8 @@ void* calloc_hook(size_t count, size_t size) noexcept
void memory_tracking_start() void memory_tracking_start()
{ {
SymInitialize(GetCurrentProcess(), NULL, TRUE);
using namespace p2pool; using namespace p2pool;
uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook); uv_replace_allocator(malloc_hook, realloc_hook, calloc_hook, free_hook);
@ -196,7 +280,6 @@ void memory_tracking_stop()
uv_mutex_destroy(&allocation_lock); uv_mutex_destroy(&allocation_lock);
const HANDLE h = GetCurrentProcess(); const HANDLE h = GetCurrentProcess();
SymInitialize(h, NULL, TRUE);
uint64_t total_leaks = 0; uint64_t total_leaks = 0;
@ -205,39 +288,16 @@ void memory_tracking_stop()
if (t.allocated_size) { if (t.allocated_size) {
total_leaks += t.allocated_size; total_leaks += t.allocated_size;
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {}; printf("Memory leak detected, %I64u bytes allocated at %p by thread %u:\n", t.allocated_size, t.p, t.thread_id);
PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer); t.print(h);
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
IMAGEHLP_LINE64 line{};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
printf("Memory leak detected, %u bytes allocated at %p by thread %u:\n", t.allocated_size, t.p, t.thread_id);
for (size_t j = 0; j < MAX_FRAMES; ++j) {
const DWORD64 address = reinterpret_cast<DWORD64>(t.stack_trace[j]);
DWORD64 t1 = 0;
DWORD t2 = 0;
if (SymFromAddr(h, address, &t1, pSymbol) && SymGetLineFromAddr64(h, address, &t2, &line)) {
const char* s = line.FileName;
const char* file_name = nullptr;
while (*s) {
if ((*s == '\\') || (*s == '/')) {
file_name = s + 1;
}
++s;
}
printf("%-25s %s (line %lu)\n", file_name ? file_name : line.FileName, pSymbol->Name, line.LineNumber);
}
}
printf("\n");
} }
} }
if (total_leaks > 0) { if (total_leaks > 0) {
printf("%I64u bytes leaked\n\n", total_leaks); printf("%I64u bytes leaked\n\n", total_leaks);
} }
SymCleanup(h);
} }
NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); } NOINLINE void* operator new(size_t n) { return p2pool::allocate(n); }

View file

@ -747,12 +747,16 @@ void P2PServer::broadcast(const PoolBlock& block)
Broadcast* data = new Broadcast(); Broadcast* data = new Broadcast();
data->blob.reserve(block.m_mainChainData.size() + block.m_sideChainData.size()); int outputs_offset, outputs_blob_size;
data->blob = block.m_mainChainData; const std::vector<uint8_t> mainchain_data = block.serialize_mainchain_data(nullptr, nullptr, &outputs_offset, &outputs_blob_size);
data->blob.insert(data->blob.end(), block.m_sideChainData.begin(), block.m_sideChainData.end()); const std::vector<uint8_t> sidechain_data = block.serialize_sidechain_data();
data->pruned_blob.reserve(block.m_mainChainData.size() + block.m_sideChainData.size() + 16 - block.m_mainChainOutputsBlobSize); data->blob.reserve(mainchain_data.size() + sidechain_data.size());
data->pruned_blob.assign(block.m_mainChainData.begin(), block.m_mainChainData.begin() + block.m_mainChainOutputsOffset); data->blob = mainchain_data;
data->blob.insert(data->blob.end(), sidechain_data.begin(), sidechain_data.end());
data->pruned_blob.reserve(mainchain_data.size() + sidechain_data.size() + 16 - outputs_blob_size);
data->pruned_blob.assign(mainchain_data.begin(), mainchain_data.begin() + outputs_offset);
// 0 outputs in the pruned blob // 0 outputs in the pruned blob
data->pruned_blob.push_back(0); data->pruned_blob.push_back(0);
@ -764,10 +768,10 @@ void P2PServer::broadcast(const PoolBlock& block)
}); });
writeVarint(total_reward, data->pruned_blob); writeVarint(total_reward, data->pruned_blob);
writeVarint(block.m_mainChainOutputsBlobSize, data->pruned_blob); writeVarint(outputs_blob_size, data->pruned_blob);
data->pruned_blob.insert(data->pruned_blob.end(), block.m_mainChainData.begin() + block.m_mainChainOutputsOffset + block.m_mainChainOutputsBlobSize, block.m_mainChainData.end()); data->pruned_blob.insert(data->pruned_blob.end(), mainchain_data.begin() + outputs_offset + outputs_blob_size, mainchain_data.end());
data->pruned_blob.insert(data->pruned_blob.end(), block.m_sideChainData.begin(), block.m_sideChainData.end()); data->pruned_blob.insert(data->pruned_blob.end(), sidechain_data.begin(), sidechain_data.end());
data->ancestor_hashes.reserve(block.m_uncles.size() + 1); data->ancestor_hashes.reserve(block.m_uncles.size() + 1);
data->ancestor_hashes = block.m_uncles; data->ancestor_hashes = block.m_uncles;

View file

@ -301,6 +301,11 @@ void p2pool::handle_miner_data(MinerData& data)
"\n---------------------------------------------------------------------------------------------------------------" "\n---------------------------------------------------------------------------------------------------------------"
); );
// Tx secret keys from all miners change every block, so cache can be cleared here
if (m_sideChain->precalcFinished()) {
clear_crypto_cache();
}
if (!is_main_thread()) { if (!is_main_thread()) {
update_block_template_async(); update_block_template_async();
} }
@ -440,7 +445,7 @@ void p2pool::submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t e
} }
} }
void p2pool::submit_block_async(const std::vector<uint8_t>& blob) void p2pool::submit_block_async(std::vector<uint8_t>&& blob)
{ {
{ {
MutexLock lock(m_submitBlockDataLock); MutexLock lock(m_submitBlockDataLock);
@ -448,7 +453,7 @@ void p2pool::submit_block_async(const std::vector<uint8_t>& blob)
m_submitBlockData.template_id = 0; m_submitBlockData.template_id = 0;
m_submitBlockData.nonce = 0; m_submitBlockData.nonce = 0;
m_submitBlockData.extra_nonce = 0; m_submitBlockData.extra_nonce = 0;
m_submitBlockData.blob = blob; m_submitBlockData.blob = std::move(blob);
} }
// If p2pool is stopped, m_submitBlockAsync is most likely already closed // If p2pool is stopped, m_submitBlockAsync is most likely already closed
@ -1322,8 +1327,6 @@ void p2pool::cleanup_mainchain_data(uint64_t height)
void p2pool::api_update_block_found(const ChainMain* data) void p2pool::api_update_block_found(const ChainMain* data)
{ {
clear_crypto_cache();
if (!m_api) { if (!m_api) {
return; return;
} }

View file

@ -74,7 +74,7 @@ public:
virtual void handle_chain_main(ChainMain& data, const char* extra) override; virtual void handle_chain_main(ChainMain& data, const char* extra) override;
void submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce); void submit_block_async(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void submit_block_async(const std::vector<uint8_t>& blob); void submit_block_async(std::vector<uint8_t>&& blob);
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce); void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
void update_block_template_async(bool is_alternative_block = false); void update_block_template_async(bool is_alternative_block = false);

View file

@ -29,11 +29,7 @@ static constexpr char log_category_prefix[] = "PoolBlock ";
namespace p2pool { namespace p2pool {
PoolBlock::PoolBlock() PoolBlock::PoolBlock()
: m_mainChainHeaderSize(0) : m_majorVersion(0)
, m_mainChainMinerTxSize(0)
, m_mainChainOutputsOffset(0)
, m_mainChainOutputsBlobSize(0)
, m_majorVersion(0)
, m_minorVersion(0) , m_minorVersion(0)
, m_timestamp(0) , m_timestamp(0)
, m_prevId{} , m_prevId{}
@ -57,12 +53,6 @@ PoolBlock::PoolBlock()
, m_localTimestamp(seconds_since_epoch()) , m_localTimestamp(seconds_since_epoch())
{ {
uv_mutex_init_checked(&m_lock); uv_mutex_init_checked(&m_lock);
m_mainChainData.reserve(48 * 1024);
m_outputs.reserve(2048);
m_transactions.reserve(256);
m_sideChainData.reserve(512);
m_uncles.reserve(8);
} }
PoolBlock::PoolBlock(const PoolBlock& b) PoolBlock::PoolBlock(const PoolBlock& b)
@ -83,11 +73,11 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
LOGERR(1, "operator= uv_mutex_trylock failed. Fix the code!"); LOGERR(1, "operator= uv_mutex_trylock failed. Fix the code!");
} }
m_mainChainData = b.m_mainChainData; #if POOL_BLOCK_DEBUG
m_mainChainHeaderSize = b.m_mainChainHeaderSize; m_mainChainDataDebug = b.m_mainChainDataDebug;
m_mainChainMinerTxSize = b.m_mainChainMinerTxSize; m_sideChainDataDebug = b.m_sideChainDataDebug;
m_mainChainOutputsOffset = b.m_mainChainOutputsOffset; #endif
m_mainChainOutputsBlobSize = b.m_mainChainOutputsBlobSize;
m_majorVersion = b.m_majorVersion; m_majorVersion = b.m_majorVersion;
m_minorVersion = b.m_minorVersion; m_minorVersion = b.m_minorVersion;
m_timestamp = b.m_timestamp; m_timestamp = b.m_timestamp;
@ -99,7 +89,6 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b)
m_extraNonceSize = b.m_extraNonceSize; m_extraNonceSize = b.m_extraNonceSize;
m_extraNonce = b.m_extraNonce; m_extraNonce = b.m_extraNonce;
m_transactions = b.m_transactions; m_transactions = b.m_transactions;
m_sideChainData = b.m_sideChainData;
m_minerWallet = b.m_minerWallet; m_minerWallet = b.m_minerWallet;
m_txkeySec = b.m_txkeySec; m_txkeySec = b.m_txkeySec;
m_parent = b.m_parent; m_parent = b.m_parent;
@ -129,43 +118,58 @@ PoolBlock::~PoolBlock()
uv_mutex_destroy(&m_lock); uv_mutex_destroy(&m_lock);
} }
void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash) std::vector<uint8_t> PoolBlock::serialize_mainchain_data(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
{ {
MutexLock lock(m_lock); MutexLock lock(m_lock);
return serialize_mainchain_data_nolock(header_size, miner_tx_size, outputs_offset, outputs_blob_size);
}
m_mainChainData.clear(); std::vector<uint8_t> PoolBlock::serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const
{
std::vector<uint8_t> data;
data.reserve(128 + m_outputs.size() * 39 + m_transactions.size() * HASH_SIZE);
// Header // Header
m_mainChainData.push_back(m_majorVersion); data.push_back(m_majorVersion);
m_mainChainData.push_back(m_minorVersion); data.push_back(m_minorVersion);
writeVarint(m_timestamp, m_mainChainData); writeVarint(m_timestamp, data);
m_mainChainData.insert(m_mainChainData.end(), m_prevId.h, m_prevId.h + HASH_SIZE); data.insert(data.end(), m_prevId.h, m_prevId.h + HASH_SIZE);
m_mainChainData.insert(m_mainChainData.end(), reinterpret_cast<uint8_t*>(&nonce), reinterpret_cast<uint8_t*>(&nonce) + NONCE_SIZE); data.insert(data.end(), reinterpret_cast<const uint8_t*>(&m_nonce), reinterpret_cast<const uint8_t*>(&m_nonce) + NONCE_SIZE);
m_mainChainHeaderSize = m_mainChainData.size(); const size_t header_size0 = data.size();
if (header_size) {
*header_size = header_size0;
}
// Miner tx // Miner tx
m_mainChainData.push_back(TX_VERSION); data.push_back(TX_VERSION);
writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, m_mainChainData); writeVarint(m_txinGenHeight + MINER_REWARD_UNLOCK_TIME, data);
m_mainChainData.push_back(1); data.push_back(1);
m_mainChainData.push_back(TXIN_GEN); data.push_back(TXIN_GEN);
writeVarint(m_txinGenHeight, m_mainChainData); writeVarint(m_txinGenHeight, data);
m_mainChainOutputsOffset = static_cast<int>(m_mainChainData.size()); const int outputs_offset0 = static_cast<int>(data.size());
if (outputs_offset) {
*outputs_offset = outputs_offset0;
}
writeVarint(m_outputs.size(), m_mainChainData); writeVarint(m_outputs.size(), data);
for (TxOutput& output : m_outputs) { const uint8_t tx_type = get_tx_type();
writeVarint(output.m_reward, m_mainChainData);
m_mainChainData.push_back(output.m_txType);
m_mainChainData.insert(m_mainChainData.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (output.m_txType == TXOUT_TO_TAGGED_KEY) { for (const TxOutput& output : m_outputs) {
m_mainChainData.push_back(output.m_viewTag); writeVarint(output.m_reward, data);
data.push_back(tx_type);
data.insert(data.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (tx_type == TXOUT_TO_TAGGED_KEY) {
data.push_back(static_cast<uint8_t>(output.m_viewTag));
} }
} }
m_mainChainOutputsBlobSize = static_cast<int>(m_mainChainData.size()) - m_mainChainOutputsOffset; if (outputs_blob_size) {
*outputs_blob_size = static_cast<int>(data.size()) - outputs_offset0;
}
uint8_t tx_extra[128]; uint8_t tx_extra[128];
uint8_t* p = tx_extra; uint8_t* p = tx_extra;
@ -183,7 +187,6 @@ void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, c
*(p++) = TX_EXTRA_NONCE; *(p++) = TX_EXTRA_NONCE;
*(p++) = static_cast<uint8_t>(extra_nonce_size); *(p++) = static_cast<uint8_t>(extra_nonce_size);
m_extraNonce = extra_nonce;
memcpy(p, &m_extraNonce, EXTRA_NONCE_SIZE); memcpy(p, &m_extraNonce, EXTRA_NONCE_SIZE);
p += EXTRA_NONCE_SIZE; p += EXTRA_NONCE_SIZE;
if (extra_nonce_size > EXTRA_NONCE_SIZE) { if (extra_nonce_size > EXTRA_NONCE_SIZE) {
@ -193,49 +196,70 @@ void PoolBlock::serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, c
*(p++) = TX_EXTRA_MERGE_MINING_TAG; *(p++) = TX_EXTRA_MERGE_MINING_TAG;
*(p++) = HASH_SIZE; *(p++) = HASH_SIZE;
memcpy(p, sidechain_hash.h, HASH_SIZE); memcpy(p, m_sidechainId.h, HASH_SIZE);
p += HASH_SIZE; p += HASH_SIZE;
writeVarint(static_cast<size_t>(p - tx_extra), m_mainChainData); writeVarint(static_cast<size_t>(p - tx_extra), data);
m_mainChainData.insert(m_mainChainData.end(), tx_extra, p); data.insert(data.end(), tx_extra, p);
m_mainChainData.push_back(0); data.push_back(0);
m_mainChainMinerTxSize = m_mainChainData.size() - m_mainChainHeaderSize; if (miner_tx_size) {
*miner_tx_size = data.size() - header_size0;
}
writeVarint(m_transactions.size() - 1, m_mainChainData); writeVarint(m_transactions.size() - 1, data);
const uint8_t* data = reinterpret_cast<const uint8_t*>(m_transactions.data()); const uint8_t* t = reinterpret_cast<const uint8_t*>(m_transactions.data());
m_mainChainData.insert(m_mainChainData.end(), data + HASH_SIZE, data + m_transactions.size() * HASH_SIZE); data.insert(data.end(), t + HASH_SIZE, t + m_transactions.size() * HASH_SIZE);
#if POOL_BLOCK_DEBUG
if (!m_mainChainDataDebug.empty() && (data != m_mainChainDataDebug)) {
LOGERR(1, "serialize_mainchain_data() has a bug, fix it!");
panic();
}
#endif
return data;
} }
void PoolBlock::serialize_sidechain_data() std::vector<uint8_t> PoolBlock::serialize_sidechain_data() const
{ {
std::vector<uint8_t> data;
MutexLock lock(m_lock); MutexLock lock(m_lock);
m_sideChainData.clear(); data.reserve((m_uncles.size() + 4) * HASH_SIZE + 20);
m_sideChainData.reserve((m_uncles.size() + 4) * HASH_SIZE + 11);
const hash& spend = m_minerWallet.spend_public_key(); const hash& spend = m_minerWallet.spend_public_key();
const hash& view = m_minerWallet.view_public_key(); const hash& view = m_minerWallet.view_public_key();
m_sideChainData.insert(m_sideChainData.end(), spend.h, spend.h + HASH_SIZE); data.insert(data.end(), spend.h, spend.h + HASH_SIZE);
m_sideChainData.insert(m_sideChainData.end(), view.h, view.h + HASH_SIZE); data.insert(data.end(), view.h, view.h + HASH_SIZE);
m_sideChainData.insert(m_sideChainData.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE); data.insert(data.end(), m_txkeySec.h, m_txkeySec.h + HASH_SIZE);
m_sideChainData.insert(m_sideChainData.end(), m_parent.h, m_parent.h + HASH_SIZE); data.insert(data.end(), m_parent.h, m_parent.h + HASH_SIZE);
writeVarint(m_uncles.size(), m_sideChainData); writeVarint(m_uncles.size(), data);
for (const hash& id : m_uncles) { for (const hash& id : m_uncles) {
m_sideChainData.insert(m_sideChainData.end(), id.h, id.h + HASH_SIZE); data.insert(data.end(), id.h, id.h + HASH_SIZE);
} }
writeVarint(m_sidechainHeight, m_sideChainData); writeVarint(m_sidechainHeight, data);
writeVarint(m_difficulty.lo, m_sideChainData); writeVarint(m_difficulty.lo, data);
writeVarint(m_difficulty.hi, m_sideChainData); writeVarint(m_difficulty.hi, data);
writeVarint(m_cumulativeDifficulty.lo, m_sideChainData); writeVarint(m_cumulativeDifficulty.lo, data);
writeVarint(m_cumulativeDifficulty.hi, m_sideChainData); writeVarint(m_cumulativeDifficulty.hi, data);
#if POOL_BLOCK_DEBUG
if (!m_sideChainDataDebug.empty() && (data != m_sideChainDataDebug)) {
LOGERR(1, "serialize_sidechain_data() has a bug, fix it!");
panic();
}
#endif
return data;
} }
void PoolBlock::reset_offchain_data() void PoolBlock::reset_offchain_data()
@ -274,16 +298,19 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
{ {
MutexLock lock(m_lock); MutexLock lock(m_lock);
if (!m_mainChainHeaderSize || !m_mainChainMinerTxSize || (m_mainChainData.size() < m_mainChainHeaderSize + m_mainChainMinerTxSize)) { size_t header_size, miner_tx_size;
const std::vector<uint8_t> mainchain_data = serialize_mainchain_data_nolock(&header_size, &miner_tx_size, nullptr, nullptr);
if (!header_size || !miner_tx_size || (mainchain_data.size() < header_size + miner_tx_size)) {
LOGERR(1, "tried to calculate PoW of uninitialized block"); LOGERR(1, "tried to calculate PoW of uninitialized block");
return false; return false;
} }
blob_size = m_mainChainHeaderSize; blob_size = header_size;
memcpy(blob, m_mainChainData.data(), blob_size); memcpy(blob, mainchain_data.data(), blob_size);
uint8_t* miner_tx = m_mainChainData.data() + m_mainChainHeaderSize; const uint8_t* miner_tx = mainchain_data.data() + header_size;
keccak(miner_tx, static_cast<int>(m_mainChainMinerTxSize) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE); keccak(miner_tx, static_cast<int>(miner_tx_size) - 1, reinterpret_cast<uint8_t*>(hashes), HASH_SIZE);
count = m_transactions.size(); count = m_transactions.size();
uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data()); uint8_t* h = reinterpret_cast<uint8_t*>(m_transactions.data());
@ -329,11 +356,13 @@ bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const
uint64_t PoolBlock::get_payout(const Wallet& w) const uint64_t PoolBlock::get_payout(const Wallet& w) const
{ {
const uint8_t tx_type = get_tx_type();
for (size_t i = 0, n = m_outputs.size(); i < n; ++i) { for (size_t i = 0, n = m_outputs.size(); i < n; ++i) {
const TxOutput& out = m_outputs[i]; const TxOutput& out = m_outputs[i];
hash eph_public_key; hash eph_public_key;
if (out.m_txType == TXOUT_TO_TAGGED_KEY) { if (tx_type == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(m_txkeySec, i, eph_public_key, out.m_viewTag) && (eph_public_key == out.m_ephPublicKey)) { if (w.get_eph_public_key_with_view_tag(m_txkeySec, i, eph_public_key, out.m_viewTag) && (eph_public_key == out.m_ephPublicKey)) {
return out.m_reward; return out.m_reward;
} }

View file

@ -63,13 +63,12 @@ struct PoolBlock
mutable uv_mutex_t m_lock; mutable uv_mutex_t m_lock;
// Monero block template #if POOL_BLOCK_DEBUG
std::vector<uint8_t> m_mainChainData; std::vector<uint8_t> m_mainChainDataDebug;
size_t m_mainChainHeaderSize; std::vector<uint8_t> m_sideChainDataDebug;
size_t m_mainChainMinerTxSize; #endif
int m_mainChainOutputsOffset;
int m_mainChainOutputsBlobSize;
// Monero block template
uint8_t m_majorVersion; uint8_t m_majorVersion;
uint8_t m_minorVersion; uint8_t m_minorVersion;
uint64_t m_timestamp; uint64_t m_timestamp;
@ -81,15 +80,16 @@ struct PoolBlock
struct TxOutput struct TxOutput
{ {
FORCEINLINE TxOutput() : m_reward(0), m_ephPublicKey(), m_txType(0), m_viewTag(0) {} FORCEINLINE TxOutput() : m_ephPublicKey(), m_reward(0), m_viewTag(0) {}
FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t tx_type, uint8_t view_tag) : m_reward(r), m_ephPublicKey(k), m_txType(tx_type), m_viewTag(view_tag) {} FORCEINLINE TxOutput(uint64_t r, const hash& k, uint8_t view_tag) : m_ephPublicKey(k), m_reward(r), m_viewTag(view_tag) {}
uint64_t m_reward;
hash m_ephPublicKey; hash m_ephPublicKey;
uint8_t m_txType; uint64_t m_reward : 56;
uint8_t m_viewTag; uint64_t m_viewTag : 8;
}; };
static_assert(sizeof(TxOutput) == sizeof(hash) + sizeof(uint64_t), "TxOutput bit packing didn't work with this compiler, fix the code!");
std::vector<TxOutput> m_outputs; std::vector<TxOutput> m_outputs;
hash m_txkeyPub; hash m_txkeyPub;
@ -99,9 +99,6 @@ struct PoolBlock
// All block transaction hashes including the miner transaction hash at index 0 // All block transaction hashes including the miner transaction hash at index 0
std::vector<hash> m_transactions; std::vector<hash> m_transactions;
// Side-chain data
std::vector<uint8_t> m_sideChainData;
// Miner's wallet // Miner's wallet
Wallet m_minerWallet{ nullptr }; Wallet m_minerWallet{ nullptr };
@ -134,8 +131,9 @@ struct PoolBlock
uint64_t m_localTimestamp; uint64_t m_localTimestamp;
void serialize_mainchain_data(uint32_t nonce, uint32_t extra_nonce, const hash& sidechain_hash); std::vector<uint8_t> serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr) const;
void serialize_sidechain_data(); std::vector<uint8_t> serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const;
std::vector<uint8_t> serialize_sidechain_data() const;
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop); int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop);
void reset_offchain_data(); void reset_offchain_data();

View file

@ -75,8 +75,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
const int nonce_offset = static_cast<int>(data - data_begin); const int nonce_offset = static_cast<int>(data - data_begin);
READ_BUF(&m_nonce, NONCE_SIZE); READ_BUF(&m_nonce, NONCE_SIZE);
m_mainChainHeaderSize = data - data_begin;
EXPECT_BYTE(TX_VERSION); EXPECT_BYTE(TX_VERSION);
uint64_t unlock_height; uint64_t unlock_height;
@ -89,12 +87,13 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
if (unlock_height != m_txinGenHeight + MINER_REWARD_UNLOCK_TIME) return __LINE__; if (unlock_height != m_txinGenHeight + MINER_REWARD_UNLOCK_TIME) return __LINE__;
std::vector<uint8_t> outputs_blob; std::vector<uint8_t> outputs_blob;
m_mainChainOutputsOffset = static_cast<int>(data - data_begin); const int outputs_offset = static_cast<int>(data - data_begin);
uint64_t num_outputs; uint64_t num_outputs;
READ_VARINT(num_outputs); READ_VARINT(num_outputs);
uint64_t total_reward = 0; uint64_t total_reward = 0;
int outputs_blob_size;
if (num_outputs > 0) { if (num_outputs > 0) {
// Outputs are in the buffer, just read them // Outputs are in the buffer, just read them
@ -105,31 +104,32 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
if (num_outputs > std::numeric_limits<uint64_t>::max() / MIN_OUTPUT_SIZE) return __LINE__; if (num_outputs > std::numeric_limits<uint64_t>::max() / MIN_OUTPUT_SIZE) return __LINE__;
if (static_cast<uint64_t>(data_end - data) < num_outputs * MIN_OUTPUT_SIZE) return __LINE__; if (static_cast<uint64_t>(data_end - data) < num_outputs * MIN_OUTPUT_SIZE) return __LINE__;
m_outputs.clear(); m_outputs.resize(num_outputs);
m_outputs.reserve(num_outputs); m_outputs.shrink_to_fit();
const uint8_t expected_tx_type = get_tx_type(); const uint8_t expected_tx_type = get_tx_type();
for (uint64_t i = 0; i < num_outputs; ++i) { for (uint64_t i = 0; i < num_outputs; ++i) {
TxOutput t; TxOutput& t = m_outputs[i];
READ_VARINT(t.m_reward); uint64_t reward;
total_reward += t.m_reward; READ_VARINT(reward);
t.m_reward = reward;
total_reward += reward;
EXPECT_BYTE(expected_tx_type); EXPECT_BYTE(expected_tx_type);
t.m_txType = expected_tx_type;
READ_BUF(t.m_ephPublicKey.h, HASH_SIZE); READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);
if (expected_tx_type == TXOUT_TO_TAGGED_KEY) { if (expected_tx_type == TXOUT_TO_TAGGED_KEY) {
READ_BYTE(t.m_viewTag); uint8_t view_tag;
READ_BYTE(view_tag);
t.m_viewTag = view_tag;
} }
m_outputs.emplace_back(std::move(t));
} }
m_mainChainOutputsBlobSize = static_cast<int>(data - data_begin) - m_mainChainOutputsOffset; outputs_blob_size = static_cast<int>(data - data_begin) - outputs_offset;
outputs_blob.assign(data_begin + m_mainChainOutputsOffset, data); outputs_blob.assign(data_begin + outputs_offset, data);
} }
else { else {
// Outputs are not in the buffer and must be calculated from sidechain data // Outputs are not in the buffer and must be calculated from sidechain data
@ -144,7 +144,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__; return __LINE__;
} }
m_mainChainOutputsBlobSize = static_cast<int>(tmp); outputs_blob_size = static_cast<int>(tmp);
} }
// Technically some p2pool node could keep stuffing block with transactions until reward is less than 0.6 XMR // Technically some p2pool node could keep stuffing block with transactions until reward is less than 0.6 XMR
@ -153,13 +153,12 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__; return __LINE__;
} }
const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - m_mainChainOutputsOffset; const int outputs_actual_blob_size = static_cast<int>(data - data_begin) - outputs_offset;
if (outputs_blob_size < outputs_actual_blob_size) {
if (m_mainChainOutputsBlobSize < outputs_actual_blob_size) {
return __LINE__; return __LINE__;
} }
const int outputs_blob_size_diff = m_mainChainOutputsBlobSize - outputs_actual_blob_size; const int outputs_blob_size_diff = outputs_blob_size - outputs_actual_blob_size;
uint64_t tx_extra_size; uint64_t tx_extra_size;
READ_VARINT(tx_extra_size); READ_VARINT(tx_extra_size);
@ -191,8 +190,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
EXPECT_BYTE(0); EXPECT_BYTE(0);
m_mainChainMinerTxSize = (data - data_begin) + outputs_blob_size_diff - m_mainChainHeaderSize;
uint64_t num_transactions; uint64_t num_transactions;
READ_VARINT(num_transactions); READ_VARINT(num_transactions);
@ -208,12 +205,14 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
m_transactions.emplace_back(std::move(id)); m_transactions.emplace_back(std::move(id));
} }
m_mainChainData.reserve((data - data_begin) + outputs_blob_size_diff); #if POOL_BLOCK_DEBUG
m_mainChainData.assign(data_begin, data_begin + m_mainChainOutputsOffset); m_mainChainDataDebug.reserve((data - data_begin) + outputs_blob_size_diff);
m_mainChainData.insert(m_mainChainData.end(), m_mainChainOutputsBlobSize, 0); m_mainChainDataDebug.assign(data_begin, data_begin + outputs_offset);
m_mainChainData.insert(m_mainChainData.end(), data_begin + m_mainChainOutputsOffset + outputs_actual_blob_size, data); m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), outputs_blob_size, 0);
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), data_begin + outputs_offset + outputs_actual_blob_size, data);
const uint8_t* sidechain_data_begin = data; const uint8_t* sidechain_data_begin = data;
#endif
hash spend_pub_key; hash spend_pub_key;
hash view_pub_key; hash view_pub_key;
@ -276,16 +275,18 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__; return __LINE__;
} }
if (static_cast<int>(outputs_blob.size()) != m_mainChainOutputsBlobSize) { if (static_cast<int>(outputs_blob.size()) != outputs_blob_size) {
return __LINE__; return __LINE__;
} }
memcpy(m_mainChainData.data() + m_mainChainOutputsOffset, outputs_blob.data(), m_mainChainOutputsBlobSize); #if POOL_BLOCK_DEBUG
memcpy(m_mainChainDataDebug.data() + outputs_offset, outputs_blob.data(), outputs_blob_size);
#endif
hash check; hash check;
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id(); const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
keccak_custom( keccak_custom(
[this, nonce_offset, extra_nonce_offset, sidechain_hash_offset, data_begin, data_end, &consensus_id, &outputs_blob, outputs_blob_size_diff](int offset) -> uint8_t [nonce_offset, extra_nonce_offset, sidechain_hash_offset, data_begin, data_end, &consensus_id, &outputs_blob, outputs_blob_size_diff, outputs_offset, outputs_blob_size](int offset) -> uint8_t
{ {
uint32_t k = static_cast<uint32_t>(offset - nonce_offset); uint32_t k = static_cast<uint32_t>(offset - nonce_offset);
if (k < NONCE_SIZE) { if (k < NONCE_SIZE) {
@ -304,11 +305,11 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff); const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff);
if (offset < data_size) { if (offset < data_size) {
if (offset < m_mainChainOutputsOffset) { if (offset < outputs_offset) {
return data_begin[offset]; return data_begin[offset];
} }
else if (offset < m_mainChainOutputsOffset + m_mainChainOutputsBlobSize) { else if (offset < outputs_offset + outputs_blob_size) {
const int tmp = offset - m_mainChainOutputsOffset; const int tmp = offset - outputs_offset;
return outputs_blob[tmp]; return outputs_blob[tmp];
} }
else { else {
@ -325,7 +326,9 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__; return __LINE__;
} }
m_sideChainData.assign(sidechain_data_begin, data_end); #if POOL_BLOCK_DEBUG
m_sideChainDataDebug.assign(sidechain_data_begin, data_end);
#endif
} }
catch (std::exception& e) { catch (std::exception& e) {
const char* msg = e.what(); const char* msg = e.what();

View file

@ -32,6 +32,7 @@
#include "stratum_server.h" #include "stratum_server.h"
#include "params.h" #include "params.h"
#include "json_parsers.h" #include "json_parsers.h"
#include "crypto.h"
#include <rapidjson/document.h> #include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h> #include <rapidjson/istreamwrapper.h>
#include <fstream> #include <fstream>
@ -169,7 +170,10 @@ SideChain::SideChain(p2pool* pool, NetworkType type, const char* pool_name)
// Use between 1 and 8 threads // Use between 1 and 8 threads
if (numThreads < 1) numThreads = 1; if (numThreads < 1) numThreads = 1;
#ifndef _DEBUG
if (numThreads > 8) numThreads = 8; if (numThreads > 8) numThreads = 8;
#endif
LOGINFO(4, "running " << numThreads << " pre-calculation workers"); LOGINFO(4, "running " << numThreads << " pre-calculation workers");
@ -527,7 +531,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
MinerData miner_data = m_pool->miner_data(); MinerData miner_data = m_pool->miner_data();
if ((block.m_prevId == miner_data.prev_id) && miner_data.difficulty.check_pow(pow_hash)) { if ((block.m_prevId == miner_data.prev_id) && miner_data.difficulty.check_pow(pow_hash)) {
LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero network, submitting it"); LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero network, submitting it");
m_pool->submit_block_async(block.m_mainChainData); m_pool->submit_block_async(block.serialize_mainchain_data());
} }
else { else {
difficulty_type diff; difficulty_type diff;
@ -536,7 +540,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
} }
else if (diff.check_pow(pow_hash)) { else if (diff.check_pow(pow_hash)) {
LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero height " << block.m_txinGenHeight << ", submitting it"); LOGINFO(0, log::LightGreen() << "add_external_block: block " << block.m_sidechainId << " has enough PoW for Monero height " << block.m_txinGenHeight << ", submitting it");
m_pool->submit_block_async(block.m_mainChainData); m_pool->submit_block_async(block.serialize_mainchain_data());
} }
} }
@ -675,10 +679,10 @@ bool SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob) const
return false; return false;
} }
blob.reserve(block->m_mainChainData.size() + block->m_sideChainData.size()); blob = block->serialize_mainchain_data();
const std::vector<uint8_t> sidechain_data = block->serialize_sidechain_data();
blob.insert(blob.end(), sidechain_data.begin(), sidechain_data.end());
blob = block->m_mainChainData;
blob.insert(blob.end(), block->m_sideChainData.begin(), block->m_sideChainData.end());
return true; return true;
} }
@ -696,13 +700,15 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
blob.reserve(n * 39 + 64); blob.reserve(n * 39 + 64);
writeVarint(n, blob); writeVarint(n, blob);
const uint8_t tx_type = b->get_tx_type();
for (const PoolBlock::TxOutput& output : b->m_outputs) { for (const PoolBlock::TxOutput& output : b->m_outputs) {
writeVarint(output.m_reward, blob); writeVarint(output.m_reward, blob);
blob.emplace_back(output.m_txType); blob.emplace_back(tx_type);
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE); blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
if (output.m_txType == TXOUT_TO_TAGGED_KEY) { if (tx_type == TXOUT_TO_TAGGED_KEY) {
blob.emplace_back(output.m_viewTag); blob.emplace_back(static_cast<uint8_t>(output.m_viewTag));
} }
} }
@ -808,9 +814,11 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
blob.emplace_back(view_tag); blob.emplace_back(view_tag);
} }
block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, tx_type, view_tag); block->m_outputs.emplace_back(tmpRewards[i], eph_public_key, view_tag);
} }
block->m_outputs.shrink_to_fit();
if (loop) { if (loop) {
// this will cause all helper jobs to finish immediately // this will cause all helper jobs to finish immediately
counter = -1; counter = -1;
@ -911,12 +919,13 @@ void SideChain::print_status(bool obtain_sidechain_lock) const
} }
const Wallet& w = m_pool->params().m_wallet; const Wallet& w = m_pool->params().m_wallet;
const uint8_t tx_type = tip->get_tx_type();
hash eph_public_key; hash eph_public_key;
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) { for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = tip->m_outputs[i]; const PoolBlock::TxOutput& out = tip->m_outputs[i];
if (!your_reward) { if (!your_reward) {
if (out.m_txType == TXOUT_TO_TAGGED_KEY) { if (tx_type == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) { if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
your_reward = out.m_reward; your_reward = out.m_reward;
} }
@ -983,11 +992,12 @@ double SideChain::get_reward_share(const Wallet& w) const
const PoolBlock* tip = m_chainTip; const PoolBlock* tip = m_chainTip;
if (tip) { if (tip) {
const uint8_t tx_type = tip->get_tx_type();
hash eph_public_key; hash eph_public_key;
for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) { for (size_t i = 0, n = tip->m_outputs.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = tip->m_outputs[i]; const PoolBlock::TxOutput& out = tip->m_outputs[i];
if (!reward) { if (!reward) {
if (out.m_txType == TXOUT_TO_TAGGED_KEY) { if (tx_type == TXOUT_TO_TAGGED_KEY) {
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) { if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
reward = out.m_reward; reward = out.m_reward;
} }
@ -1538,6 +1548,8 @@ void SideChain::verify(PoolBlock* block)
return; return;
} }
const uint8_t tx_type = block->get_tx_type();
for (size_t i = 0, n = rewards.size(); i < n; ++i) { for (size_t i = 0, n = rewards.size(); i < n; ++i) {
const PoolBlock::TxOutput& out = block->m_outputs[i]; const PoolBlock::TxOutput& out = block->m_outputs[i];
@ -1561,7 +1573,7 @@ void SideChain::verify(PoolBlock* block)
return; return;
} }
if ((out.m_txType == TXOUT_TO_TAGGED_KEY) && (out.m_viewTag != view_tag)) { if ((tx_type == TXOUT_TO_TAGGED_KEY) && (out.m_viewTag != view_tag)) {
LOGWARN(3, "block at height = " << block->m_sidechainHeight << LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
", id = " << block->m_sidechainId << ", id = " << block->m_sidechainId <<
", mainchain height = " << block->m_txinGenHeight << ", mainchain height = " << block->m_txinGenHeight <<
@ -1622,6 +1634,8 @@ void SideChain::update_chain_tip(const PoolBlock* block)
if (s) { if (s) {
s->reset_share_counters(); s->reset_share_counters();
} }
// Also clear cache because it has data from all old blocks now
clear_crypto_cache();
LOGINFO(0, log::LightCyan() << "SYNCHRONIZED"); LOGINFO(0, log::LightCyan() << "SYNCHRONIZED");
} }
} }
@ -2125,6 +2139,9 @@ void SideChain::finish_precalc()
LOGERR(1, "exception in finish_precalc(): " << e.what()); LOGERR(1, "exception in finish_precalc(): " << e.what());
} }
// Also clear cache because it has data from all old blocks now
clear_crypto_cache();
#ifdef DEV_TEST_SYNC #ifdef DEV_TEST_SYNC
if (m_pool) { if (m_pool) {
LOGINFO(0, log::LightGreen() << "[DEV] Synchronization finished successfully, stopping P2Pool now"); LOGINFO(0, log::LightGreen() << "[DEV] Synchronization finished successfully, stopping P2Pool now");

View file

@ -51,11 +51,16 @@ TEST(pool_block, deserialize)
ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr), 0); ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr), 0);
ASSERT_EQ(b.m_mainChainData.size(), 5607); size_t header_size, miner_tx_size;
ASSERT_EQ(b.m_mainChainHeaderSize, 43); int outputs_offset, outputs_blob_size;
ASSERT_EQ(b.m_mainChainMinerTxSize, 506); const std::vector<uint8_t> mainchain_data = b.serialize_mainchain_data(&header_size, &miner_tx_size, &outputs_offset, &outputs_blob_size);
ASSERT_EQ(b.m_mainChainOutputsOffset, 54);
ASSERT_EQ(b.m_mainChainOutputsBlobSize, 420); ASSERT_EQ(mainchain_data.size(), 5607);
ASSERT_EQ(header_size, 43);
ASSERT_EQ(miner_tx_size, 506);
ASSERT_EQ(outputs_offset, 54);
ASSERT_EQ(outputs_blob_size, 420);
ASSERT_EQ(b.m_majorVersion, 14); ASSERT_EQ(b.m_majorVersion, 14);
ASSERT_EQ(b.m_minorVersion, 14); ASSERT_EQ(b.m_minorVersion, 14);
ASSERT_EQ(b.m_timestamp, 1630934403); ASSERT_EQ(b.m_timestamp, 1630934403);
@ -65,7 +70,6 @@ TEST(pool_block, deserialize)
ASSERT_EQ(b.m_extraNonceSize, 4); ASSERT_EQ(b.m_extraNonceSize, 4);
ASSERT_EQ(b.m_extraNonce, 28); ASSERT_EQ(b.m_extraNonce, 28);
ASSERT_EQ(b.m_transactions.size(), 159); ASSERT_EQ(b.m_transactions.size(), 159);
ASSERT_EQ(b.m_sideChainData.size(), 146);
ASSERT_EQ(b.m_uncles.size(), 0); ASSERT_EQ(b.m_uncles.size(), 0);
ASSERT_EQ(b.m_sidechainHeight, 53450); ASSERT_EQ(b.m_sidechainHeight, 53450);
ASSERT_EQ(b.m_difficulty.lo, 319296691); ASSERT_EQ(b.m_difficulty.lo, 319296691);