Merge pull request #7169
bbe3b27
tx_pool: full tx revalidation on fork boundaries (moneromooo-monero)
This commit is contained in:
commit
c3b1b94453
|
@ -588,6 +588,7 @@ block Blockchain::pop_block_from_blockchain()
|
|||
|
||||
CHECK_AND_ASSERT_THROW_MES(m_db->height() > 1, "Cannot pop the genesis block");
|
||||
|
||||
const uint8_t previous_hf_version = get_current_hard_fork_version();
|
||||
try
|
||||
{
|
||||
m_db->pop_block(popped_block, popped_txs);
|
||||
|
@ -650,6 +651,13 @@ block Blockchain::pop_block_from_blockchain()
|
|||
m_tx_pool.on_blockchain_dec(top_block_height, top_block_hash);
|
||||
invalidate_block_template_cache();
|
||||
|
||||
const uint8_t new_hf_version = get_current_hard_fork_version();
|
||||
if (new_hf_version != previous_hf_version)
|
||||
{
|
||||
MINFO("Validating txpool for v" << (unsigned)new_hf_version);
|
||||
m_tx_pool.validate(new_hf_version);
|
||||
}
|
||||
|
||||
return popped_block;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
@ -4392,6 +4400,19 @@ leave:
|
|||
get_difficulty_for_next_block(); // just to cache it
|
||||
invalidate_block_template_cache();
|
||||
|
||||
const uint8_t new_hf_version = get_current_hard_fork_version();
|
||||
if (new_hf_version != hf_version)
|
||||
{
|
||||
// the genesis block is added before everything's setup, and the txpool is empty
|
||||
// when we start from scratch, so we skip this
|
||||
const bool is_genesis_block = new_height == 1;
|
||||
if (!is_genesis_block)
|
||||
{
|
||||
MGINFO("Validating txpool for v" << (unsigned)new_hf_version);
|
||||
m_tx_pool.validate(new_hf_version);
|
||||
}
|
||||
}
|
||||
|
||||
send_miner_notifications(id, already_generated_coins);
|
||||
|
||||
for (const auto& notifier: m_block_notifiers)
|
||||
|
|
|
@ -1568,61 +1568,59 @@ namespace cryptonote
|
|||
{
|
||||
CRITICAL_REGION_LOCAL(m_transactions_lock);
|
||||
CRITICAL_REGION_LOCAL1(m_blockchain);
|
||||
size_t tx_weight_limit = get_transaction_weight_limit(version);
|
||||
std::unordered_set<crypto::hash> remove;
|
||||
|
||||
m_txpool_weight = 0;
|
||||
m_blockchain.for_all_txpool_txes([this, &remove, tx_weight_limit](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref*) {
|
||||
m_txpool_weight += meta.weight;
|
||||
if (meta.weight > tx_weight_limit) {
|
||||
LOG_PRINT_L1("Transaction " << txid << " is too big (" << meta.weight << " bytes), removing it from pool");
|
||||
remove.insert(txid);
|
||||
}
|
||||
else if (m_blockchain.have_tx(txid)) {
|
||||
LOG_PRINT_L1("Transaction " << txid << " is in the blockchain, removing it from pool");
|
||||
remove.insert(txid);
|
||||
}
|
||||
MINFO("Validating txpool contents for v" << (unsigned)version);
|
||||
|
||||
LockedTXN lock(m_blockchain.get_db());
|
||||
|
||||
struct tx_entry_t
|
||||
{
|
||||
crypto::hash txid;
|
||||
txpool_tx_meta_t meta;
|
||||
};
|
||||
|
||||
// get all txids
|
||||
std::vector<tx_entry_t> txes;
|
||||
m_blockchain.for_all_txpool_txes([this, &txes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata_ref*) {
|
||||
if (!meta.pruned) // skip pruned txes
|
||||
txes.push_back({txid, meta});
|
||||
return true;
|
||||
}, false, relay_category::all);
|
||||
|
||||
size_t n_removed = 0;
|
||||
if (!remove.empty())
|
||||
{
|
||||
LockedTXN lock(m_blockchain.get_db());
|
||||
for (const crypto::hash &txid: remove)
|
||||
// take them all out and add them back in, some might fail
|
||||
size_t added = 0;
|
||||
for (auto &e: txes)
|
||||
{
|
||||
try
|
||||
{
|
||||
cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all);
|
||||
size_t weight;
|
||||
uint64_t fee;
|
||||
cryptonote::transaction tx;
|
||||
if (!parse_and_validate_tx_from_blob(txblob, tx)) // remove pruned ones on startup, they're meant to be temporary
|
||||
cryptonote::blobdata blob;
|
||||
bool relayed, do_not_relay, double_spend_seen, pruned;
|
||||
if (!take_tx(e.txid, tx, blob, weight, fee, relayed, do_not_relay, double_spend_seen, pruned))
|
||||
MERROR("Failed to get tx " << e.txid << " from txpool for re-validation");
|
||||
|
||||
cryptonote::tx_verification_context tvc{};
|
||||
relay_method tx_relay = e.meta.get_relay_method();
|
||||
if (!add_tx(tx, e.txid, blob, e.meta.weight, tvc, tx_relay, relayed, version))
|
||||
{
|
||||
MERROR("Failed to parse tx from txpool");
|
||||
MINFO("Failed to re-validate tx " << e.txid << " for v" << (unsigned)version << ", dropped");
|
||||
continue;
|
||||
}
|
||||
// remove tx from db first
|
||||
m_blockchain.remove_txpool_tx(txid);
|
||||
m_txpool_weight -= get_transaction_weight(tx, txblob.size());
|
||||
remove_transaction_keyimages(tx, txid);
|
||||
auto sorted_it = find_tx_in_sorted_container(txid);
|
||||
if (sorted_it == m_txs_by_fee_and_receive_time.end())
|
||||
{
|
||||
LOG_PRINT_L1("Removing tx " << txid << " from tx pool, but it was not found in the sorted txs container!");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_txs_by_fee_and_receive_time.erase(sorted_it);
|
||||
}
|
||||
++n_removed;
|
||||
m_blockchain.update_txpool_tx(e.txid, e.meta);
|
||||
++added;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
MERROR("Failed to remove invalid tx from pool");
|
||||
// continue
|
||||
MERROR("Failed to re-validate tx from pool");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
lock.commit();
|
||||
}
|
||||
|
||||
const size_t n_removed = txes.size() - added;
|
||||
if (n_removed > 0)
|
||||
++m_cookie;
|
||||
return n_removed;
|
||||
|
|
|
@ -106,10 +106,16 @@ static uint32_t lcg()
|
|||
|
||||
}
|
||||
|
||||
struct BlockchainAndPool
|
||||
{
|
||||
cryptonote::tx_memory_pool txpool;
|
||||
cryptonote::Blockchain bc;
|
||||
BlockchainAndPool(): txpool(bc), bc(txpool) {}
|
||||
};
|
||||
|
||||
#define PREFIX_WINDOW(hf_version,window) \
|
||||
std::unique_ptr<cryptonote::Blockchain> bc; \
|
||||
cryptonote::tx_memory_pool txpool(*bc); \
|
||||
bc.reset(new cryptonote::Blockchain(txpool)); \
|
||||
BlockchainAndPool bap; \
|
||||
cryptonote::Blockchain *bc = &bap.bc; \
|
||||
struct get_test_options { \
|
||||
const std::pair<uint8_t, uint64_t> hard_forks[3]; \
|
||||
const cryptonote::test_options test_options = { \
|
||||
|
@ -118,8 +124,7 @@ static uint32_t lcg()
|
|||
}; \
|
||||
get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)1), std::make_pair((uint8_t)0, (uint64_t)0)} {} \
|
||||
} opts; \
|
||||
cryptonote::Blockchain *blockchain = bc.get(); \
|
||||
bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); \
|
||||
bool r = bc->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); \
|
||||
ASSERT_TRUE(r)
|
||||
|
||||
#define PREFIX(hf_version) PREFIX_WINDOW(hf_version, TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW)
|
||||
|
|
Loading…
Reference in New Issue