Fix: init Merkle tree before selecting txs

This commit is contained in:
SChernykh 2024-06-18 18:27:07 +02:00
parent 3ce57ec254
commit 245e6fd9e8
2 changed files with 69 additions and 64 deletions

View file

@ -324,61 +324,17 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const
parallel_run(uv_default_loop_checked(), Precalc(m_shares, m_poolBlockTemplate->m_txkeySec)); parallel_run(uv_default_loop_checked(), Precalc(m_shares, m_poolBlockTemplate->m_txkeySec));
} }
// Only choose transactions that were received 5 or more seconds ago, or high fee (>= 0.006 XMR) transactions if (m_poolBlockTemplate->merge_mining_enabled()) {
m_mempoolTxs.clear(); m_poolBlockTemplate->m_merkleTreeData = PoolBlock::encode_merkle_tree_data(static_cast<uint32_t>(data.aux_chains.size() + 1), data.aux_nonce);
m_poolBlockTemplate->m_merkleTreeDataSize = 0;
const uint64_t cur_time = seconds_since_epoch(); writeVarint(m_poolBlockTemplate->m_merkleTreeData, [this](uint8_t) { ++m_poolBlockTemplate->m_merkleTreeDataSize; });
size_t total_mempool_transactions = 0;
mempool.iterate([this, cur_time, &total_mempool_transactions](const hash&, const TxMempoolData& tx) {
++total_mempool_transactions;
if ((cur_time > tx.time_received + 5) || (tx.fee >= HIGH_FEE_VALUE)) {
m_mempoolTxs.emplace_back(tx);
} }
}); else {
m_poolBlockTemplate->m_merkleTreeData = 0;
// Safeguard for busy mempool moments m_poolBlockTemplate->m_merkleTreeDataSize = 0;
// If the block template gets too big, nodes won't be able to send and receive it because of p2p packet size limit
// Calculate how many transactions we can take
{
PoolBlock* b = m_poolBlockTemplate;
b->m_transactions.clear();
b->m_transactions.resize(1);
b->m_outputs.clear();
// Block template size without coinbase outputs and transactions (minus 2 bytes for output and tx count dummy varints)
size_t k = b->serialize_mainchain_data().size() + b->serialize_sidechain_data().size() - 2;
// Add output and tx count real varints
writeVarint(m_shares.size(), [&k](uint8_t) { ++k; });
writeVarint(m_mempoolTxs.size(), [&k](uint8_t) { ++k; });
// Add a rough upper bound estimation of outputs' size. All outputs have <= 5 bytes for each output's reward (< 0.034359738368 XMR per output)
k += m_shares.size() * (5 /* reward */ + 1 /* tx_type */ + HASH_SIZE /* stealth address */ + 1 /* viewtag */);
// >= 0.034359738368 XMR is required for a 6 byte varint, add 1 byte per each potential 6-byte varint
{
uint64_t r = BASE_BLOCK_REWARD;
for (const auto& tx : m_mempoolTxs) {
r += tx.fee;
}
k += r / 34359738368ULL;
} }
const size_t max_transactions = (MAX_BLOCK_SIZE > k) ? ((MAX_BLOCK_SIZE - k) / HASH_SIZE) : 0; select_mempool_transactions(mempool);
LOGINFO(6, max_transactions << " transactions can be taken with current block size limit");
if (max_transactions == 0) {
m_mempoolTxs.clear();
}
else if (m_mempoolTxs.size() > max_transactions) {
std::nth_element(m_mempoolTxs.begin(), m_mempoolTxs.begin() + max_transactions, m_mempoolTxs.end());
m_mempoolTxs.resize(max_transactions);
}
LOGINFO(4, "mempool has " << total_mempool_transactions << " transactions, taking " << m_mempoolTxs.size() << " transactions from it");
}
const uint64_t base_reward = get_base_reward(data.already_generated_coins); const uint64_t base_reward = get_base_reward(data.already_generated_coins);
@ -411,16 +367,6 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const
}; };
uint64_t max_reward_amounts_weight = get_reward_amounts_weight(); uint64_t max_reward_amounts_weight = get_reward_amounts_weight();
if (m_poolBlockTemplate->merge_mining_enabled()) {
m_poolBlockTemplate->m_merkleTreeData = PoolBlock::encode_merkle_tree_data(static_cast<uint32_t>(data.aux_chains.size() + 1), data.aux_nonce);
m_poolBlockTemplate->m_merkleTreeDataSize = 0;
writeVarint(m_poolBlockTemplate->m_merkleTreeData, [this](uint8_t) { ++m_poolBlockTemplate->m_merkleTreeDataSize; });
}
else {
m_poolBlockTemplate->m_merkleTreeData = 0;
m_poolBlockTemplate->m_merkleTreeDataSize = 0;
}
if (create_miner_tx(data, m_shares, max_reward_amounts_weight, true) < 0) { if (create_miner_tx(data, m_shares, max_reward_amounts_weight, true) < 0) {
use_old_template(); use_old_template();
return; return;
@ -865,6 +811,64 @@ void BlockTemplate::fill_optimal_knapsack(const MinerData& data, uint64_t base_r
} }
#endif #endif
void BlockTemplate::select_mempool_transactions(const Mempool& mempool)
{
// Only choose transactions that were received 5 or more seconds ago, or high fee (>= 0.006 XMR) transactions
m_mempoolTxs.clear();
const uint64_t cur_time = seconds_since_epoch();
size_t total_mempool_transactions = 0;
mempool.iterate([this, cur_time, &total_mempool_transactions](const hash&, const TxMempoolData& tx) {
++total_mempool_transactions;
if ((cur_time > tx.time_received + 5) || (tx.fee >= HIGH_FEE_VALUE)) {
m_mempoolTxs.emplace_back(tx);
}
});
// Safeguard for busy mempool moments
// If the block template gets too big, nodes won't be able to send and receive it because of p2p packet size limit
// Calculate how many transactions we can take
PoolBlock* b = m_poolBlockTemplate;
b->m_transactions.clear();
b->m_transactions.resize(1);
b->m_outputs.clear();
// Block template size without coinbase outputs and transactions (minus 2 bytes for output and tx count dummy varints)
size_t k = b->serialize_mainchain_data().size() + b->serialize_sidechain_data().size() - 2;
// Add output and tx count real varints
writeVarint(m_shares.size(), [&k](uint8_t) { ++k; });
writeVarint(m_mempoolTxs.size(), [&k](uint8_t) { ++k; });
// Add a rough upper bound estimation of outputs' size. All outputs have <= 5 bytes for each output's reward (< 0.034359738368 XMR per output)
k += m_shares.size() * (5 /* reward */ + 1 /* tx_type */ + HASH_SIZE /* stealth address */ + 1 /* viewtag */);
// >= 0.034359738368 XMR is required for a 6 byte varint, add 1 byte per each potential 6-byte varint
{
uint64_t r = BASE_BLOCK_REWARD;
for (const auto& tx : m_mempoolTxs) {
r += tx.fee;
}
k += r / 34359738368ULL;
}
const size_t max_transactions = (MAX_BLOCK_SIZE > k) ? ((MAX_BLOCK_SIZE - k) / HASH_SIZE) : 0;
LOGINFO(6, max_transactions << " transactions can be taken with current block size limit");
if (max_transactions == 0) {
m_mempoolTxs.clear();
}
else if (m_mempoolTxs.size() > max_transactions) {
std::nth_element(m_mempoolTxs.begin(), m_mempoolTxs.begin() + max_transactions, m_mempoolTxs.end());
m_mempoolTxs.resize(max_transactions);
}
LOGINFO(4, "mempool has " << total_mempool_transactions << " transactions, taking " << m_mempoolTxs.size() << " transactions from it");
}
int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run) int BlockTemplate::create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run)
{ {
// Miner transaction (coinbase) // Miner transaction (coinbase)

View file

@ -73,6 +73,7 @@ private:
RandomX_Hasher_Base* m_hasher; RandomX_Hasher_Base* m_hasher;
private: private:
void select_mempool_transactions(const Mempool& mempool);
int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run); int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run);
hash calc_sidechain_hash(uint32_t sidechain_extra_nonce) const; hash calc_sidechain_hash(uint32_t sidechain_extra_nonce) const;
hash calc_miner_tx_hash(uint32_t extra_nonce) const; hash calc_miner_tx_hash(uint32_t extra_nonce) const;