mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-13 03:30:42 +02:00
Merge 20702 via rpc_getblocklocations
This commit is contained in:
commit
34b69da316
@ -3206,6 +3206,77 @@ return RPCHelpMan{
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static RPCHelpMan getblocklocations()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{"getblocklocations",
|
||||||
|
"\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
|
||||||
|
"\nReturns a JSON for the file system location of 'blockhash' block and undo data.\n"
|
||||||
|
"\nIt is possible to return also the locations of previous blocks, by specifying 'nblocks' > 1.\n",
|
||||||
|
{
|
||||||
|
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
|
||||||
|
{"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "Maximum number locations to return (up to genesis block)"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RPCResult{
|
||||||
|
RPCResult::Type::ARR, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::OBJ, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::NUM, "file", "blk*.dat/rev*.dat file index"},
|
||||||
|
{RPCResult::Type::NUM, "data", "block data file offset"},
|
||||||
|
{RPCResult::Type::NUM, "undo", /*optional=*/true, "undo data file offset (if exists)"},
|
||||||
|
{RPCResult::Type::STR_HEX, "prev", "previous block hash"},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("getblocklocations", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 10")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
|
||||||
|
|
||||||
|
ChainstateManager& chainman = EnsureAnyChainman(request.context);
|
||||||
|
if (chainman.m_blockman.IsPruneMode()) {
|
||||||
|
throw JSONRPCError(RPC_MISC_ERROR, "Block locations are not available in prune mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 hash(ParseHashV(request.params[0], "blockhash"));
|
||||||
|
size_t nblocks = request.params[1].getInt<size_t>();
|
||||||
|
|
||||||
|
const CBlockIndex* pblockindex = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(hash));
|
||||||
|
if (!pblockindex) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue result(UniValue::VARR);
|
||||||
|
do {
|
||||||
|
int64_t file_num;
|
||||||
|
uint64_t data_pos, undo_pos;
|
||||||
|
{
|
||||||
|
LOCK(::cs_main);
|
||||||
|
file_num = pblockindex->nFile;
|
||||||
|
data_pos = pblockindex->nDataPos;
|
||||||
|
undo_pos = pblockindex->nUndoPos;
|
||||||
|
}
|
||||||
|
UniValue location(UniValue::VOBJ);
|
||||||
|
location.pushKV("file", file_num);
|
||||||
|
location.pushKV("data", data_pos);
|
||||||
|
if (undo_pos) {
|
||||||
|
location.pushKV("undo", undo_pos);
|
||||||
|
}
|
||||||
|
if (pblockindex->pprev) {
|
||||||
|
location.pushKV("prev", pblockindex->pprev->GetBlockHash().GetHex());
|
||||||
|
} else {
|
||||||
|
location.pushKV("prev", uint256().GetHex());
|
||||||
|
}
|
||||||
|
result.push_back(location);
|
||||||
|
pblockindex = pblockindex->pprev;
|
||||||
|
} while (result.size() < nblocks && pblockindex);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void RegisterBlockchainRPCCommands(CRPCTable& t)
|
void RegisterBlockchainRPCCommands(CRPCTable& t)
|
||||||
{
|
{
|
||||||
@ -3241,6 +3312,7 @@ void RegisterBlockchainRPCCommands(CRPCTable& t)
|
|||||||
{"hidden", &waitforblock},
|
{"hidden", &waitforblock},
|
||||||
{"hidden", &waitforblockheight},
|
{"hidden", &waitforblockheight},
|
||||||
{"hidden", &syncwithvalidationinterfacequeue},
|
{"hidden", &syncwithvalidationinterfacequeue},
|
||||||
|
{"hidden", &getblocklocations},
|
||||||
};
|
};
|
||||||
for (const auto& c : commands) {
|
for (const auto& c : commands) {
|
||||||
t.appendCommand(c.name, &c);
|
t.appendCommand(c.name, &c);
|
||||||
|
@ -113,6 +113,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||||||
{ "getblock", 1, "verbosity" },
|
{ "getblock", 1, "verbosity" },
|
||||||
{ "getblock", 1, "verbose" },
|
{ "getblock", 1, "verbose" },
|
||||||
{ "getblockheader", 1, "verbose" },
|
{ "getblockheader", 1, "verbose" },
|
||||||
|
{ "getblocklocations", 1, "nblocks" },
|
||||||
{ "getchaintxstats", 0, "nblocks" },
|
{ "getchaintxstats", 0, "nblocks" },
|
||||||
{ "gettransaction", 1, "include_watchonly" },
|
{ "gettransaction", 1, "include_watchonly" },
|
||||||
{ "gettransaction", 2, "verbose" },
|
{ "gettransaction", 2, "verbose" },
|
||||||
|
@ -123,6 +123,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
|
|||||||
"getblockfrompeer", // when no peers are connected, no p2p message is sent
|
"getblockfrompeer", // when no peers are connected, no p2p message is sent
|
||||||
"getblockhash",
|
"getblockhash",
|
||||||
"getblockheader",
|
"getblockheader",
|
||||||
|
"getblocklocations",
|
||||||
"getblockstats",
|
"getblockstats",
|
||||||
"getblocktemplate",
|
"getblocktemplate",
|
||||||
"getchaintips",
|
"getchaintips",
|
||||||
|
86
test/functional/rpc_getblocklocations.py
Executable file
86
test/functional/rpc_getblocklocations.py
Executable file
@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2019-2020 The Bitcoin Core developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
"""Test the getblocklocations rpc call."""
|
||||||
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
from test_framework.util import (
|
||||||
|
assert_equal,
|
||||||
|
assert_raises_rpc_error,
|
||||||
|
util_xor,
|
||||||
|
)
|
||||||
|
from test_framework.messages import ser_vector
|
||||||
|
|
||||||
|
|
||||||
|
class GetblocklocationsTest(BitcoinTestFramework):
|
||||||
|
def set_test_params(self):
|
||||||
|
self.setup_clean_chain = True
|
||||||
|
self.num_nodes = 1
|
||||||
|
|
||||||
|
def run_test(self):
|
||||||
|
"""Test a trivial usage of the getblocklocations RPC command."""
|
||||||
|
node = self.nodes[0]
|
||||||
|
test_block_count = 7
|
||||||
|
self.generate(node, test_block_count)
|
||||||
|
|
||||||
|
NULL_HASH = '0000000000000000000000000000000000000000000000000000000000000000'
|
||||||
|
|
||||||
|
block_hashes = [node.getblockhash(height) for height in range(test_block_count)]
|
||||||
|
block_hashes.reverse()
|
||||||
|
|
||||||
|
block_locations = {}
|
||||||
|
def check_consistency(tip, a):
|
||||||
|
for o in a:
|
||||||
|
if tip in block_locations:
|
||||||
|
assert_equal(block_locations[tip], o)
|
||||||
|
else:
|
||||||
|
block_locations[tip] = o
|
||||||
|
tip = o['prev']
|
||||||
|
|
||||||
|
# Get blocks' locations using several batch sizes
|
||||||
|
last_locations = None
|
||||||
|
for batch_size in range(1, 10):
|
||||||
|
locations = []
|
||||||
|
tip = block_hashes[0]
|
||||||
|
while tip != NULL_HASH:
|
||||||
|
locations.extend(node.getblocklocations(tip, batch_size))
|
||||||
|
check_consistency(block_hashes[0], locations)
|
||||||
|
tip = locations[-1]['prev']
|
||||||
|
if last_locations: assert_equal(last_locations, locations)
|
||||||
|
last_locations = locations
|
||||||
|
|
||||||
|
xor_key = node.read_xor_key()
|
||||||
|
|
||||||
|
# Read blocks' data from the file system
|
||||||
|
blocks_dir = node.chain_path / 'blocks'
|
||||||
|
with (blocks_dir / 'blk00000.dat').open('rb') as blkfile:
|
||||||
|
for block_hash in block_hashes:
|
||||||
|
location = block_locations[block_hash]
|
||||||
|
block_bytes = bytes.fromhex(node.getblock(block_hash, 0))
|
||||||
|
assert_file_contains(blkfile, location['data'], block_bytes, xor_key)
|
||||||
|
|
||||||
|
|
||||||
|
empty_undo = ser_vector([]) # empty blocks = no transactions to undo
|
||||||
|
with (blocks_dir / 'rev00000.dat').open('rb') as revfile:
|
||||||
|
for block_hash in block_hashes[:-1]: # skip genesis block (has no undo)
|
||||||
|
location = block_locations[block_hash]
|
||||||
|
assert_file_contains(revfile, location['undo'], empty_undo, xor_key)
|
||||||
|
|
||||||
|
# Fail getting unknown block
|
||||||
|
unknown_block_hash = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
|
||||||
|
assert_raises_rpc_error(-5, 'Block not found', node.getblocklocations, unknown_block_hash, 3)
|
||||||
|
|
||||||
|
# Fail in pruned mode
|
||||||
|
self.restart_node(0, ['-prune=1'])
|
||||||
|
tip = block_hashes[0]
|
||||||
|
assert_raises_rpc_error(-1, 'Block locations are not available in prune mode', node.getblocklocations, tip, 3)
|
||||||
|
|
||||||
|
|
||||||
|
def assert_file_contains(fileobj, offset, data, xor_key):
|
||||||
|
fileobj.seek(offset)
|
||||||
|
read_data = fileobj.read(len(data))
|
||||||
|
read_data = util_xor(read_data, xor_key, offset=offset)
|
||||||
|
assert_equal(read_data, data)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
GetblocklocationsTest(__file__).main()
|
@ -333,6 +333,7 @@ BASE_SCRIPTS = [
|
|||||||
'wallet_fallbackfee.py --legacy-wallet',
|
'wallet_fallbackfee.py --legacy-wallet',
|
||||||
'wallet_fallbackfee.py --descriptors',
|
'wallet_fallbackfee.py --descriptors',
|
||||||
'rpc_dumptxoutset.py',
|
'rpc_dumptxoutset.py',
|
||||||
|
'rpc_getblocklocations.py',
|
||||||
'feature_minchainwork.py',
|
'feature_minchainwork.py',
|
||||||
'rpc_estimatefee.py',
|
'rpc_estimatefee.py',
|
||||||
'rpc_getblockstats.py',
|
'rpc_getblockstats.py',
|
||||||
|
Loading…
Reference in New Issue
Block a user