rpc: Add getblockfilter RPC method.
Retrieves and returns block filter and header from index.
This commit is contained in:
parent
ff35105096
commit
19308c9e21
3 changed files with 142 additions and 0 deletions
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <amount.h>
|
#include <amount.h>
|
||||||
#include <base58.h>
|
#include <base58.h>
|
||||||
|
#include <blockfilter.h>
|
||||||
#include <chain.h>
|
#include <chain.h>
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
#include <checkpoints.h>
|
#include <checkpoints.h>
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
#include <core_io.h>
|
#include <core_io.h>
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
|
#include <index/blockfilterindex.h>
|
||||||
#include <index/txindex.h>
|
#include <index/txindex.h>
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
#include <policy/feerate.h>
|
#include <policy/feerate.h>
|
||||||
|
@ -2296,6 +2298,85 @@ UniValue scantxoutset(const JSONRPCRequest& request)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static UniValue getblockfilter(const JSONRPCRequest& request)
|
||||||
|
{
|
||||||
|
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
RPCHelpMan{"getblockfilter",
|
||||||
|
"\nRetrieve a BIP 157 content filter for a particular block.\n",
|
||||||
|
{
|
||||||
|
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hash of the block"},
|
||||||
|
{"filtertype", RPCArg::Type::STR, /*default*/ "basic", "The type name of the filter"},
|
||||||
|
},
|
||||||
|
RPCResult{
|
||||||
|
"{\n"
|
||||||
|
" \"filter\" : (string) the hex-encoded filter data\n"
|
||||||
|
" \"header\" : (string) the hex-encoded filter header\n"
|
||||||
|
"}\n"
|
||||||
|
},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"")
|
||||||
|
}
|
||||||
|
}.ToString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 block_hash = ParseHashV(request.params[0], "blockhash");
|
||||||
|
std::string filtertype_name = "basic";
|
||||||
|
if (!request.params[1].isNull()) {
|
||||||
|
filtertype_name = request.params[1].get_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockFilterType filtertype;
|
||||||
|
if (!BlockFilterTypeByName(filtertype_name, filtertype)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype");
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
|
||||||
|
if (!index) {
|
||||||
|
throw JSONRPCError(RPC_MISC_ERROR, "Index is not enabled for filtertype " + filtertype_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CBlockIndex* block_index;
|
||||||
|
bool block_was_connected;
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
block_index = LookupBlockIndex(block_hash);
|
||||||
|
if (!block_index) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||||
|
}
|
||||||
|
block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool index_ready = index->BlockUntilSyncedToCurrentChain();
|
||||||
|
|
||||||
|
BlockFilter filter;
|
||||||
|
uint256 filter_header;
|
||||||
|
if (!index->LookupFilter(block_index, filter) ||
|
||||||
|
!index->LookupFilterHeader(block_index, filter_header)) {
|
||||||
|
int err_code;
|
||||||
|
std::string errmsg = "Filter not found.";
|
||||||
|
|
||||||
|
if (!block_was_connected) {
|
||||||
|
err_code = RPC_INVALID_ADDRESS_OR_KEY;
|
||||||
|
errmsg += " Block was not connected to active chain.";
|
||||||
|
} else if (!index_ready) {
|
||||||
|
err_code = RPC_MISC_ERROR;
|
||||||
|
errmsg += " Block filters are still in the process of being indexed.";
|
||||||
|
} else {
|
||||||
|
err_code = RPC_INTERNAL_ERROR;
|
||||||
|
errmsg += " This error is unexpected and indicates index corruption.";
|
||||||
|
}
|
||||||
|
|
||||||
|
throw JSONRPCError(err_code, errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue ret(UniValue::VOBJ);
|
||||||
|
ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
|
||||||
|
ret.pushKV("header", filter_header.GetHex());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const CRPCCommand commands[] =
|
static const CRPCCommand commands[] =
|
||||||
{ // category name actor (function) argNames
|
{ // category name actor (function) argNames
|
||||||
|
@ -2323,6 +2404,7 @@ static const CRPCCommand commands[] =
|
||||||
|
|
||||||
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
|
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
|
||||||
{ "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} },
|
{ "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} },
|
||||||
|
{ "blockchain", "getblockfilter", &getblockfilter, {"blockhash", "filtertype"} },
|
||||||
|
|
||||||
/* Not shown in help */
|
/* Not shown in help */
|
||||||
{ "hidden", "invalidateblock", &invalidateblock, {"blockhash"} },
|
{ "hidden", "invalidateblock", &invalidateblock, {"blockhash"} },
|
||||||
|
|
59
test/functional/rpc_getblockfilter.py
Executable file
59
test/functional/rpc_getblockfilter.py
Executable file
|
@ -0,0 +1,59 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2018 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 getblockfilter RPC."""
|
||||||
|
|
||||||
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
from test_framework.util import (
|
||||||
|
assert_equal, assert_is_hex_string, assert_raises_rpc_error,
|
||||||
|
connect_nodes, disconnect_nodes, sync_blocks
|
||||||
|
)
|
||||||
|
|
||||||
|
FILTER_TYPES = ["basic"]
|
||||||
|
|
||||||
|
class GetBlockFilterTest(BitcoinTestFramework):
|
||||||
|
def set_test_params(self):
|
||||||
|
self.setup_clean_chain = True
|
||||||
|
self.num_nodes = 2
|
||||||
|
self.extra_args = [["-blockfilterindex"], []]
|
||||||
|
|
||||||
|
def run_test(self):
|
||||||
|
# Create two chains by disconnecting nodes 0 & 1, mining, then reconnecting
|
||||||
|
disconnect_nodes(self.nodes[0], 1)
|
||||||
|
|
||||||
|
self.nodes[0].generate(3)
|
||||||
|
self.nodes[1].generate(4)
|
||||||
|
|
||||||
|
assert_equal(self.nodes[0].getblockcount(), 3)
|
||||||
|
chain0_hashes = [self.nodes[0].getblockhash(block_height) for block_height in range(4)]
|
||||||
|
|
||||||
|
# Reorg node 0 to a new chain
|
||||||
|
connect_nodes(self.nodes[0], 1)
|
||||||
|
sync_blocks(self.nodes)
|
||||||
|
|
||||||
|
assert_equal(self.nodes[0].getblockcount(), 4)
|
||||||
|
chain1_hashes = [self.nodes[0].getblockhash(block_height) for block_height in range(4)]
|
||||||
|
|
||||||
|
# Test getblockfilter returns a filter for all blocks and filter types on active chain
|
||||||
|
for block_hash in chain1_hashes:
|
||||||
|
for filter_type in FILTER_TYPES:
|
||||||
|
result = self.nodes[0].getblockfilter(block_hash, filter_type)
|
||||||
|
assert_is_hex_string(result['filter'])
|
||||||
|
|
||||||
|
# Test getblockfilter returns a filter for all blocks and filter types on stale chain
|
||||||
|
for block_hash in chain0_hashes:
|
||||||
|
for filter_type in FILTER_TYPES:
|
||||||
|
result = self.nodes[0].getblockfilter(block_hash, filter_type)
|
||||||
|
assert_is_hex_string(result['filter'])
|
||||||
|
|
||||||
|
# Test getblockfilter with unknown block
|
||||||
|
bad_block_hash = "0123456789abcdef" * 4
|
||||||
|
assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getblockfilter, bad_block_hash, "basic")
|
||||||
|
|
||||||
|
# Test getblockfilter with undefined filter type
|
||||||
|
genesis_hash = self.nodes[0].getblockhash(0)
|
||||||
|
assert_raises_rpc_error(-5, "Unknown filtertype", self.nodes[0].getblockfilter, genesis_hash, "unknown")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
GetBlockFilterTest().main()
|
|
@ -144,6 +144,7 @@ BASE_SCRIPTS = [
|
||||||
'wallet_txn_doublespend.py',
|
'wallet_txn_doublespend.py',
|
||||||
'wallet_txn_clone.py --mineblock',
|
'wallet_txn_clone.py --mineblock',
|
||||||
'feature_notifications.py',
|
'feature_notifications.py',
|
||||||
|
'rpc_getblockfilter.py',
|
||||||
'rpc_invalidateblock.py',
|
'rpc_invalidateblock.py',
|
||||||
'feature_rbf.py',
|
'feature_rbf.py',
|
||||||
'mempool_packages.py',
|
'mempool_packages.py',
|
||||||
|
|
Loading…
Reference in a new issue