From b7f8c973f444410388e986286d422dad5c0bde60 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Mon, 21 Nov 2022 08:08:31 +0100 Subject: [PATCH] Refined transaction picking algorithm --- src/block_template.cpp | 110 +++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/src/block_template.cpp b/src/block_template.cpp index 22df40d..277f97b 100644 --- a/src/block_template.cpp +++ b/src/block_template.cpp @@ -34,6 +34,9 @@ static constexpr char log_category_prefix[] = "BlockTemplate "; +// Max P2P message size (128 KB) minus BLOCK_RESPONSE header (5 bytes) +static constexpr size_t MAX_BLOCK_TEMPLATE_SIZE = 128 * 1024 - (1 + sizeof(uint32_t)); + namespace p2pool { BlockTemplate::BlockTemplate(p2pool* pool) @@ -220,6 +223,39 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet m_difficulty = data.difficulty; m_seedHash = data.seed_hash; + m_blockHeader.clear(); + m_poolBlockTemplate->m_verified = false; + + // Major and minor hardfork version + m_blockHeader.push_back(data.major_version); + m_blockHeader.push_back(HARDFORK_SUPPORTED_VERSION); + m_poolBlockTemplate->m_majorVersion = data.major_version; + m_poolBlockTemplate->m_minorVersion = HARDFORK_SUPPORTED_VERSION; + + // Timestamp + m_timestamp = time(nullptr); + if (m_timestamp <= data.median_timestamp) { + LOGWARN(2, "timestamp adjusted from " << m_timestamp << " to " << data.median_timestamp + 1 << ". Fix your system time!"); + m_timestamp = data.median_timestamp + 1; + } + + writeVarint(m_timestamp, m_blockHeader); + m_poolBlockTemplate->m_timestamp = m_timestamp; + + // Previous block id + m_blockHeader.insert(m_blockHeader.end(), data.prev_id.h, data.prev_id.h + HASH_SIZE); + m_prevId = data.prev_id; + m_poolBlockTemplate->m_prevId = m_prevId; + + // Miner nonce + m_nonceOffset = m_blockHeader.size(); + m_blockHeader.insert(m_blockHeader.end(), NONCE_SIZE, 0); + m_poolBlockTemplate->m_nonce = 0; + + m_blockHeaderSize = m_blockHeader.size(); + + m_pool->side_chain().fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares); + // Only choose transactions that were received 10 or more seconds ago size_t total_mempool_transactions; { @@ -240,17 +276,36 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet // 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 - // Select 1000 transactions with the highest fee per byte - if (m_mempoolTxs.size() > 1000) { - std::nth_element(m_mempoolTxs.begin(), m_mempoolTxs.begin() + 1000, m_mempoolTxs.end(), - [](const TxMempoolData& tx_a, const TxMempoolData& tx_b) - { - return tx_a.fee * tx_b.weight > tx_b.fee * tx_a.weight; - }); - m_mempoolTxs.resize(1000); - } + // Calculate how many transactions we can take + { + PoolBlock* b = m_poolBlockTemplate; + b->m_transactions.clear(); + b->m_transactions.resize(1); + b->m_outputs.clear(); - LOGINFO(4, "mempool has " << total_mempool_transactions << " transactions, taking " << m_mempoolTxs.size() << " transactions from it"); + // Block template size without coinbase outputs and transactions (add 1+1 more bytes for output and tx count if they go above 128) + size_t k = b->serialize_mainchain_data().size() + b->serialize_sidechain_data().size() + 2; + + // a rough estimation of outputs' size + // all outputs have <= 5 bytes for each output's reward, and up to 18 outputs can have 6 bytes for output's reward + k += m_shares.size() * (5 /* reward */ + 1 /* tx_type */ + HASH_SIZE /* stealth address */ + 1 /* viewtag */) + 18; + + const size_t max_transactions = (MAX_BLOCK_TEMPLATE_SIZE > k) ? ((MAX_BLOCK_TEMPLATE_SIZE - k) / HASH_SIZE) : 0; + + 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(), + [](const TxMempoolData& tx_a, const TxMempoolData& tx_b) + { + return tx_a.fee * tx_b.weight > tx_b.fee * tx_a.weight; + }); + 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); @@ -268,38 +323,6 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet " transactions, fees = " << log::Gray() << log::XMRAmount(total_tx_fees) << log::NoColor() << ", weight = " << log::Gray() << total_tx_weight); - m_blockHeader.clear(); - m_poolBlockTemplate->m_verified = false; - - // Major and minor hardfork version - m_blockHeader.push_back(data.major_version); - m_blockHeader.push_back(HARDFORK_SUPPORTED_VERSION); - m_poolBlockTemplate->m_majorVersion = data.major_version; - m_poolBlockTemplate->m_minorVersion = HARDFORK_SUPPORTED_VERSION; - - // Timestamp - m_timestamp = time(nullptr); - if (m_timestamp <= data.median_timestamp) { - LOGWARN(2, "timestamp adjusted from " << m_timestamp << " to " << data.median_timestamp + 1 << ". Fix your system time!"); - m_timestamp = data.median_timestamp + 1; - } - - writeVarint(m_timestamp, m_blockHeader); - m_poolBlockTemplate->m_timestamp = m_timestamp; - - // Previous block id - m_blockHeader.insert(m_blockHeader.end(), data.prev_id.h, data.prev_id.h + HASH_SIZE); - m_prevId = data.prev_id; - m_poolBlockTemplate->m_prevId = m_prevId; - - // Miner nonce - m_nonceOffset = m_blockHeader.size(); - m_blockHeader.insert(m_blockHeader.end(), NONCE_SIZE, 0); - m_poolBlockTemplate->m_nonce = 0; - - m_blockHeaderSize = m_blockHeader.size(); - - m_pool->side_chain().fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares); if (!SideChain::split_reward(max_reward, m_shares, m_rewards)) { use_old_template(); return; @@ -382,7 +405,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet // Try replacing other transactions when we are above the limit if (final_weight + tx.weight > data.median_weight) { - for (int j = 0; j < i; ++j) { + // Don't check more than 100 transactions deep because they have higher and higher fee/byte + for (int j = i - 1, j1 = std::max(0, i - 100); j >= j1; --j) { const TxMempoolData& prev_tx = m_mempoolTxs[m_mempoolTxsOrder[j]]; const uint64_t reward2 = get_block_reward(base_reward, data.median_weight, final_fees + tx.fee - prev_tx.fee, final_weight + tx.weight - prev_tx.weight); if (reward2 > final_reward) {