1 // Copyright (c) 2012-2017 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 #include <wallet/wallet.h>
12 #include <consensus/validation.h>
13 #include <rpc/server.h>
14 #include <test/test_bitcoin.h>
15 #include <validation.h>
16 #include <wallet/coincontrol.h>
17 #include <wallet/test/wallet_test_fixture.h>
19 #include <boost/test/unit_test.hpp>
22 extern UniValue
importmulti(const JSONRPCRequest
& request
);
23 extern UniValue
dumpwallet(const JSONRPCRequest
& request
);
24 extern UniValue
importwallet(const JSONRPCRequest
& request
);
26 // how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles
29 // some tests fail 1% of the time due to bad luck.
30 // we repeat those tests this many times and only complain if all iterations of the test fail
31 #define RANDOM_REPEATS 5
33 std::vector
<std::unique_ptr
<CWalletTx
>> wtxn
;
35 typedef std::set
<CInputCoin
> CoinSet
;
37 BOOST_FIXTURE_TEST_SUITE(wallet_tests
, WalletTestingSetup
)
39 static const CWallet testWallet
;
40 static std::vector
<COutput
> vCoins
;
42 static void add_coin(const CAmount
& nValue
, int nAge
= 6*24, bool fIsFromMe
= false, int nInput
=0)
44 static int nextLockTime
= 0;
45 CMutableTransaction tx
;
46 tx
.nLockTime
= nextLockTime
++; // so all transactions get different hashes
47 tx
.vout
.resize(nInput
+1);
48 tx
.vout
[nInput
].nValue
= nValue
;
50 // IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(),
51 // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
54 std::unique_ptr
<CWalletTx
> wtx(new CWalletTx(&testWallet
, MakeTransactionRef(std::move(tx
))));
57 wtx
->fDebitCached
= true;
58 wtx
->nDebitCached
= 1;
60 COutput
output(wtx
.get(), nInput
, nAge
, true /* spendable */, true /* solvable */, true /* safe */);
61 vCoins
.push_back(output
);
62 wtxn
.emplace_back(std::move(wtx
));
65 static void empty_wallet(void)
71 static bool equal_sets(CoinSet a
, CoinSet b
)
73 std::pair
<CoinSet::iterator
, CoinSet::iterator
> ret
= mismatch(a
.begin(), a
.end(), b
.begin());
74 return ret
.first
== a
.end() && ret
.second
== b
.end();
77 BOOST_AUTO_TEST_CASE(coin_selection_tests
)
79 CoinSet setCoinsRet
, setCoinsRet2
;
82 LOCK(testWallet
.cs_wallet
);
84 // test multiple times to allow for differences in the shuffle order
85 for (int i
= 0; i
< RUN_TESTS
; i
++)
89 // with an empty wallet we can't even pay one cent
90 BOOST_CHECK(!testWallet
.SelectCoinsMinConf( 1 * CENT
, 1, 6, 0, vCoins
, setCoinsRet
, nValueRet
));
92 add_coin(1*CENT
, 4); // add a new 1 cent coin
94 // with a new 1 cent coin, we still can't find a mature 1 cent
95 BOOST_CHECK(!testWallet
.SelectCoinsMinConf( 1 * CENT
, 1, 6, 0, vCoins
, setCoinsRet
, nValueRet
));
97 // but we can find a new 1 cent
98 BOOST_CHECK( testWallet
.SelectCoinsMinConf( 1 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
99 BOOST_CHECK_EQUAL(nValueRet
, 1 * CENT
);
101 add_coin(2*CENT
); // add a mature 2 cent coin
103 // we can't make 3 cents of mature coins
104 BOOST_CHECK(!testWallet
.SelectCoinsMinConf( 3 * CENT
, 1, 6, 0, vCoins
, setCoinsRet
, nValueRet
));
106 // we can make 3 cents of new coins
107 BOOST_CHECK( testWallet
.SelectCoinsMinConf( 3 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
108 BOOST_CHECK_EQUAL(nValueRet
, 3 * CENT
);
110 add_coin(5*CENT
); // add a mature 5 cent coin,
111 add_coin(10*CENT
, 3, true); // a new 10 cent coin sent from one of our own addresses
112 add_coin(20*CENT
); // and a mature 20 cent coin
114 // now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
116 // we can't make 38 cents only if we disallow new coins:
117 BOOST_CHECK(!testWallet
.SelectCoinsMinConf(38 * CENT
, 1, 6, 0, vCoins
, setCoinsRet
, nValueRet
));
118 // we can't even make 37 cents if we don't allow new coins even if they're from us
119 BOOST_CHECK(!testWallet
.SelectCoinsMinConf(38 * CENT
, 6, 6, 0, vCoins
, setCoinsRet
, nValueRet
));
120 // but we can make 37 cents if we accept new coins from ourself
121 BOOST_CHECK( testWallet
.SelectCoinsMinConf(37 * CENT
, 1, 6, 0, vCoins
, setCoinsRet
, nValueRet
));
122 BOOST_CHECK_EQUAL(nValueRet
, 37 * CENT
);
123 // and we can make 38 cents if we accept all new coins
124 BOOST_CHECK( testWallet
.SelectCoinsMinConf(38 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
125 BOOST_CHECK_EQUAL(nValueRet
, 38 * CENT
);
127 // try making 34 cents from 1,2,5,10,20 - we can't do it exactly
128 BOOST_CHECK( testWallet
.SelectCoinsMinConf(34 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
129 BOOST_CHECK_EQUAL(nValueRet
, 35 * CENT
); // but 35 cents is closest
130 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
132 // when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
133 BOOST_CHECK( testWallet
.SelectCoinsMinConf( 7 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
134 BOOST_CHECK_EQUAL(nValueRet
, 7 * CENT
);
135 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 2U);
137 // when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
138 BOOST_CHECK( testWallet
.SelectCoinsMinConf( 8 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
139 BOOST_CHECK(nValueRet
== 8 * CENT
);
140 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 3U);
142 // when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
143 BOOST_CHECK( testWallet
.SelectCoinsMinConf( 9 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
144 BOOST_CHECK_EQUAL(nValueRet
, 10 * CENT
);
145 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 1U);
147 // now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin
154 add_coin(30*CENT
); // now we have 6+7+8+20+30 = 71 cents total
156 // check that we have 71 and not 72
157 BOOST_CHECK( testWallet
.SelectCoinsMinConf(71 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
158 BOOST_CHECK(!testWallet
.SelectCoinsMinConf(72 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
160 // now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20
161 BOOST_CHECK( testWallet
.SelectCoinsMinConf(16 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
162 BOOST_CHECK_EQUAL(nValueRet
, 20 * CENT
); // we should get 20 in one coin
163 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 1U);
165 add_coin( 5*CENT
); // now we have 5+6+7+8+20+30 = 75 cents total
167 // now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
168 BOOST_CHECK( testWallet
.SelectCoinsMinConf(16 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
169 BOOST_CHECK_EQUAL(nValueRet
, 18 * CENT
); // we should get 18 in 3 coins
170 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 3U);
172 add_coin( 18*CENT
); // now we have 5+6+7+8+18+20+30
174 // and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
175 BOOST_CHECK( testWallet
.SelectCoinsMinConf(16 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
176 BOOST_CHECK_EQUAL(nValueRet
, 18 * CENT
); // we should get 18 in 1 coin
177 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 1U); // because in the event of a tie, the biggest coin wins
179 // now try making 11 cents. we should get 5+6
180 BOOST_CHECK( testWallet
.SelectCoinsMinConf(11 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
181 BOOST_CHECK_EQUAL(nValueRet
, 11 * CENT
);
182 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 2U);
184 // check that the smallest bigger coin is used
188 add_coin( 4*COIN
); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
189 BOOST_CHECK( testWallet
.SelectCoinsMinConf(95 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
190 BOOST_CHECK_EQUAL(nValueRet
, 1 * COIN
); // we should get 1 BTC in 1 coin
191 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 1U);
193 BOOST_CHECK( testWallet
.SelectCoinsMinConf(195 * CENT
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
194 BOOST_CHECK_EQUAL(nValueRet
, 2 * COIN
); // we should get 2 BTC in 1 coin
195 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 1U);
197 // empty the wallet and start again, now with fractions of a cent, to test small change avoidance
200 add_coin(MIN_CHANGE
* 1 / 10);
201 add_coin(MIN_CHANGE
* 2 / 10);
202 add_coin(MIN_CHANGE
* 3 / 10);
203 add_coin(MIN_CHANGE
* 4 / 10);
204 add_coin(MIN_CHANGE
* 5 / 10);
206 // try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
207 // we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
208 BOOST_CHECK( testWallet
.SelectCoinsMinConf(MIN_CHANGE
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
209 BOOST_CHECK_EQUAL(nValueRet
, MIN_CHANGE
);
211 // but if we add a bigger coin, small change is avoided
212 add_coin(1111*MIN_CHANGE
);
214 // try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
215 BOOST_CHECK( testWallet
.SelectCoinsMinConf(1 * MIN_CHANGE
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
216 BOOST_CHECK_EQUAL(nValueRet
, 1 * MIN_CHANGE
); // we should get the exact amount
218 // if we add more small coins:
219 add_coin(MIN_CHANGE
* 6 / 10);
220 add_coin(MIN_CHANGE
* 7 / 10);
222 // and try again to make 1.0 * MIN_CHANGE
223 BOOST_CHECK( testWallet
.SelectCoinsMinConf(1 * MIN_CHANGE
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
224 BOOST_CHECK_EQUAL(nValueRet
, 1 * MIN_CHANGE
); // we should get the exact amount
226 // run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
227 // they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change
229 for (int j
= 0; j
< 20; j
++)
230 add_coin(50000 * COIN
);
232 BOOST_CHECK( testWallet
.SelectCoinsMinConf(500000 * COIN
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
233 BOOST_CHECK_EQUAL(nValueRet
, 500000 * COIN
); // we should get the exact amount
234 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 10U); // in ten coins
236 // if there's not enough in the smaller coins to make at least 1 * MIN_CHANGE change (0.5+0.6+0.7 < 1.0+1.0),
237 // we need to try finding an exact subset anyway
239 // sometimes it will fail, and so we use the next biggest coin:
241 add_coin(MIN_CHANGE
* 5 / 10);
242 add_coin(MIN_CHANGE
* 6 / 10);
243 add_coin(MIN_CHANGE
* 7 / 10);
244 add_coin(1111 * MIN_CHANGE
);
245 BOOST_CHECK( testWallet
.SelectCoinsMinConf(1 * MIN_CHANGE
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
246 BOOST_CHECK_EQUAL(nValueRet
, 1111 * MIN_CHANGE
); // we get the bigger coin
247 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 1U);
249 // but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
251 add_coin(MIN_CHANGE
* 4 / 10);
252 add_coin(MIN_CHANGE
* 6 / 10);
253 add_coin(MIN_CHANGE
* 8 / 10);
254 add_coin(1111 * MIN_CHANGE
);
255 BOOST_CHECK( testWallet
.SelectCoinsMinConf(MIN_CHANGE
, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
256 BOOST_CHECK_EQUAL(nValueRet
, MIN_CHANGE
); // we should get the exact amount
257 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 2U); // in two coins 0.4+0.6
259 // test avoiding small change
261 add_coin(MIN_CHANGE
* 5 / 100);
262 add_coin(MIN_CHANGE
* 1);
263 add_coin(MIN_CHANGE
* 100);
265 // trying to make 100.01 from these three coins
266 BOOST_CHECK(testWallet
.SelectCoinsMinConf(MIN_CHANGE
* 10001 / 100, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
267 BOOST_CHECK_EQUAL(nValueRet
, MIN_CHANGE
* 10105 / 100); // we should get all coins
268 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 3U);
270 // but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
271 BOOST_CHECK(testWallet
.SelectCoinsMinConf(MIN_CHANGE
* 9990 / 100, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
272 BOOST_CHECK_EQUAL(nValueRet
, 101 * MIN_CHANGE
);
273 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 2U);
275 // test with many inputs
276 for (CAmount amt
=1500; amt
< COIN
; amt
*=10) {
278 // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
279 for (uint16_t j
= 0; j
< 676; j
++)
281 BOOST_CHECK(testWallet
.SelectCoinsMinConf(2000, 1, 1, 0, vCoins
, setCoinsRet
, nValueRet
));
282 if (amt
- 2000 < MIN_CHANGE
) {
283 // needs more than one input:
284 uint16_t returnSize
= std::ceil((2000.0 + MIN_CHANGE
)/amt
);
285 CAmount returnValue
= amt
* returnSize
;
286 BOOST_CHECK_EQUAL(nValueRet
, returnValue
);
287 BOOST_CHECK_EQUAL(setCoinsRet
.size(), returnSize
);
289 // one input is sufficient:
290 BOOST_CHECK_EQUAL(nValueRet
, amt
);
291 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 1U);
298 for (int i2
= 0; i2
< 100; i2
++)
301 // picking 50 from 100 coins doesn't depend on the shuffle,
302 // but does depend on randomness in the stochastic approximation code
303 BOOST_CHECK(testWallet
.SelectCoinsMinConf(50 * COIN
, 1, 6, 0, vCoins
, setCoinsRet
, nValueRet
));
304 BOOST_CHECK(testWallet
.SelectCoinsMinConf(50 * COIN
, 1, 6, 0, vCoins
, setCoinsRet2
, nValueRet
));
305 BOOST_CHECK(!equal_sets(setCoinsRet
, setCoinsRet2
));
308 for (int j
= 0; j
< RANDOM_REPEATS
; j
++)
310 // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
311 // run the test RANDOM_REPEATS times and only complain if all of them fail
312 BOOST_CHECK(testWallet
.SelectCoinsMinConf(COIN
, 1, 6, 0, vCoins
, setCoinsRet
, nValueRet
));
313 BOOST_CHECK(testWallet
.SelectCoinsMinConf(COIN
, 1, 6, 0, vCoins
, setCoinsRet2
, nValueRet
));
314 if (equal_sets(setCoinsRet
, setCoinsRet2
))
317 BOOST_CHECK_NE(fails
, RANDOM_REPEATS
);
319 // add 75 cents in small change. not enough to make 90 cents,
320 // then try making 90 cents. there are multiple competing "smallest bigger" coins,
321 // one of which should be picked at random
329 for (int j
= 0; j
< RANDOM_REPEATS
; j
++)
331 // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
332 // run the test RANDOM_REPEATS times and only complain if all of them fail
333 BOOST_CHECK(testWallet
.SelectCoinsMinConf(90*CENT
, 1, 6, 0, vCoins
, setCoinsRet
, nValueRet
));
334 BOOST_CHECK(testWallet
.SelectCoinsMinConf(90*CENT
, 1, 6, 0, vCoins
, setCoinsRet2
, nValueRet
));
335 if (equal_sets(setCoinsRet
, setCoinsRet2
))
338 BOOST_CHECK_NE(fails
, RANDOM_REPEATS
);
344 BOOST_AUTO_TEST_CASE(ApproximateBestSubset
)
349 LOCK(testWallet
.cs_wallet
);
353 // Test vValue sort order
354 for (int i
= 0; i
< 1000; i
++)
355 add_coin(1000 * COIN
);
358 BOOST_CHECK(testWallet
.SelectCoinsMinConf(1003 * COIN
, 1, 6, 0, vCoins
, setCoinsRet
, nValueRet
));
359 BOOST_CHECK_EQUAL(nValueRet
, 1003 * COIN
);
360 BOOST_CHECK_EQUAL(setCoinsRet
.size(), 2U);
365 static void AddKey(CWallet
& wallet
, const CKey
& key
)
367 LOCK(wallet
.cs_wallet
);
368 wallet
.AddKeyPubKey(key
, key
.GetPubKey());
371 BOOST_FIXTURE_TEST_CASE(rescan
, TestChain100Setup
)
373 // Cap last block file size, and mine new block in a new block file.
374 CBlockIndex
* const nullBlock
= nullptr;
375 CBlockIndex
* oldTip
= chainActive
.Tip();
376 GetBlockFileInfo(oldTip
->GetBlockPos().nFile
)->nSize
= MAX_BLOCKFILE_SIZE
;
377 CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey
.GetPubKey()));
378 CBlockIndex
* newTip
= chainActive
.Tip();
382 // Verify ScanForWalletTransactions picks up transactions in both the old
383 // and new block files.
386 AddKey(wallet
, coinbaseKey
);
387 BOOST_CHECK_EQUAL(nullBlock
, wallet
.ScanForWalletTransactions(oldTip
, nullptr));
388 BOOST_CHECK_EQUAL(wallet
.GetImmatureBalance(), 100 * COIN
);
391 // Prune the older block file.
392 PruneOneBlockFile(oldTip
->GetBlockPos().nFile
);
393 UnlinkPrunedFiles({oldTip
->GetBlockPos().nFile
});
395 // Verify ScanForWalletTransactions only picks transactions in the new block
399 AddKey(wallet
, coinbaseKey
);
400 BOOST_CHECK_EQUAL(oldTip
, wallet
.ScanForWalletTransactions(oldTip
, nullptr));
401 BOOST_CHECK_EQUAL(wallet
.GetImmatureBalance(), 50 * COIN
);
404 // Verify importmulti RPC returns failure for a key whose creation time is
405 // before the missing block, and success for a key whose creation time is
409 vpwallets
.insert(vpwallets
.begin(), &wallet
);
414 key
.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey
.GetPubKey())));
415 key
.pushKV("timestamp", 0);
416 key
.pushKV("internal", UniValue(true));
421 futureKey
.MakeNewKey(true);
422 key
.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey
.GetPubKey())));
423 key
.pushKV("timestamp", newTip
->GetBlockTimeMax() + TIMESTAMP_WINDOW
+ 1);
424 key
.pushKV("internal", UniValue(true));
426 JSONRPCRequest request
;
427 request
.params
.setArray();
428 request
.params
.push_back(keys
);
430 UniValue response
= importmulti(request
);
431 BOOST_CHECK_EQUAL(response
.write(),
432 strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation "
433 "timestamp %d. There was an error reading a block from time %d, which is after or within %d "
434 "seconds of key creation, and could contain transactions pertaining to the key. As a result, "
435 "transactions and coins using this key may not appear in the wallet. This error could be caused "
436 "by pruning or data corruption (see bitcoind log for details) and could be dealt with by "
437 "downloading and rescanning the relevant blocks (see -reindex and -rescan "
438 "options).\"}},{\"success\":true}]",
439 0, oldTip
->GetBlockTimeMax(), TIMESTAMP_WINDOW
));
440 vpwallets
.erase(vpwallets
.begin());
444 // Verify importwallet RPC starts rescan at earliest block with timestamp
445 // greater or equal than key birthday. Previously there was a bug where
446 // importwallet RPC would start the scan at the latest block with timestamp less
447 // than or equal to key birthday.
448 BOOST_FIXTURE_TEST_CASE(importwallet_rescan
, TestChain100Setup
)
450 // Create two blocks with same timestamp to verify that importwallet rescan
451 // will pick up both blocks, not just the first.
452 const int64_t BLOCK_TIME
= chainActive
.Tip()->GetBlockTimeMax() + 5;
453 SetMockTime(BLOCK_TIME
);
454 coinbaseTxns
.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey
.GetPubKey())).vtx
[0]);
455 coinbaseTxns
.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey
.GetPubKey())).vtx
[0]);
457 // Set key birthday to block time increased by the timestamp window, so
458 // rescan will start at the block time.
459 const int64_t KEY_TIME
= BLOCK_TIME
+ TIMESTAMP_WINDOW
;
460 SetMockTime(KEY_TIME
);
461 coinbaseTxns
.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey
.GetPubKey())).vtx
[0]);
465 // Import key into wallet and call dumpwallet to create backup file.
468 LOCK(wallet
.cs_wallet
);
469 wallet
.mapKeyMetadata
[coinbaseKey
.GetPubKey().GetID()].nCreateTime
= KEY_TIME
;
470 wallet
.AddKeyPubKey(coinbaseKey
, coinbaseKey
.GetPubKey());
472 JSONRPCRequest request
;
473 request
.params
.setArray();
474 request
.params
.push_back((pathTemp
/ "wallet.backup").string());
475 vpwallets
.insert(vpwallets
.begin(), &wallet
);
476 ::dumpwallet(request
);
479 // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
480 // were scanned, and no prior blocks were scanned.
484 JSONRPCRequest request
;
485 request
.params
.setArray();
486 request
.params
.push_back((pathTemp
/ "wallet.backup").string());
487 vpwallets
[0] = &wallet
;
488 ::importwallet(request
);
490 LOCK(wallet
.cs_wallet
);
491 BOOST_CHECK_EQUAL(wallet
.mapWallet
.size(), 3);
492 BOOST_CHECK_EQUAL(coinbaseTxns
.size(), 103);
493 for (size_t i
= 0; i
< coinbaseTxns
.size(); ++i
) {
494 bool found
= wallet
.GetWalletTx(coinbaseTxns
[i
].GetHash());
495 bool expected
= i
>= 100;
496 BOOST_CHECK_EQUAL(found
, expected
);
501 vpwallets
.erase(vpwallets
.begin());
504 // Check that GetImmatureCredit() returns a newly calculated value instead of
505 // the cached value after a MarkDirty() call.
507 // This is a regression test written to verify a bugfix for the immature credit
508 // function. Similar tests probably should be written for the other credit and
510 BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit
, TestChain100Setup
)
513 CWalletTx
wtx(&wallet
, MakeTransactionRef(coinbaseTxns
.back()));
514 LOCK2(cs_main
, wallet
.cs_wallet
);
515 wtx
.hashBlock
= chainActive
.Tip()->GetBlockHash();
518 // Call GetImmatureCredit() once before adding the key to the wallet to
519 // cache the current immature credit amount, which is 0.
520 BOOST_CHECK_EQUAL(wtx
.GetImmatureCredit(), 0);
522 // Invalidate the cached value, add the key, and make sure a new immature
523 // credit amount is calculated.
525 wallet
.AddKeyPubKey(coinbaseKey
, coinbaseKey
.GetPubKey());
526 BOOST_CHECK_EQUAL(wtx
.GetImmatureCredit(), 50*COIN
);
529 static int64_t AddTx(CWallet
& wallet
, uint32_t lockTime
, int64_t mockTime
, int64_t blockTime
)
531 CMutableTransaction tx
;
532 tx
.nLockTime
= lockTime
;
533 SetMockTime(mockTime
);
534 CBlockIndex
* block
= nullptr;
537 auto inserted
= mapBlockIndex
.emplace(GetRandHash(), new CBlockIndex
);
538 assert(inserted
.second
);
539 const uint256
& hash
= inserted
.first
->first
;
540 block
= inserted
.first
->second
;
541 block
->nTime
= blockTime
;
542 block
->phashBlock
= &hash
;
545 CWalletTx
wtx(&wallet
, MakeTransactionRef(tx
));
547 wtx
.SetMerkleBranch(block
, 0);
549 wallet
.AddToWallet(wtx
);
550 LOCK(wallet
.cs_wallet
);
551 return wallet
.mapWallet
.at(wtx
.GetHash()).nTimeSmart
;
554 // Simple test to verify assignment of CWalletTx::nSmartTime value. Could be
555 // expanded to cover more corner cases of smart time logic.
556 BOOST_AUTO_TEST_CASE(ComputeTimeSmart
)
560 // New transaction should use clock time if lower than block time.
561 BOOST_CHECK_EQUAL(AddTx(wallet
, 1, 100, 120), 100);
563 // Test that updating existing transaction does not change smart time.
564 BOOST_CHECK_EQUAL(AddTx(wallet
, 1, 200, 220), 100);
566 // New transaction should use clock time if there's no block time.
567 BOOST_CHECK_EQUAL(AddTx(wallet
, 2, 300, 0), 300);
569 // New transaction should use block time if lower than clock time.
570 BOOST_CHECK_EQUAL(AddTx(wallet
, 3, 420, 400), 400);
572 // New transaction should use latest entry time if higher than
573 // min(block time, clock time).
574 BOOST_CHECK_EQUAL(AddTx(wallet
, 4, 500, 390), 400);
576 // If there are future entries, new transaction should use time of the
577 // newest entry that is no more than 300 seconds ahead of the clock time.
578 BOOST_CHECK_EQUAL(AddTx(wallet
, 5, 50, 600), 300);
580 // Reset mock time for other tests.
584 BOOST_AUTO_TEST_CASE(LoadReceiveRequests
)
586 CTxDestination dest
= CKeyID();
587 LOCK(pwalletMain
->cs_wallet
);
588 pwalletMain
->AddDestData(dest
, "misc", "val_misc");
589 pwalletMain
->AddDestData(dest
, "rr0", "val_rr0");
590 pwalletMain
->AddDestData(dest
, "rr1", "val_rr1");
592 auto values
= pwalletMain
->GetDestValues("rr");
593 BOOST_CHECK_EQUAL(values
.size(), 2);
594 BOOST_CHECK_EQUAL(values
[0], "val_rr0");
595 BOOST_CHECK_EQUAL(values
[1], "val_rr1");
598 class ListCoinsTestingSetup
: public TestChain100Setup
601 ListCoinsTestingSetup()
603 CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey
.GetPubKey()));
605 wallet
.reset(new CWallet(std::unique_ptr
<CWalletDBWrapper
>(new CWalletDBWrapper(&bitdb
, "wallet_test.dat"))));
607 wallet
->LoadWallet(firstRun
);
608 AddKey(*wallet
, coinbaseKey
);
609 wallet
->ScanForWalletTransactions(chainActive
.Genesis(), nullptr);
612 ~ListCoinsTestingSetup()
619 CWalletTx
& AddTx(CRecipient recipient
)
622 CReserveKey
reservekey(wallet
.get());
627 BOOST_CHECK(wallet
->CreateTransaction({recipient
}, wtx
, reservekey
, fee
, changePos
, error
, dummy
));
628 CValidationState state
;
629 BOOST_CHECK(wallet
->CommitTransaction(wtx
, reservekey
, nullptr, state
));
630 CMutableTransaction blocktx
;
632 LOCK(wallet
->cs_wallet
);
633 blocktx
= CMutableTransaction(*wallet
->mapWallet
.at(wtx
.GetHash()).tx
);
635 CreateAndProcessBlock({CMutableTransaction(blocktx
)}, GetScriptForRawPubKey(coinbaseKey
.GetPubKey()));
636 LOCK(wallet
->cs_wallet
);
637 auto it
= wallet
->mapWallet
.find(wtx
.GetHash());
638 BOOST_CHECK(it
!= wallet
->mapWallet
.end());
639 it
->second
.SetMerkleBranch(chainActive
.Tip(), 1);
643 std::unique_ptr
<CWallet
> wallet
;
646 BOOST_FIXTURE_TEST_CASE(ListCoins
, ListCoinsTestingSetup
)
648 std::string coinbaseAddress
= coinbaseKey
.GetPubKey().GetID().ToString();
650 // Confirm ListCoins initially returns 1 coin grouped under coinbaseKey
652 auto list
= wallet
->ListCoins();
653 BOOST_CHECK_EQUAL(list
.size(), 1);
654 BOOST_CHECK_EQUAL(boost::get
<CKeyID
>(list
.begin()->first
).ToString(), coinbaseAddress
);
655 BOOST_CHECK_EQUAL(list
.begin()->second
.size(), 1);
657 // Check initial balance from one mature coinbase transaction.
658 BOOST_CHECK_EQUAL(50 * COIN
, wallet
->GetAvailableBalance());
660 // Add a transaction creating a change address, and confirm ListCoins still
661 // returns the coin associated with the change address underneath the
662 // coinbaseKey pubkey, even though the change address has a different
664 AddTx(CRecipient
{GetScriptForRawPubKey({}), 1 * COIN
, false /* subtract fee */});
665 list
= wallet
->ListCoins();
666 BOOST_CHECK_EQUAL(list
.size(), 1);
667 BOOST_CHECK_EQUAL(boost::get
<CKeyID
>(list
.begin()->first
).ToString(), coinbaseAddress
);
668 BOOST_CHECK_EQUAL(list
.begin()->second
.size(), 2);
670 // Lock both coins. Confirm number of available coins drops to 0.
671 std::vector
<COutput
> available
;
672 wallet
->AvailableCoins(available
);
673 BOOST_CHECK_EQUAL(available
.size(), 2);
674 for (const auto& group
: list
) {
675 for (const auto& coin
: group
.second
) {
676 LOCK(wallet
->cs_wallet
);
677 wallet
->LockCoin(COutPoint(coin
.tx
->GetHash(), coin
.i
));
680 wallet
->AvailableCoins(available
);
681 BOOST_CHECK_EQUAL(available
.size(), 0);
683 // Confirm ListCoins still returns same result as before, despite coins
685 list
= wallet
->ListCoins();
686 BOOST_CHECK_EQUAL(list
.size(), 1);
687 BOOST_CHECK_EQUAL(boost::get
<CKeyID
>(list
.begin()->first
).ToString(), coinbaseAddress
);
688 BOOST_CHECK_EQUAL(list
.begin()->second
.size(), 2);
691 BOOST_AUTO_TEST_SUITE_END()