De-duplicate tx hashes and pub keys to save memory (off by default) (#382)

P2Pool-main: 8.2 MB saved
P2Pool-mini: 66 MB saved
P2Pool-nano: 25.2 MB saved

The feature is available only when building from source and is intended for use on low-memory systems (for example, a VPS server with < 1 GB RAM).

It only makes sense to use with `--no-cache --no-randomx` in the command line because cache and RandomX hasher take much more memory.
This commit is contained in:
SChernykh
2025-10-18 12:21:16 +02:00
committed by GitHub
parent 52bcbda381
commit e2f0ec7c69
25 changed files with 640 additions and 103 deletions

View File

@@ -105,11 +105,14 @@ 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 (static_cast<uint64_t>(data_end - data) < num_outputs * MIN_OUTPUT_SIZE) return __LINE__;
m_outputs.resize(num_outputs);
m_outputs.shrink_to_fit();
m_ephPublicKeys.resize(num_outputs);
m_outputAmounts.resize(num_outputs);
m_ephPublicKeys.shrink_to_fit();
m_outputAmounts.shrink_to_fit();
for (uint64_t i = 0; i < num_outputs; ++i) {
TxOutput& t = m_outputs[i];
TxOutput& t = m_outputAmounts[i];
uint64_t reward;
READ_VARINT(reward);
@@ -124,7 +127,9 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
EXPECT_BYTE(TXOUT_TO_TAGGED_KEY);
READ_BUF(t.m_ephPublicKey.h, HASH_SIZE);
hash ephPublicKey;
READ_BUF(ephPublicKey.h, HASH_SIZE);
m_ephPublicKeys[i] = ephPublicKey;
uint8_t view_tag;
READ_BYTE(view_tag);
@@ -221,17 +226,18 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
const int transactions_offset = static_cast<int>(data - data_begin);
std::vector<uint64_t> parent_indices;
std::vector<hash> transactions;
if (compact) {
if (static_cast<uint64_t>(data_end - data) < num_transactions) return __LINE__;
m_transactions.resize(1);
parent_indices.resize(1);
// limit reserved memory size because we can't check "num_transactions" properly here
const uint64_t k = std::min<uint64_t>(num_transactions + 1, 256);
m_transactions.reserve(k);
transactions.reserve(k);
parent_indices.reserve(k);
transactions.resize(1);
parent_indices.resize(1);
for (uint64_t i = 0; i < num_transactions; ++i) {
uint64_t parent_index;
READ_VARINT(parent_index);
@@ -241,7 +247,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
READ_BUF(id.h, HASH_SIZE);
}
m_transactions.emplace_back(id);
transactions.emplace_back(id);
parent_indices.emplace_back(parent_index);
}
}
@@ -249,13 +255,13 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
if (num_transactions > std::numeric_limits<uint64_t>::max() / HASH_SIZE) return __LINE__;
if (static_cast<uint64_t>(data_end - data) < num_transactions * HASH_SIZE) return __LINE__;
m_transactions.resize(1);
m_transactions.reserve(num_transactions + 1);
transactions.reserve(num_transactions + 1);
transactions.resize(1);
for (uint64_t i = 0; i < num_transactions; ++i) {
hash id;
READ_BUF(id.h, HASH_SIZE);
m_transactions.emplace_back(id);
transactions.emplace_back(id);
}
}
@@ -263,8 +269,6 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
const int transactions_blob_size = static_cast<int>(num_transactions) * HASH_SIZE;
const int transactions_blob_size_diff = transactions_blob_size - transactions_actual_blob_size;
m_transactions.shrink_to_fit();
#if POOL_BLOCK_DEBUG
m_mainChainDataDebug.reserve((data - data_begin) + outputs_blob_size_diff + transactions_blob_size_diff);
m_mainChainDataDebug.assign(data_begin, data_begin + outputs_offset);
@@ -298,22 +302,42 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
READ_BUF(m_parent.h, HASH_SIZE);
m_transactions.clear();
m_transactions.reserve(transactions.size());
if (compact) {
const PoolBlock* parent = sidechain.find_block(m_parent);
if (!parent) {
return __LINE__;
}
for (uint64_t i = 1, n = m_transactions.size(); i < n; ++i) {
const uint64_t n = transactions.size();
if (n > 0) {
m_transactions.emplace_back(transactions[0]);
}
for (uint64_t i = 1; i < n; ++i) {
const uint64_t parent_index = parent_indices[i];
if (parent_index) {
if (parent_index >= parent->m_transactions.size()) {
return __LINE__;
}
m_transactions[i] = parent->m_transactions[parent_index];
transactions[i] = parent->m_transactions[parent_index];
m_transactions.emplace_back(parent->m_transactions[parent_index]);
}
else {
m_transactions.emplace_back(transactions[i]);
}
}
}
else {
for (const hash& h : transactions) {
m_transactions.emplace_back(h);
}
}
m_transactions.shrink_to_fit();
uint64_t num_uncles;
READ_VARINT(num_uncles);
@@ -416,7 +440,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
return __LINE__;
}
const uint8_t* transactions_blob = reinterpret_cast<uint8_t*>(m_transactions.data() + 1);
const uint8_t* transactions_blob = reinterpret_cast<uint8_t*>(transactions.data() + 1);
#if POOL_BLOCK_DEBUG
memcpy(m_mainChainDataDebug.data() + outputs_offset, outputs_blob.data(), outputs_blob_size);