Skip to content

Commit b35a591

Browse files
committed
Merge #7558: [RPC] Add import/removeprunedfunds rpc call
f1bb13c Added companion removeprunedfunds call. (instagibbs) 7eb7029 Add importprunedfunds rpc call (instagibbs)
2 parents 2676e12 + f1bb13c commit b35a591

File tree

13 files changed

+351
-23
lines changed

13 files changed

+351
-23
lines changed

qa/pull-tester/rpc-tests.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
'invalidtxrequest.py',
117117
'abandonconflict.py',
118118
'p2p-versionbits-warning.py',
119+
'importprunedfunds.py',
119120
]
120121
testScriptsExt = [
121122
'bip65-cltv.py',
@@ -127,6 +128,7 @@
127128
'getblocktemplate_proposals.py',
128129
'txn_doublespend.py',
129130
'txn_clone.py --mineblock',
131+
'pruning.py',
130132
'forknotify.py',
131133
'invalidateblock.py',
132134
# 'rpcbind_test.py', #temporary, bug in libevent, see #6655

qa/rpc-tests/importprunedfunds.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#!/usr/bin/env python2
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+
import decimal
9+
10+
class ImportPrunedFundsTest(BitcoinTestFramework):
11+
12+
def setup_chain(self):
13+
print("Initializing test directory "+self.options.tmpdir)
14+
initialize_chain_clean(self.options.tmpdir, 4)
15+
16+
def setup_network(self, split=False):
17+
self.nodes = start_nodes(2, self.options.tmpdir)
18+
connect_nodes_bi(self.nodes,0,1)
19+
self.is_network_split=False
20+
self.sync_all()
21+
22+
def run_test (self):
23+
import time
24+
begintime = int(time.time())
25+
26+
print "Mining blocks..."
27+
self.nodes[0].generate(101)
28+
29+
# sync
30+
self.sync_all()
31+
32+
# address
33+
address1 = self.nodes[0].getnewaddress()
34+
# pubkey
35+
address2 = self.nodes[0].getnewaddress()
36+
address2_pubkey = self.nodes[0].validateaddress(address2)['pubkey'] # Using pubkey
37+
# privkey
38+
address3 = self.nodes[0].getnewaddress()
39+
address3_privkey = self.nodes[0].dumpprivkey(address3) # Using privkey
40+
41+
#Check only one address
42+
address_info = self.nodes[0].validateaddress(address1)
43+
assert_equal(address_info['ismine'], True)
44+
45+
self.sync_all()
46+
47+
#Node 1 sync test
48+
assert_equal(self.nodes[1].getblockcount(),101)
49+
50+
#Address Test - before import
51+
address_info = self.nodes[1].validateaddress(address1)
52+
assert_equal(address_info['iswatchonly'], False)
53+
assert_equal(address_info['ismine'], False)
54+
55+
address_info = self.nodes[1].validateaddress(address2)
56+
assert_equal(address_info['iswatchonly'], False)
57+
assert_equal(address_info['ismine'], False)
58+
59+
address_info = self.nodes[1].validateaddress(address3)
60+
assert_equal(address_info['iswatchonly'], False)
61+
assert_equal(address_info['ismine'], False)
62+
63+
#Send funds to self
64+
txnid1 = self.nodes[0].sendtoaddress(address1, 0.1)
65+
self.nodes[0].generate(1)
66+
rawtxn1 = self.nodes[0].gettransaction(txnid1)['hex']
67+
proof1 = self.nodes[0].gettxoutproof([txnid1])
68+
69+
txnid2 = self.nodes[0].sendtoaddress(address2, 0.05)
70+
self.nodes[0].generate(1)
71+
rawtxn2 = self.nodes[0].gettransaction(txnid2)['hex']
72+
proof2 = self.nodes[0].gettxoutproof([txnid2])
73+
74+
75+
txnid3 = self.nodes[0].sendtoaddress(address3, 0.025)
76+
self.nodes[0].generate(1)
77+
rawtxn3 = self.nodes[0].gettransaction(txnid3)['hex']
78+
proof3 = self.nodes[0].gettxoutproof([txnid3])
79+
80+
self.sync_all()
81+
82+
#Import with no affiliated address
83+
try:
84+
result1 = self.nodes[1].importprunedfunds(rawtxn1, proof1, "")
85+
except JSONRPCException,e:
86+
errorString = e.error['message']
87+
88+
assert('No addresses' in errorString)
89+
90+
balance1 = self.nodes[1].getbalance("", 0, True)
91+
assert_equal(balance1, Decimal(0))
92+
93+
#Import with affiliated address with no rescan
94+
self.nodes[1].importaddress(address2, "", False)
95+
result2 = self.nodes[1].importprunedfunds(rawtxn2, proof2, "")
96+
balance2 = Decimal(self.nodes[1].getbalance("", 0, True))
97+
assert_equal(balance2, Decimal('0.05'))
98+
99+
#Import with private key with no rescan
100+
self.nodes[1].importprivkey(address3_privkey, "", False)
101+
result3 = self.nodes[1].importprunedfunds(rawtxn3, proof3, "")
102+
balance3 = Decimal(self.nodes[1].getbalance("", 0, False))
103+
assert_equal(balance3, Decimal('0.025'))
104+
balance3 = Decimal(self.nodes[1].getbalance("", 0, True))
105+
assert_equal(balance3, Decimal('0.075'))
106+
107+
#Addresses Test - after import
108+
address_info = self.nodes[1].validateaddress(address1)
109+
assert_equal(address_info['iswatchonly'], False)
110+
assert_equal(address_info['ismine'], False)
111+
address_info = self.nodes[1].validateaddress(address2)
112+
assert_equal(address_info['iswatchonly'], True)
113+
assert_equal(address_info['ismine'], False)
114+
address_info = self.nodes[1].validateaddress(address3)
115+
assert_equal(address_info['iswatchonly'], False)
116+
assert_equal(address_info['ismine'], True)
117+
118+
#Remove transactions
119+
120+
try:
121+
self.nodes[1].removeprunedfunds(txnid1)
122+
except JSONRPCException,e:
123+
errorString = e.error['message']
124+
125+
assert('does not exist' in errorString)
126+
127+
balance1 = Decimal(self.nodes[1].getbalance("", 0, True))
128+
assert_equal(balance1, Decimal('0.075'))
129+
130+
131+
self.nodes[1].removeprunedfunds(txnid2)
132+
balance2 = Decimal(self.nodes[1].getbalance("", 0, True))
133+
assert_equal(balance2, Decimal('0.025'))
134+
135+
self.nodes[1].removeprunedfunds(txnid3)
136+
balance3 = Decimal(self.nodes[1].getbalance("", 0, True))
137+
assert_equal(balance3, Decimal('0.0'))
138+
139+
if __name__ == '__main__':
140+
ImportPrunedFundsTest ().main ()

src/merkleblock.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const st
9595
}
9696
}
9797

98-
uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch) {
98+
uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex) {
9999
if (nBitsUsed >= vBits.size()) {
100100
// overflowed the bits array - failure
101101
fBad = true;
@@ -110,14 +110,16 @@ uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, uns
110110
return uint256();
111111
}
112112
const uint256 &hash = vHash[nHashUsed++];
113-
if (height==0 && fParentOfMatch) // in case of height 0, we have a matched txid
113+
if (height==0 && fParentOfMatch) { // in case of height 0, we have a matched txid
114114
vMatch.push_back(hash);
115+
vnIndex.push_back(pos);
116+
}
115117
return hash;
116118
} else {
117119
// otherwise, descend into the subtrees to extract matched txids and hashes
118-
uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch), right;
120+
uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch, vnIndex), right;
119121
if (pos*2+1 < CalcTreeWidth(height-1)) {
120-
right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch);
122+
right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch, vnIndex);
121123
if (right == left) {
122124
// The left and right branches should never be identical, as the transaction
123125
// hashes covered by them must each be unique.
@@ -147,7 +149,7 @@ CPartialMerkleTree::CPartialMerkleTree(const std::vector<uint256> &vTxid, const
147149

148150
CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {}
149151

150-
uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) {
152+
uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex) {
151153
vMatch.clear();
152154
// An empty set will not work
153155
if (nTransactions == 0)
@@ -167,7 +169,7 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) {
167169
nHeight++;
168170
// traverse the partial tree
169171
unsigned int nBitsUsed = 0, nHashUsed = 0;
170-
uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch);
172+
uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch, vnIndex);
171173
// verify that no problems occurred during the tree traversal
172174
if (fBad)
173175
return uint256();

src/merkleblock.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ class CPartialMerkleTree
7575

7676
/**
7777
* recursive function that traverses tree nodes, consuming the bits and hashes produced by TraverseAndBuild.
78-
* it returns the hash of the respective node.
78+
* it returns the hash of the respective node and its respective index.
7979
*/
80-
uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch);
80+
uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex);
8181

8282
public:
8383

@@ -110,10 +110,11 @@ class CPartialMerkleTree
110110
CPartialMerkleTree();
111111

112112
/**
113-
* extract the matching txid's represented by this partial merkle tree.
113+
* extract the matching txid's represented by this partial merkle tree
114+
* and their respective indices within the partial tree.
114115
* returns the merkle root, or 0 in case of failure
115116
*/
116-
uint256 ExtractMatches(std::vector<uint256> &vMatch);
117+
uint256 ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex);
117118
};
118119

119120

src/rpc/rawtransaction.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ UniValue verifytxoutproof(const UniValue& params, bool fHelp)
303303
UniValue res(UniValue::VARR);
304304

305305
vector<uint256> vMatch;
306-
if (merkleBlock.txn.ExtractMatches(vMatch) != merkleBlock.header.hashMerkleRoot)
306+
vector<unsigned int> vIndex;
307+
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot)
307308
return res;
308309

309310
LOCK(cs_main);

src/test/bloom_tests.cpp

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_1)
204204
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 8);
205205

206206
vector<uint256> vMatched;
207-
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
207+
vector<unsigned int> vIndex;
208+
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
208209
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
209210
for (unsigned int i = 0; i < vMatched.size(); i++)
210211
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
@@ -221,7 +222,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_1)
221222
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
222223
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 7);
223224

224-
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
225+
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
225226
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
226227
for (unsigned int i = 0; i < vMatched.size(); i++)
227228
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
@@ -249,7 +250,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_2)
249250
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
250251

251252
vector<uint256> vMatched;
252-
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
253+
vector<unsigned int> vIndex;
254+
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
253255
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
254256
for (unsigned int i = 0; i < vMatched.size(); i++)
255257
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
@@ -275,7 +277,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_2)
275277
BOOST_CHECK(merkleBlock.vMatchedTxn[3].second == uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
276278
BOOST_CHECK(merkleBlock.vMatchedTxn[3].first == 3);
277279

278-
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
280+
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
279281
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
280282
for (unsigned int i = 0; i < vMatched.size(); i++)
281283
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
@@ -303,7 +305,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none)
303305
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
304306

305307
vector<uint256> vMatched;
306-
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
308+
vector<unsigned int> vIndex;
309+
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
307310
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
308311
for (unsigned int i = 0; i < vMatched.size(); i++)
309312
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
@@ -326,7 +329,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none)
326329
BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
327330
BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 3);
328331

329-
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
332+
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
330333
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
331334
for (unsigned int i = 0; i < vMatched.size(); i++)
332335
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
@@ -353,7 +356,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_3_and_serialize)
353356
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
354357

355358
vector<uint256> vMatched;
356-
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
359+
vector<unsigned int> vIndex;
360+
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
357361
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
358362
for (unsigned int i = 0; i < vMatched.size(); i++)
359363
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
@@ -392,7 +396,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_4)
392396
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 6);
393397

394398
vector<uint256> vMatched;
395-
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
399+
vector<unsigned int> vIndex;
400+
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
396401
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
397402
for (unsigned int i = 0; i < vMatched.size(); i++)
398403
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
@@ -409,7 +414,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_4)
409414

410415
BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair);
411416

412-
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
417+
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
413418
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
414419
for (unsigned int i = 0; i < vMatched.size(); i++)
415420
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);

src/test/pmt_tests.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ BOOST_AUTO_TEST_CASE(pmt_test1)
8888

8989
// extract merkle root and matched txids from copy
9090
std::vector<uint256> vMatchTxid2;
91-
uint256 merkleRoot2 = pmt2.ExtractMatches(vMatchTxid2);
91+
std::vector<unsigned int> vIndex;
92+
uint256 merkleRoot2 = pmt2.ExtractMatches(vMatchTxid2, vIndex);
9293

9394
// check that it has the same merkle root as the original, and a valid one
9495
BOOST_CHECK(merkleRoot1 == merkleRoot2);
@@ -102,7 +103,7 @@ BOOST_AUTO_TEST_CASE(pmt_test1)
102103
CPartialMerkleTreeTester pmt3(pmt2);
103104
pmt3.Damage();
104105
std::vector<uint256> vMatchTxid3;
105-
uint256 merkleRoot3 = pmt3.ExtractMatches(vMatchTxid3);
106+
uint256 merkleRoot3 = pmt3.ExtractMatches(vMatchTxid3, vIndex);
106107
BOOST_CHECK(merkleRoot3 != merkleRoot1);
107108
}
108109
}
@@ -122,7 +123,8 @@ BOOST_AUTO_TEST_CASE(pmt_malleability)
122123

123124
CPartialMerkleTree tree(vTxid, vMatch);
124125
std::vector<uint256> vTxid2;
125-
BOOST_CHECK(tree.ExtractMatches(vTxid).IsNull());
126+
std::vector<unsigned int> vIndex;
127+
BOOST_CHECK(tree.ExtractMatches(vTxid, vIndex).IsNull());
126128
}
127129

128130
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)