Skip to content

Commit cb08fdb

Browse files
author
Pedro Branco
committed
Add importmulti rpc call
1 parent 97c7f73 commit cb08fdb

File tree

7 files changed

+698
-0
lines changed

7 files changed

+698
-0
lines changed

qa/pull-tester/rpc-tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
'signmessages.py',
146146
'p2p-compactblocks.py',
147147
'nulldummy.py',
148+
'importmulti.py',
148149
]
149150
if ENABLE_ZMQ:
150151
testScripts.append('zmq_test.py')

qa/rpc-tests/importmulti.py

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
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 *
8+
9+
class ImportMultiTest (BitcoinTestFramework):
10+
def __init__(self):
11+
super().__init__()
12+
self.num_nodes = 2
13+
self.setup_clean_chain = True
14+
15+
def setup_network(self, split=False):
16+
self.nodes = start_nodes(2, self.options.tmpdir)
17+
self.is_network_split=False
18+
19+
def run_test (self):
20+
print ("Mining blocks...")
21+
self.nodes[0].generate(1)
22+
self.nodes[1].generate(1)
23+
24+
# keyword definition
25+
PRIV_KEY = 'privkey'
26+
PUB_KEY = 'pubkey'
27+
ADDRESS_KEY = 'address'
28+
SCRIPT_KEY = 'script'
29+
30+
31+
node0_address1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
32+
node0_address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
33+
node0_address3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
34+
35+
#Check only one address
36+
assert_equal(node0_address1['ismine'], True)
37+
38+
#Node 1 sync test
39+
assert_equal(self.nodes[1].getblockcount(),1)
40+
41+
#Address Test - before import
42+
address_info = self.nodes[1].validateaddress(node0_address1['address'])
43+
assert_equal(address_info['iswatchonly'], False)
44+
assert_equal(address_info['ismine'], False)
45+
46+
47+
# RPC importmulti -----------------------------------------------
48+
49+
# Bitcoin Address
50+
print("Should import an address")
51+
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
52+
result = self.nodes[1].importmulti([{
53+
"scriptPubKey": {
54+
"address": address['address']
55+
}
56+
}])
57+
assert_equal(result[0]['success'], True)
58+
address_assert = self.nodes[1].validateaddress(address['address'])
59+
assert_equal(address_assert['iswatchonly'], True)
60+
assert_equal(address_assert['ismine'], False)
61+
62+
63+
# ScriptPubKey + internal
64+
print("Should import a scriptPubKey with internal flag")
65+
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
66+
result = self.nodes[1].importmulti([{
67+
"scriptPubKey": address['scriptPubKey'],
68+
"internal": True
69+
}])
70+
assert_equal(result[0]['success'], True)
71+
address_assert = self.nodes[1].validateaddress(address['address'])
72+
assert_equal(address_assert['iswatchonly'], True)
73+
assert_equal(address_assert['ismine'], False)
74+
75+
# ScriptPubKey + !internal
76+
print("Should not import a scriptPubKey without internal flag")
77+
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
78+
result = self.nodes[1].importmulti([{
79+
"scriptPubKey": address['scriptPubKey']
80+
}])
81+
assert_equal(result[0]['success'], False)
82+
assert_equal(result[0]['error']['code'], -8)
83+
assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey')
84+
address_assert = self.nodes[1].validateaddress(address['address'])
85+
assert_equal(address_assert['iswatchonly'], False)
86+
assert_equal(address_assert['ismine'], False)
87+
88+
89+
# Address + Public key + !Internal
90+
print("Should import an address with public key")
91+
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
92+
result = self.nodes[1].importmulti([{
93+
"scriptPubKey": {
94+
"address": address['address']
95+
},
96+
"pubkeys": [ address['pubkey'] ]
97+
}])
98+
assert_equal(result[0]['success'], True)
99+
address_assert = self.nodes[1].validateaddress(address['address'])
100+
assert_equal(address_assert['iswatchonly'], True)
101+
assert_equal(address_assert['ismine'], False)
102+
103+
104+
# ScriptPubKey + Public key + internal
105+
print("Should import a scriptPubKey with internal and with public key")
106+
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
107+
request = [{
108+
"scriptPubKey": address['scriptPubKey'],
109+
"pubkeys": [ address['pubkey'] ],
110+
"internal": True
111+
}];
112+
result = self.nodes[1].importmulti(request)
113+
assert_equal(result[0]['success'], True)
114+
address_assert = self.nodes[1].validateaddress(address['address'])
115+
assert_equal(address_assert['iswatchonly'], True)
116+
assert_equal(address_assert['ismine'], False)
117+
118+
# ScriptPubKey + Public key + !internal
119+
print("Should not import a scriptPubKey without internal and with public key")
120+
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
121+
request = [{
122+
"scriptPubKey": address['scriptPubKey'],
123+
"pubkeys": [ address['pubkey'] ]
124+
}];
125+
result = self.nodes[1].importmulti(request)
126+
assert_equal(result[0]['success'], False)
127+
assert_equal(result[0]['error']['code'], -8)
128+
assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey')
129+
address_assert = self.nodes[1].validateaddress(address['address'])
130+
assert_equal(address_assert['iswatchonly'], False)
131+
assert_equal(address_assert['ismine'], False)
132+
133+
# Address + Private key + !watchonly
134+
print("Should import an address with private key")
135+
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
136+
result = self.nodes[1].importmulti([{
137+
"scriptPubKey": {
138+
"address": address['address']
139+
},
140+
"keys": [ self.nodes[0].dumpprivkey(address['address']) ]
141+
}])
142+
assert_equal(result[0]['success'], True)
143+
address_assert = self.nodes[1].validateaddress(address['address'])
144+
assert_equal(address_assert['iswatchonly'], False)
145+
assert_equal(address_assert['ismine'], True)
146+
147+
# Address + Private key + watchonly
148+
print("Should not import an address with private key and with watchonly")
149+
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
150+
result = self.nodes[1].importmulti([{
151+
"scriptPubKey": {
152+
"address": address['address']
153+
},
154+
"keys": [ self.nodes[0].dumpprivkey(address['address']) ],
155+
"watchonly": True
156+
}])
157+
assert_equal(result[0]['success'], False)
158+
assert_equal(result[0]['error']['code'], -8)
159+
assert_equal(result[0]['error']['message'], 'Incompatibility found between watchonly and keys')
160+
address_assert = self.nodes[1].validateaddress(address['address'])
161+
assert_equal(address_assert['iswatchonly'], False)
162+
assert_equal(address_assert['ismine'], False)
163+
164+
# ScriptPubKey + Private key + internal
165+
print("Should import a scriptPubKey with internal and with private key")
166+
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
167+
result = self.nodes[1].importmulti([{
168+
"scriptPubKey": address['scriptPubKey'],
169+
"keys": [ self.nodes[0].dumpprivkey(address['address']) ],
170+
"internal": True
171+
}])
172+
assert_equal(result[0]['success'], True)
173+
address_assert = self.nodes[1].validateaddress(address['address'])
174+
assert_equal(address_assert['iswatchonly'], False)
175+
assert_equal(address_assert['ismine'], True)
176+
177+
# ScriptPubKey + Private key + !internal
178+
print("Should not import a scriptPubKey without internal and with private key")
179+
address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
180+
result = self.nodes[1].importmulti([{
181+
"scriptPubKey": address['scriptPubKey'],
182+
"keys": [ self.nodes[0].dumpprivkey(address['address']) ]
183+
}])
184+
assert_equal(result[0]['success'], False)
185+
assert_equal(result[0]['error']['code'], -8)
186+
assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey')
187+
address_assert = self.nodes[1].validateaddress(address['address'])
188+
assert_equal(address_assert['iswatchonly'], False)
189+
assert_equal(address_assert['ismine'], False)
190+
191+
192+
# P2SH address
193+
sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
194+
sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
195+
sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
196+
multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']])
197+
self.nodes[1].generate(100)
198+
transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
199+
self.nodes[1].generate(1)
200+
transaction = self.nodes[1].gettransaction(transactionid);
201+
202+
print("Should import a p2sh")
203+
result = self.nodes[1].importmulti([{
204+
"scriptPubKey": {
205+
"address": multi_sig_script['address']
206+
}
207+
}])
208+
assert_equal(result[0]['success'], True)
209+
address_assert = self.nodes[1].validateaddress(multi_sig_script['address'])
210+
assert_equal(address_assert['isscript'], True)
211+
assert_equal(address_assert['iswatchonly'], True)
212+
p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
213+
assert_equal(p2shunspent['spendable'], False)
214+
assert_equal(p2shunspent['solvable'], False)
215+
216+
217+
# P2SH + Redeem script
218+
sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
219+
sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
220+
sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
221+
multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']])
222+
self.nodes[1].generate(100)
223+
transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
224+
self.nodes[1].generate(1)
225+
transaction = self.nodes[1].gettransaction(transactionid);
226+
227+
print("Should import a p2sh with respective redeem script")
228+
result = self.nodes[1].importmulti([{
229+
"scriptPubKey": {
230+
"address": multi_sig_script['address']
231+
},
232+
"redeemscript": multi_sig_script['redeemScript']
233+
}])
234+
assert_equal(result[0]['success'], True)
235+
236+
p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
237+
assert_equal(p2shunspent['spendable'], False)
238+
assert_equal(p2shunspent['solvable'], True)
239+
240+
241+
# P2SH + Redeem script + Private Keys + !Watchonly
242+
sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
243+
sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
244+
sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
245+
multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']])
246+
self.nodes[1].generate(100)
247+
transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
248+
self.nodes[1].generate(1)
249+
transaction = self.nodes[1].gettransaction(transactionid);
250+
251+
print("Should import a p2sh with respective redeem script and private keys")
252+
result = self.nodes[1].importmulti([{
253+
"scriptPubKey": {
254+
"address": multi_sig_script['address']
255+
},
256+
"redeemscript": multi_sig_script['redeemScript'],
257+
"keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])]
258+
}])
259+
assert_equal(result[0]['success'], True)
260+
261+
p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0]
262+
assert_equal(p2shunspent['spendable'], False)
263+
assert_equal(p2shunspent['solvable'], True)
264+
265+
# P2SH + Redeem script + Private Keys + Watchonly
266+
sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
267+
sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
268+
sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress())
269+
multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']])
270+
self.nodes[1].generate(100)
271+
transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00)
272+
self.nodes[1].generate(1)
273+
transaction = self.nodes[1].gettransaction(transactionid);
274+
275+
print("Should import a p2sh with respective redeem script and private keys")
276+
result = self.nodes[1].importmulti([{
277+
"scriptPubKey": {
278+
"address": multi_sig_script['address']
279+
},
280+
"redeemscript": multi_sig_script['redeemScript'],
281+
"keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])],
282+
"watchonly": True
283+
}])
284+
assert_equal(result[0]['success'], False)
285+
assert_equal(result[0]['error']['code'], -8)
286+
assert_equal(result[0]['error']['message'], 'Incompatibility found between watchonly and keys')
287+
288+
# TODO Consistency tests?
289+
290+
291+
292+
if __name__ == '__main__':
293+
ImportMultiTest ().main ()

src/chain.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
6161
return pindex;
6262
}
6363

64+
CBlockIndex* CChain::FindLatestBefore(int64_t nTime) const
65+
{
66+
std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime,
67+
[](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTime() < time; });
68+
return (lower == vChain.end() ? NULL : *lower);
69+
}
70+
6471
/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */
6572
int static inline InvertLowestOne(int n) { return n & (n - 1); }
6673

src/chain.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,9 @@ class CChain {
459459

460460
/** Find the last common block between this chain and a block index entry. */
461461
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
462+
463+
/** Find the most recent block with timestamp lower than the given. */
464+
CBlockIndex* FindLatestBefore(int64_t nTime) const;
462465
};
463466

464467
#endif // BITCOIN_CHAIN_H

src/rpc/client.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
9595
{ "importaddress", 2 },
9696
{ "importaddress", 3 },
9797
{ "importpubkey", 2 },
98+
{ "importmulti", 0 },
99+
{ "importmulti", 1 },
98100
{ "verifychain", 0 },
99101
{ "verifychain", 1 },
100102
{ "keypoolrefill", 0 },

0 commit comments

Comments
 (0)