Skip to content

Commit 71792e7

Browse files
committed
rpc: add getsilentpaymentblockdata
1 parent 89b8474 commit 71792e7

File tree

5 files changed

+158
-0
lines changed

5 files changed

+158
-0
lines changed

src/index/bip352.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ static constexpr bool DEFAULT_BIP352_CT_INDEX{false};
2424
*/
2525
class BIP352Index final : public BaseIndex
2626
{
27+
public:
28+
/**
29+
* For each block hash we store and array of transactions. For each
30+
* transaction we store:
31+
* - the tweaked pubkey sum
32+
*/
33+
using tweak_index_entry = std::vector<CPubKey>;
34+
2735
protected:
2836
class DB;
2937

src/rpc/blockchain.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <deploymentstatus.h>
2121
#include <flatfile.h>
2222
#include <hash.h>
23+
#include <index/bip352.h>
2324
#include <index/blockfilterindex.h>
2425
#include <index/coinstatsindex.h>
2526
#include <interfaces/mining.h>
@@ -723,6 +724,62 @@ const RPCResult getblock_vin{
723724
}
724725
};
725726

727+
static RPCHelpMan getsilentpaymentblockdata()
728+
{
729+
return RPCHelpMan{"getsilentpaymentblockdata",
730+
"Returns an array of hex-encoded strings data for the tweaked public key sum of candidate silent transaction inputs in each transaction.\n",
731+
{
732+
{"block_hash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
733+
},
734+
{
735+
RPCResult{
736+
RPCResult::Type::OBJ, "", "",
737+
{
738+
{RPCResult::Type::ARR, "bip352_tweaks", "The tweaked public key for each transaction that could be a silent payment",
739+
{{RPCResult::Type::STR_HEX, "tweak", "Hex-encoded data for the public key sum of candidate silent transaction inputs in the transaction"}}
740+
}
741+
}},
742+
},
743+
RPCExamples{
744+
HelpExampleCli("getsilentpaymentblockdata", "\"00000000000000000002cbdf64ae445b53b545ba1e960f9e83787130d1530484\"")
745+
+ HelpExampleRpc("getsilentpaymentblockdata", "\"00000000000000000002cbdf64ae445b53b545ba1e960f9e83787130d1530484\"")
746+
},
747+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
748+
{
749+
// TODO: add cut-through argument, check which index to use here
750+
if (!g_bip352_index) {
751+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Requires bip352index");
752+
}
753+
754+
uint256 block_hash(ParseHashV(request.params[0], "block_hash"));
755+
{
756+
ChainstateManager& chainman = EnsureAnyChainman(request.context);
757+
const CBlockIndex* block_index;
758+
LOCK(cs_main);
759+
block_index = chainman.m_blockman.LookupBlockIndex(block_hash);
760+
if (!block_index) {
761+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
762+
}
763+
}
764+
765+
BIP352Index::tweak_index_entry tweak_index_entry;
766+
if (!g_bip352_index->FindSilentPayment(block_hash, tweak_index_entry)) {
767+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block has not been indexed yet");
768+
}
769+
770+
UniValue ret(UniValue::VOBJ);
771+
UniValue tweaks_res(UniValue::VARR);
772+
773+
for (const auto& entry : tweak_index_entry) {
774+
tweaks_res.push_back(HexStr(entry));
775+
}
776+
777+
ret.pushKV("bip352_tweaks", tweaks_res);
778+
return ret;
779+
},
780+
};
781+
}
782+
726783
static RPCHelpMan getblock()
727784
{
728785
return RPCHelpMan{
@@ -3433,6 +3490,7 @@ void RegisterBlockchainRPCCommands(CRPCTable& t)
34333490
{"blockchain", &getblockfrompeer},
34343491
{"blockchain", &getblockhash},
34353492
{"blockchain", &getblockheader},
3493+
{"blockchain", &getsilentpaymentblockdata},
34363494
{"blockchain", &getchaintips},
34373495
{"blockchain", &getdifficulty},
34383496
{"blockchain", &getdeploymentinfo},

src/test/fuzz/rpc.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
185185
"waitforblock",
186186
"waitforblockheight",
187187
"waitfornewblock",
188+
"getsilentpaymentblockdata"
188189
};
189190

190191
std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2023-present The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#
7+
# Test getsilentpaymentblockdata rpc call
8+
#
9+
10+
from test_framework.test_framework import BitcoinTestFramework
11+
from test_framework.util import (
12+
assert_equal,
13+
)
14+
from test_framework.blocktools import (
15+
TIME_GENESIS_BLOCK,
16+
)
17+
from test_framework.descriptors import (
18+
descsum_create,
19+
)
20+
21+
class GetsilentpaymentblockdataTest(BitcoinTestFramework):
22+
def skip_test_if_missing_module(self):
23+
self.skip_if_no_wallet()
24+
25+
def set_test_params(self):
26+
self.num_nodes = 1
27+
28+
self.extra_args = [[
29+
'-bip352index',
30+
]]
31+
32+
self.setup_clean_chain = True
33+
34+
def run_test(self):
35+
node = self.nodes[0]
36+
mocktime = 1702294115
37+
node.setmocktime(mocktime)
38+
node.createwallet(wallet_name="w", blank=True)
39+
wallet = self.nodes[0].get_wallet_rpc("w")
40+
res = wallet.importdescriptors([{
41+
'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg/84h/1h/0h/0/*)'),
42+
'timestamp': TIME_GENESIS_BLOCK,
43+
'active': True,
44+
},
45+
{
46+
'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg/84h/1h/0h/1/*)'),
47+
'timestamp': TIME_GENESIS_BLOCK,
48+
'active': True,
49+
'internal': True,
50+
},
51+
{
52+
'desc': descsum_create('tr(tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg/86h/1h/0h/0/*)'),
53+
'timestamp': TIME_GENESIS_BLOCK,
54+
'active': True,
55+
},
56+
{
57+
'desc': descsum_create('tr(tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg/86h/1h/0h/1/*)'),
58+
'timestamp': TIME_GENESIS_BLOCK,
59+
'active': True,
60+
'internal': True,
61+
}
62+
])
63+
assert all([r["success"] for r in res])
64+
self.log.info("Mine fresh coins to a taproot addresses")
65+
mine_tr = wallet.getnewaddress(address_type="bech32m")
66+
self.generatetoaddress(node, 1, mine_tr)
67+
self.generate(node, 100)
68+
69+
self.log.info("Blocks with only a coinbase won't have any silent payment data")
70+
silent_data = node.getsilentpaymentblockdata(node.getbestblockhash())
71+
assert_equal(silent_data['bip352_tweaks'], [])
72+
73+
self.log.info("Spending from taproot to segwit won't result in silent payment data")
74+
dest_sw = wallet.getnewaddress(address_type="bech32")
75+
txid = wallet.send(outputs={dest_sw: 1}, options={'change_position': 1})['txid']
76+
assert_equal(txid, '4d9bc8c3b77922767b06750bd9ea83865e19e6f9ccfd8d2cc048ca7a37a32ece')
77+
self.generate(node, 1)
78+
79+
silent_data = node.getsilentpaymentblockdata(node.getbestblockhash())
80+
assert_equal(silent_data['bip352_tweaks'], [])
81+
82+
self.log.info("Spending (from taproot) to taproot results in silent payment data")
83+
dest_tr = wallet.getnewaddress(address_type="bech32m")
84+
wallet.send(outputs={dest_tr: 1}, options={'inputs': [{'txid': txid, 'vout': 1}], 'add_inputs': False, 'change_position': 1})
85+
self.generate(node, 1)
86+
silent_data = node.getsilentpaymentblockdata(node.getbestblockhash())
87+
assert_equal(silent_data['bip352_tweaks'], ['03d05270be66782cfaa3114848c92a194b8eebb74824ffe7bcd44f5d6051e9c11f'])
88+
89+
if __name__ == '__main__':
90+
GetsilentpaymentblockdataTest(__file__).main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@
343343
'wallet_orphanedreward.py',
344344
'wallet_timelock.py',
345345
'p2p_permissions.py',
346+
'rpc_getsilentpaymentblockdata.py',
346347
'feature_blocksdir.py',
347348
'wallet_startup.py',
348349
'feature_remove_pruned_files_on_startup.py',

0 commit comments

Comments
 (0)