Skip to content

Commit c6fd923

Browse files
committed
Merge #9331: [qa] Add test for rescan feature of wallet key import RPCs
d8c0b9f [qa] Add test for rescan feature of wallet key import RPCs (Russell Yanofsky)
2 parents 1eef038 + d8c0b9f commit c6fd923

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

qa/pull-tester/rpc-tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@
150150
'importprunedfunds.py',
151151
'signmessages.py',
152152
'nulldummy.py',
153+
'import-rescan.py',
153154
]
154155
if ENABLE_ZMQ:
155156
testScripts.append('zmq_test.py')

qa/rpc-tests/import-rescan.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2014-2016 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+
from test_framework.test_framework import BitcoinTestFramework
7+
from test_framework.util import (start_nodes, connect_nodes, sync_blocks, assert_equal)
8+
from decimal import Decimal
9+
10+
import collections
11+
import enum
12+
import itertools
13+
import functools
14+
15+
Call = enum.Enum("Call", "single multi")
16+
Data = enum.Enum("Data", "address pub priv")
17+
ImportNode = collections.namedtuple("ImportNode", "rescan")
18+
19+
20+
def call_import_rpc(call, data, address, scriptPubKey, pubkey, key, label, node, rescan):
21+
"""Helper that calls a wallet import RPC on a bitcoin node."""
22+
watchonly = data != Data.priv
23+
if call == Call.single:
24+
if data == Data.address:
25+
response = node.importaddress(address, label, rescan)
26+
elif data == Data.pub:
27+
response = node.importpubkey(pubkey, label, rescan)
28+
elif data == Data.priv:
29+
response = node.importprivkey(key, label, rescan)
30+
assert_equal(response, None)
31+
elif call == Call.multi:
32+
response = node.importmulti([{
33+
"scriptPubKey": {
34+
"address": address
35+
},
36+
"pubkeys": [pubkey] if data == Data.pub else [],
37+
"keys": [key] if data == Data.priv else [],
38+
"label": label,
39+
"watchonly": watchonly
40+
}], {"rescan": rescan})
41+
assert_equal(response, [{"success": True}])
42+
return watchonly
43+
44+
45+
# List of RPCs that import a wallet key or address in various ways.
46+
IMPORT_RPCS = [functools.partial(call_import_rpc, call, data) for call, data in itertools.product(Call, Data)]
47+
48+
# List of bitcoind nodes that will import keys.
49+
IMPORT_NODES = [
50+
ImportNode(rescan=True),
51+
ImportNode(rescan=False),
52+
]
53+
54+
55+
class ImportRescanTest(BitcoinTestFramework):
56+
def __init__(self):
57+
super().__init__()
58+
self.num_nodes = 1 + len(IMPORT_NODES)
59+
60+
def setup_network(self):
61+
extra_args = [["-debug=1"] for _ in range(self.num_nodes)]
62+
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
63+
for i in range(1, self.num_nodes):
64+
connect_nodes(self.nodes[i], 0)
65+
66+
def run_test(self):
67+
# Create one transaction on node 0 with a unique amount and label for
68+
# each possible type of wallet import RPC.
69+
import_rpc_variants = []
70+
for i, import_rpc in enumerate(IMPORT_RPCS):
71+
label = "label{}".format(i)
72+
addr = self.nodes[0].validateaddress(self.nodes[0].getnewaddress(label))
73+
key = self.nodes[0].dumpprivkey(addr["address"])
74+
amount = 24.9375 - i * .0625
75+
txid = self.nodes[0].sendtoaddress(addr["address"], amount)
76+
import_rpc = functools.partial(import_rpc, addr["address"], addr["scriptPubKey"], addr["pubkey"], key,
77+
label)
78+
import_rpc_variants.append((import_rpc, label, amount, txid, addr))
79+
80+
self.nodes[0].generate(1)
81+
assert_equal(self.nodes[0].getrawmempool(), [])
82+
sync_blocks(self.nodes)
83+
84+
# For each importing node and variation of wallet import RPC, invoke
85+
# the RPC and check the results from getbalance and listtransactions.
86+
for node, import_node in zip(self.nodes[1:], IMPORT_NODES):
87+
for import_rpc, label, amount, txid, addr in import_rpc_variants:
88+
watchonly = import_rpc(node, import_node.rescan)
89+
90+
balance = node.getbalance(label, 0, True)
91+
if import_node.rescan:
92+
assert_equal(balance, amount)
93+
else:
94+
assert_equal(balance, 0)
95+
96+
txs = node.listtransactions(label, 10000, 0, True)
97+
if import_node.rescan:
98+
assert_equal(len(txs), 1)
99+
assert_equal(txs[0]["account"], label)
100+
assert_equal(txs[0]["address"], addr["address"])
101+
assert_equal(txs[0]["amount"], amount)
102+
assert_equal(txs[0]["category"], "receive")
103+
assert_equal(txs[0]["label"], label)
104+
assert_equal(txs[0]["txid"], txid)
105+
assert_equal(txs[0]["confirmations"], 1)
106+
assert_equal("trusted" not in txs[0], True)
107+
if watchonly:
108+
assert_equal(txs[0]["involvesWatchonly"], True)
109+
else:
110+
assert_equal("involvesWatchonly" not in txs[0], True)
111+
else:
112+
assert_equal(len(txs), 0)
113+
114+
# Create spends for all the imported addresses.
115+
spend_txids = []
116+
fee = self.nodes[0].getnetworkinfo()["relayfee"]
117+
for import_rpc, label, amount, txid, addr in import_rpc_variants:
118+
raw_tx = self.nodes[0].getrawtransaction(txid)
119+
decoded_tx = self.nodes[0].decoderawtransaction(raw_tx)
120+
input_vout = next(out["n"] for out in decoded_tx["vout"]
121+
if out["scriptPubKey"]["addresses"] == [addr["address"]])
122+
inputs = [{"txid": txid, "vout": input_vout}]
123+
outputs = {self.nodes[0].getnewaddress(): Decimal(amount) - fee}
124+
raw_spend_tx = self.nodes[0].createrawtransaction(inputs, outputs)
125+
signed_spend_tx = self.nodes[0].signrawtransaction(raw_spend_tx)
126+
spend_txid = self.nodes[0].sendrawtransaction(signed_spend_tx["hex"])
127+
spend_txids.append(spend_txid)
128+
129+
self.nodes[0].generate(1)
130+
assert_equal(self.nodes[0].getrawmempool(), [])
131+
sync_blocks(self.nodes)
132+
133+
# Check the results from getbalance and listtransactions after the spends.
134+
for node, import_node in zip(self.nodes[1:], IMPORT_NODES):
135+
txs = node.listtransactions("*", 10000, 0, True)
136+
for (import_rpc, label, amount, txid, addr), spend_txid in zip(import_rpc_variants, spend_txids):
137+
balance = node.getbalance(label, 0, True)
138+
spend_tx = [tx for tx in txs if tx["txid"] == spend_txid]
139+
if import_node.rescan:
140+
assert_equal(balance, amount)
141+
assert_equal(len(spend_tx), 1)
142+
assert_equal(spend_tx[0]["account"], "")
143+
assert_equal(spend_tx[0]["amount"] + spend_tx[0]["fee"], -amount)
144+
assert_equal(spend_tx[0]["category"], "send")
145+
assert_equal("label" not in spend_tx[0], True)
146+
assert_equal(spend_tx[0]["confirmations"], 1)
147+
assert_equal("trusted" not in spend_tx[0], True)
148+
assert_equal("involvesWatchonly" not in txs[0], True)
149+
else:
150+
assert_equal(balance, 0)
151+
assert_equal(spend_tx, [])
152+
153+
154+
if __name__ == "__main__":
155+
ImportRescanTest().main()

0 commit comments

Comments
 (0)