Blockchain: option for fast manual block popping

This commit is contained in:
jeffro256
2026-05-22 12:24:35 -05:00
parent 0857da6d6d
commit 9dbce17bb5
16 changed files with 87 additions and 77 deletions
+11 -14
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -172,13 +172,6 @@ void BlockchainDB::init_options(boost::program_options::options_description& des
command_line::add_arg(desc, arg_db_salvage);
}
void BlockchainDB::pop_block()
{
block blk;
std::vector<transaction> txs;
pop_block(blk, txs);
}
void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transaction& tx, const epee::span<const std::uint8_t> blob, const crypto::hash* tx_hash_ptr, const crypto::hash* tx_prunable_hash_ptr)
{
bool miner_tx = false;
@@ -311,7 +304,7 @@ void BlockchainDB::set_hard_fork(HardFork* hf)
m_hardfork = hf;
}
void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs)
void BlockchainDB::pop_block(block& blk, std::vector<transaction>* txs)
{
blk = get_top_block();
@@ -319,10 +312,13 @@ void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs)
for (const auto& h : boost::adaptors::reverse(blk.tx_hashes))
{
cryptonote::transaction tx;
if (!get_tx(h, tx) && !get_pruned_tx(h, tx))
throw DB_ERROR("Failed to get pruned or unpruned transaction from the db");
txs.push_back(std::move(tx));
if (txs)
{
cryptonote::transaction tx;
if (!get_tx(h, tx) && !get_pruned_tx(h, tx))
throw DB_ERROR("Failed to get pruned or unpruned transaction from the db");
txs->push_back(std::move(tx));
}
remove_transaction(h);
}
remove_transaction(get_transaction_hash(blk.miner_tx));
@@ -481,8 +477,9 @@ void BlockchainDB::fixup()
if (!has_key_image(ki))
{
LOG_PRINT_L1("Fixup: detected missing key images from block 202612! Popping blocks...");
block blk;
while (height() > 202612)
pop_block();
pop_block(blk, /*txs=*/nullptr);
}
}
}
+5 -13
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -538,13 +538,6 @@ private:
/*********************************************************************
* private concrete members
*********************************************************************/
/**
* @brief private version of pop_block, for undoing if an add_block fails
*
* This function simply calls pop_block(block& blk, std::vector<transaction>& txs)
* with dummy parameters, as the returns-by-reference can be discarded.
*/
void pop_block();
// helper function to remove transaction from blockchain
/**
@@ -1151,13 +1144,12 @@ public:
* implements remove_* functions.
*
* The subclass should return by reference the popped block and
* its associated transactions
* its associated transactions, if applicable to that overload
*
* @param blk return-by-reference the block which was popped
* @param txs return-by-reference the transactions from the popped block
* @param[out] blk the block which was popped
* @param[out] txs the non-coinbase transactions from the popped block (optional)
*/
virtual void pop_block(block& blk, std::vector<transaction>& txs);
virtual void pop_block(block& blk, std::vector<transaction>* txs);
/**
* @brief check if a transaction with a given hash exists
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
@@ -4157,7 +4157,7 @@ uint64_t BlockchainLMDB::add_block(const std::pair<block, blobdata>& blk, size_t
return ++m_height;
}
void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs)
void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>* txs)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
@@ -342,7 +342,7 @@ public:
bool block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) const;
void pop_block(block& blk, std::vector<transaction>& txs) override;
void pop_block(block& blk, std::vector<transaction>* txs) override;
bool can_thread_bulk_indices() const override { return true; }
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -100,11 +100,10 @@ int pop_blocks(cryptonote::core& core, int num_blocks)
int quit = 0;
block popped_block;
std::vector<transaction> popped_txs;
for (int i=0; i < num_blocks; ++i)
{
// simple_core.m_storage.pop_block_from_blockchain() is private, so call directly through db
core.get_blockchain_storage().get_db().pop_block(popped_block, popped_txs);
core.get_blockchain_storage().get_db().pop_block(popped_block, /*txs=*/nullptr);
quit = 1;
}
+12 -13
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -408,10 +408,9 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
MGINFO("Popping blocks... " << top_height);
++num_popped_blocks;
block popped_block;
std::vector<transaction> popped_txs;
try
{
m_db->pop_block(popped_block, popped_txs);
m_db->pop_block(popped_block, /*txs=*/nullptr);
}
// anything that could cause this to throw is likely catastrophic,
// so we re-throw
@@ -548,7 +547,7 @@ bool Blockchain::deinit()
//------------------------------------------------------------------
// This function removes blocks from the top of blockchain.
// It starts a batch and calls private method pop_block_from_blockchain().
void Blockchain::pop_blocks(uint64_t nblocks)
void Blockchain::pop_blocks(uint64_t nblocks, const bool keep_txs)
{
uint64_t i = 0;
CRITICAL_REGION_LOCAL(m_tx_pool);
@@ -563,7 +562,7 @@ void Blockchain::pop_blocks(uint64_t nblocks)
nblocks = std::min(nblocks, blockchain_height - 1);
while (i < nblocks && !m_cancel.load())
{
pop_block_from_blockchain();
pop_block_from_blockchain(keep_txs);
++i;
}
}
@@ -588,9 +587,9 @@ void Blockchain::pop_blocks(uint64_t nblocks)
}
//------------------------------------------------------------------
// This function tells BlockchainDB to remove the top block from the
// blockchain and then returns all transactions (except the miner tx, of course)
// from it to the tx_pool
block Blockchain::pop_block_from_blockchain()
// blockchain and then, if keep_txs is true, returns all transactions
// (except the miner tx, of course) from it to the tx_pool
block Blockchain::pop_block_from_blockchain(bool keep_txs)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -606,7 +605,7 @@ block Blockchain::pop_block_from_blockchain()
const uint8_t previous_hf_version = get_current_hard_fork_version();
try
{
m_db->pop_block(popped_block, popped_txs);
m_db->pop_block(popped_block, keep_txs ? &popped_txs : nullptr);
}
// anything that could cause this to throw is likely catastrophic,
// so we re-throw
@@ -626,7 +625,7 @@ block Blockchain::pop_block_from_blockchain()
// return transactions from popped block to the tx_pool
size_t pruned = 0;
for (transaction& tx : popped_txs)
if (keep_txs) for (transaction& tx : popped_txs)
{
if (tx.pruned)
{
@@ -1117,7 +1116,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
// remove blocks from blockchain until we get back to where we should be.
while (m_db->height() != rollback_height)
{
pop_block_from_blockchain();
pop_block_from_blockchain(/*keep_txs=*/true);
}
CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit");
@@ -1167,7 +1166,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
std::list<block> disconnected_chain;
while (m_db->top_block_hash() != alt_chain.front().bl.prev_id)
{
block b = pop_block_from_blockchain();
block b = pop_block_from_blockchain(/*keep_txs=*/true);
disconnected_chain.push_front(b);
}
CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit");
@@ -4342,7 +4341,7 @@ leave:
if (!update_next_cumulative_weight_limit())
{
MERROR("Failed to update next cumulative weight limit");
pop_block_from_blockchain();
pop_block_from_blockchain(/*keep_txs=*/true);
return false;
}
+6 -3
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -1131,8 +1131,9 @@ namespace cryptonote
* @brief removes blocks from the top of the blockchain
*
* @param nblocks number of blocks to be removed
* @param keep_txs whether to place popped non-coinbase transactions back into the mempool
*/
void pop_blocks(uint64_t nblocks);
void pop_blocks(uint64_t nblocks, bool keep_txs);
/**
* @brief checks whether a given block height is included in the precompiled block hash area
@@ -1377,9 +1378,11 @@ namespace cryptonote
/**
* @brief removes the most recent block from the blockchain
*
* @param keep_txs whether to place popped non-coinbase transactions back into the mempool
*
* @return the block removed
*/
block pop_block_from_blockchain();
block pop_block_from_blockchain(bool keep_txs);
/**
* @brief validate and add a new block to the end of the blockchain
+24 -4
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -931,9 +931,14 @@ bool t_command_parser_executor::sync_info(const std::vector<std::string>& args)
bool t_command_parser_executor::pop_blocks(const std::vector<std::string>& args)
{
if (args.size() != 1)
if (args.size() < 1)
{
std::cout << "Invalid syntax: One parameter expected. For more details, use the help command." << std::endl;
std::cout << "Invalid syntax: At least one parameter expected. For more details, use the help command." << std::endl;
return true;
}
else if (args.size() > 2)
{
std::cout << "Invalid syntax: A maximum of two parameters expected. For more details, use the help command." << std::endl;
return true;
}
@@ -945,7 +950,22 @@ bool t_command_parser_executor::pop_blocks(const std::vector<std::string>& args)
std::cout << "Invalid syntax: Number of blocks must be greater than 0. For more details, use the help command." << std::endl;
return true;
}
return m_executor.pop_blocks(nblocks);
bool keep_txs = false;
if (args.size() > 1)
{
const std::string keep_arg = args.at(1);
if (keep_arg == "keep-txs")
keep_txs = true;
else if (keep_arg == "no-keep-txs")
keep_txs = false;
else
{
std::cout << "Invalid syntax: Unrecognized argument: '" << keep_arg
<< "'. For more details, use the help command." << std::endl;
return true;
}
}
return m_executor.pop_blocks(nblocks, keep_txs);
}
catch (const boost::bad_lexical_cast&)
{
+3 -3
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -299,8 +299,8 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"pop_blocks"
, std::bind(&t_command_parser_executor::pop_blocks, &m_parser, p::_1)
, "pop_blocks <nblocks>"
, "Remove blocks from end of blockchain"
, "pop_blocks <nblocks> [keep-txs|no-keep-txs]"
, "Remove blocks from end of blockchain, optionally moving popped txs into the mempool (not default)"
);
m_command_lookup.set_handler(
"version"
+3 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -2310,13 +2310,14 @@ bool t_rpc_command_executor::sync_info()
return true;
}
bool t_rpc_command_executor::pop_blocks(uint64_t num_blocks)
bool t_rpc_command_executor::pop_blocks(uint64_t num_blocks, bool keep_txs)
{
cryptonote::COMMAND_RPC_POP_BLOCKS::request req;
cryptonote::COMMAND_RPC_POP_BLOCKS::response res;
std::string fail_message = "pop_blocks failed";
req.nblocks = num_blocks;
req.keep_txs = keep_txs;
if (m_is_rpc)
{
if (!m_rpc_client->rpc_request(req, res, "/pop_blocks", fail_message.c_str()))
+2 -2
View File
@@ -6,7 +6,7 @@
*/
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -154,7 +154,7 @@ public:
bool sync_info();
bool pop_blocks(uint64_t num_blocks);
bool pop_blocks(uint64_t num_blocks, bool keep_txs);
bool prune_blockchain();
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -3174,7 +3174,7 @@ namespace cryptonote
{
RPC_TRACKER(pop_blocks);
m_core.get_blockchain_storage().pop_blocks(req.nblocks);
m_core.get_blockchain_storage().pop_blocks(req.nblocks, req.keep_txs);
res.height = m_core.get_current_blockchain_height();
res.status = CORE_RPC_STATUS_OK;
+4 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -101,7 +101,7 @@ inline const std::string get_rpc_status(const bool trusted_daemon, const std::st
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 3
#define CORE_RPC_VERSION_MINOR 17
#define CORE_RPC_VERSION_MINOR 18
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -2498,10 +2498,12 @@ inline const std::string get_rpc_status(const bool trusted_daemon, const std::st
struct request_t: public rpc_request_base
{
uint64_t nblocks;
bool keep_txs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE(nblocks)
KV_SERIALIZE_OPT(keep_txs, true)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2019-2024, The Monero Project
// Copyright (c) 2019-2026, The Monero Project
//
// All rights reserved.
//
@@ -96,7 +96,7 @@ public:
*block_height = h - 1;
return top;
}
virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { blocks.pop_back(); }
virtual void pop_block(cryptonote::block &, std::vector<cryptonote::transaction> *) override { blocks.pop_back(); }
virtual void set_hard_fork_version(uint64_t height, uint8_t version) override { if (height >= hf.size()) hf.resize(height + 1); hf[height] = version; }
virtual uint8_t get_hard_fork_version(uint64_t height) const override { if (height >= hf.size()) return 255; return hf[height]; }
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2014-2024, The Monero Project
// Copyright (c) 2014-2026, The Monero Project
//
// All rights reserved.
//
@@ -132,7 +132,7 @@ namespace
return blocks[blocks.size()-1].bl;
}
virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { if (!blocks.empty()) blocks.pop_back(); }
virtual void pop_block(cryptonote::block &, std::vector<cryptonote::transaction> *) override { if (!blocks.empty()) blocks.pop_back(); }
virtual void set_hard_fork_version(uint64_t height, uint8_t version) override { if (height >= hf.size()) hf.resize(height + 1); hf[height] = version; }
virtual uint8_t get_hard_fork_version(uint64_t height) const override { if (height >= hf.size()) return 255; return hf[height]; }
+5 -8
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2019-2024, The Monero Project
// Copyright (c) 2019-2026, The Monero Project
//
// All rights reserved.
//
@@ -90,7 +90,7 @@ public:
*block_height = h - 1;
return top;
}
virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { blocks.pop_back(); }
virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> *txs) override { blocks.pop_back(); }
private:
std::vector<block_t> blocks;
@@ -206,9 +206,8 @@ TEST(long_term_block_weight, multi_pop)
}
cryptonote::block b;
std::vector<cryptonote::transaction> txs;
for (uint64_t h = 0; h < num_pop; ++h)
bc->get_db().pop_block(b, txs);
bc->get_db().pop_block(b, /*txs=*/nullptr);
ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median());
@@ -267,8 +266,7 @@ TEST(long_term_block_weight, pop_invariant_max)
for (int i = 0; i < remove; ++i)
{
cryptonote::block b;
std::vector<cryptonote::transaction> txs;
bc->get_db().pop_block(b, txs);
bc->get_db().pop_block(b, /*txs=*/nullptr);
ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
}
for (int i = 0; i < add; ++i)
@@ -317,8 +315,7 @@ TEST(long_term_block_weight, pop_invariant_random)
for (int i = 0; i < remove; ++i)
{
cryptonote::block b;
std::vector<cryptonote::transaction> txs;
bc->get_db().pop_block(b, txs);
bc->get_db().pop_block(b, /*txs=*/nullptr);
ASSERT_TRUE(bc->update_next_cumulative_weight_limit());
const uint64_t effective_median = bc->get_current_cumulative_block_weight_median();
const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit();