Daemon RPC: getblocks.bin block_ids_exclusive req param

When the request includes block_ids, the daemon uses
find_blockchain_supplement to identify the highest block hash
passed in block_ids that the daemon also knows about, and then
serves subsequent blocks contiguous to that block.

When block_ids_skip_exclusive is false (default current
behavior), the daemon includes the highest block requested in the
response, in addition to contiguous blocks after it.

When block_ids_skip_exclusive is true (new param), the daemon
serves blocks starting from the block 1 higher than the highest
known block included in block_ids. Thus, the daemon skips the
common block known to the client and daemon. Clients can make sure
the daemon is serving expected contiguous blocks to its highest
known block by checking the first block's prev_id included in the
response, and making sure it is equivalent to the block the client
already knows about that was included in block_ids. This avoids
the daemon serving 1 extra block it does not need to serve to the
client, since the client should already know about that block.

bl
This commit is contained in:
j-berman
2025-10-10 01:21:52 -07:00
parent 8e9ab9677f
commit 4a2d6eb3da
7 changed files with 29 additions and 10 deletions
+19 -3
View File
@@ -2736,17 +2736,18 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
// find split point between ours and foreign blockchain (or start at
// blockchain height <req_start_block>), and return up to max_count FULL
// blocks by reference.
bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const
bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, const bool qblock_ids_exclusive, size_t max_block_count, size_t max_tx_count) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
top_hash = m_db->top_block_hash(&total_height);
++total_height;
// if a specific start height has been requested
if(req_start_block > 0)
{
// if requested height is higher than our chain, return false -- we can't help
top_hash = m_db->top_block_hash(&total_height);
++total_height;
if (req_start_block >= total_height)
{
return false;
@@ -2755,15 +2756,30 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
}
else
{
// find_blockchain_supplement's start_height is the highest block idx included in qblock_ids that's *also* in the main chain
if(!find_blockchain_supplement(qblock_ids, start_height))
{
return false;
}
if (qblock_ids_exclusive)
{
// start from 1 block higher than the first common block (i.e. from the first block the client might not know about)
++start_height;
// if start_height is now the chain tip, we can return a truthy empty resp
if (start_height == total_height)
{
LOG_PRINT_L3("Returning empty find_blockchain_supplement, start_height: " << start_height);
blocks.clear();
return true;
}
}
}
db_rtxn_guard rtxn_guard(m_db);
top_hash = m_db->top_block_hash(&total_height);
++total_height;
CHECK_AND_ASSERT_MES(total_height > start_height, false, "chain height expected to be higher than start block");
blocks.reserve(std::min(std::min(max_block_count, (size_t)10000), (size_t)(total_height - start_height)));
CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_block_count, max_tx_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash),
false, "Error getting blocks");
+2 -1
View File
@@ -504,12 +504,13 @@ namespace cryptonote
* @param top_hash return-by-reference top block hash
* @param start_height return-by-reference the height of the first block returned
* @param pruned whether to return full or pruned tx blobs
* @param qblock_ids_exclusive when using qblock_ids, whether or not to include highest common block in response
* @param max_block_count the max number of blocks to get
* @param max_tx_count the max number of txes to get (it can get overshot by the last block's number of txes minus 1)
*
* @return true if a block found in common or req_start_block specified, else false
*/
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const;
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, const bool qblock_ids_exclusive, size_t max_block_count, size_t max_tx_count) const;
/**
* @brief retrieves a set of blocks and their transactions, and possibly other transactions
+2 -2
View File
@@ -1225,9 +1225,9 @@ namespace cryptonote
return m_blockchain_storage.find_blockchain_supplement(qblock_ids, clip_pruned, resp);
}
//-----------------------------------------------------------------------------------------------
bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const
bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, const bool qblock_ids_exclusive, size_t max_block_count, size_t max_tx_count) const
{
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, top_hash, start_height, pruned, get_miner_tx_hash, max_block_count, max_tx_count);
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, top_hash, start_height, pruned, get_miner_tx_hash, qblock_ids_exclusive, max_block_count, max_tx_count);
}
//-----------------------------------------------------------------------------------------------
bool core::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const
+1 -1
View File
@@ -593,7 +593,7 @@ namespace cryptonote
*
* @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::vector<std::pair<cryptonote::blobdata, std::vector<transaction> > >&, uint64_t&, uint64_t&, size_t) const
*/
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const;
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, const bool qblock_ids_exclusive, size_t max_block_count, size_t max_tx_count) const;
/**
* @copydoc Blockchain::get_tx_outputs_gindexs
+1 -1
View File
@@ -755,7 +755,7 @@ namespace cryptonote
}
std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.top_block_hash, res.start_height, req.prune, !req.no_miner_tx, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.top_block_hash, res.start_height, req.prune, !req.no_miner_tx, req.block_ids_exclusive, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
{
res.status = "Failed";
add_host_fail(ctx);
+3 -1
View File
@@ -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 16
#define CORE_RPC_VERSION_MINOR 17
#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)
@@ -189,6 +189,7 @@ inline const std::string get_rpc_status(const bool trusted_daemon, const std::st
uint64_t start_height;
bool prune;
bool no_miner_tx;
bool block_ids_exclusive;
uint64_t pool_info_since;
uint64_t max_block_count;
@@ -199,6 +200,7 @@ inline const std::string get_rpc_status(const bool trusted_daemon, const std::st
KV_SERIALIZE(start_height)
KV_SERIALIZE(prune)
KV_SERIALIZE_OPT(no_miner_tx, false)
KV_SERIALIZE_OPT(block_ids_exclusive, false)
KV_SERIALIZE_OPT(pool_info_since, (uint64_t)0)
KV_SERIALIZE_OPT(max_block_count, (uint64_t)0)
END_KV_SERIALIZE_MAP()
+1 -1
View File
@@ -128,7 +128,7 @@ namespace rpc
{
std::vector<std::pair<std::pair<blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, blobdata> > > > blocks;
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.top_block_hash, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.top_block_hash, res.start_height, req.prune, true, false, COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
{
res.status = Message::STATUS_FAILED;
res.error_details = "core::find_blockchain_supplement() returned false";