Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Index for BIP 157 block filters #14121

Merged
merged 12 commits into from Apr 18, 2019

rpc: Add getblockfilter RPC method.

Retrieves and returns block filter and header from index.
  • Loading branch information...
jimpo committed Aug 31, 2018
commit 19308c9e21d98a7818625218b22b37f23f87816f
@@ -7,13 +7,15 @@

#include <amount.h>
#include <base58.h>
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
#include <checkpoints.h>
#include <coins.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <hash.h>
#include <index/blockfilterindex.h>
#include <index/txindex.h>
#include <key_io.h>
#include <policy/feerate.h>
@@ -2296,6 +2298,85 @@ UniValue scantxoutset(const JSONRPCRequest& request)
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
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -2323,6 +2404,7 @@ static const CRPCCommand commands[] =

{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
{ "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} },
{ "blockchain", "getblockfilter", &getblockfilter, {"blockhash", "filtertype"} },

/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, {"blockhash"} },
@@ -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 @@
'wallet_txn_doublespend.py',
'wallet_txn_clone.py --mineblock',
'feature_notifications.py',
'rpc_getblockfilter.py',
'rpc_invalidateblock.py',
'feature_rbf.py',
'mempool_packages.py',
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.