Refined transaction picking algorithm

pull/226/head
SChernykh 1 year ago
parent 98e51feb46
commit b7f8c973f4

@ -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<int>(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) {

Loading…
Cancel
Save