PIVX Core  5.6.99
P2P Digital Currency
wallet_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2012-2014 The Bitcoin Core developers
2 // Copyright (c) 2019-2021 The PIVX Core developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
7 
8 #include "consensus/merkle.h"
9 #include "rpc/server.h"
10 #include "txmempool.h"
11 #include "validation.h"
12 #include "wallet/wallet.h"
13 #include "wallet/walletutil.h"
14 
15 #include <set>
16 #include <utility>
17 #include <vector>
18 
19 #include <boost/test/unit_test.hpp>
20 #include <univalue.h>
21 
22 extern UniValue importmulti(const JSONRPCRequest& request);
23 extern UniValue dumpwallet(const JSONRPCRequest& request);
24 extern UniValue importwallet(const JSONRPCRequest& request);
25 
26 // how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles
27 #define RUN_TESTS 100
28 
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
32 
33 std::vector<std::unique_ptr<CWalletTx>> wtxn;
34 
35 typedef std::set<std::pair<const CWalletTx*,unsigned int> > CoinSet;
36 
38 
39 static const CWallet testWallet("dummy", WalletDatabase::CreateDummy());
40 static std::vector<COutput> vCoins;
41 
42 static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
43 {
44  static int nextLockTime = 0;
46  tx.nLockTime = nextLockTime++; // so all transactions get different hashes
47  tx.vout.resize(nInput+1);
48  tx.vout[nInput].nValue = nValue;
49  if (fIsFromMe) {
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()
52  tx.vin.resize(1);
53  }
54  std::unique_ptr<CWalletTx> wtx(new CWalletTx(&testWallet, MakeTransactionRef(std::move(tx))));
55  if (fIsFromMe) {
56  wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
57  }
58  COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
59  vCoins.push_back(output);
60  wtxn.emplace_back(std::move(wtx));
61 }
62 
63 static void empty_wallet(void)
64 {
65  vCoins.clear();
66  wtxn.clear();
67 }
68 
69 static bool equal_sets(CoinSet a, CoinSet b)
70 {
71  std::pair<CoinSet::iterator, CoinSet::iterator> ret = mismatch(a.begin(), a.end(), b.begin());
72  return ret.first == a.end() && ret.second == b.end();
73 }
74 
75 BOOST_AUTO_TEST_CASE(coin_selection_tests)
76 {
77  CoinSet setCoinsRet, setCoinsRet2;
78  CAmount nValueRet;
79 
80  LOCK(testWallet.cs_wallet);
81 
82  // test multiple times to allow for differences in the shuffle order
83  for (int i = 0; i < RUN_TESTS; i++)
84  {
85  empty_wallet();
86 
87  // with an empty wallet we can't even pay one cent
88  BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
89 
90  add_coin(1*CENT, 4); // add a new 1 cent coin
91 
92  // with a new 1 cent coin, we still can't find a mature 1 cent
93  BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
94 
95  // but we can find a new 1 cent
96  BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
97  BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
98 
99  add_coin(2*CENT); // add a mature 2 cent coin
100 
101  // we can't make 3 cents of mature coins
102  BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
103 
104  // we can make 3 cents of new coins
105  BOOST_CHECK(testWallet.SelectCoinsMinConf( 3 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
106  BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
107 
108  add_coin(5*CENT); // add a mature 5 cent coin,
109  add_coin(10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
110  add_coin(20*CENT); // and a mature 20 cent coin
111 
112  // now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
113 
114  // we can't make 38 cents only if we disallow new coins:
115  BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
116  // we can't even make 37 cents if we don't allow new coins even if they're from us
117  BOOST_CHECK(!testWallet.SelectCoinsMinConf(38 * CENT, 6, 6, 0, vCoins, setCoinsRet, nValueRet));
118  // but we can make 37 cents if we accept new coins from ourself
119  BOOST_CHECK(testWallet.SelectCoinsMinConf(37 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
120  BOOST_CHECK_EQUAL(nValueRet, 37 * CENT);
121  // and we can make 38 cents if we accept all new coins
122  BOOST_CHECK(testWallet.SelectCoinsMinConf(38 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
123  BOOST_CHECK_EQUAL(nValueRet, 38 * CENT);
124 
125  // try making 34 cents from 1,2,5,10,20 - we can't do it exactly
126  BOOST_CHECK(testWallet.SelectCoinsMinConf(34 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
127  BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest
128  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)
129 
130  // when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
131  BOOST_CHECK(testWallet.SelectCoinsMinConf( 7 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
132  BOOST_CHECK_EQUAL(nValueRet, 7 * CENT);
133  BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
134 
135  // when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
136  BOOST_CHECK(testWallet.SelectCoinsMinConf( 8 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
137  BOOST_CHECK(nValueRet == 8 * CENT);
138  BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
139 
140  // when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
141  BOOST_CHECK(testWallet.SelectCoinsMinConf( 9 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
142  BOOST_CHECK_EQUAL(nValueRet, 10 * CENT);
143  BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
144 
145  // now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin
146  empty_wallet();
147 
148  add_coin(6*CENT);
149  add_coin(7*CENT);
150  add_coin(8*CENT);
151  add_coin(20*CENT);
152  add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total
153 
154  // check that we have 71 and not 72
155  BOOST_CHECK(testWallet.SelectCoinsMinConf(71 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
156  BOOST_CHECK(!testWallet.SelectCoinsMinConf(72 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
157 
158  // 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
159  BOOST_CHECK(testWallet.SelectCoinsMinConf(16 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
160  BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin
161  BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
162 
163  add_coin(5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
164 
165  // 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
166  BOOST_CHECK(testWallet.SelectCoinsMinConf(16 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
167  BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins
168  BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
169 
170  add_coin(18*CENT); // now we have 5+6+7+8+18+20+30
171 
172  // 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
173  BOOST_CHECK(testWallet.SelectCoinsMinConf(16 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
174  BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin
175  BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins
176 
177  // now try making 11 cents. we should get 5+6
178  BOOST_CHECK(testWallet.SelectCoinsMinConf(11 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
179  BOOST_CHECK_EQUAL(nValueRet, 11 * CENT);
180  BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
181 
182  // check that the smallest bigger coin is used
183  add_coin(1*COIN);
184  add_coin(2*COIN);
185  add_coin(3*COIN);
186  add_coin(4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
187  BOOST_CHECK(testWallet.SelectCoinsMinConf(95 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
188  BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin
189  BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
190 
191  BOOST_CHECK(testWallet.SelectCoinsMinConf(195 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
192  BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin
193  BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
194 
195  // empty the wallet and start again, now with fractions of a cent, to test small change avoidance
196 
197  empty_wallet();
198  add_coin(0.1*MIN_CHANGE);
199  add_coin(0.2*MIN_CHANGE);
200  add_coin(0.3*MIN_CHANGE);
201  add_coin(0.4*MIN_CHANGE);
202  add_coin(0.5*MIN_CHANGE);
203 
204  // try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
205  // we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
206  BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
207  BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
208 
209  // but if we add a bigger coin, small change is avoided
210  add_coin(1111*MIN_CHANGE);
211 
212  // try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
213  BOOST_CHECK(testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
214  BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
215 
216  // if we add more small coins:
217  add_coin(0.6*MIN_CHANGE);
218  add_coin(0.7*MIN_CHANGE);
219 
220  // and try again to make 1.0 * MIN_CHANGE
221  BOOST_CHECK(testWallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
222  BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
223 
224  // run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
225  // they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change
226  empty_wallet();
227  for (int j = 0; j < 20; j++)
228  add_coin(50000 * COIN);
229 
230  BOOST_CHECK(testWallet.SelectCoinsMinConf(500000 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
231  BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
232  BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins
233 
234  // 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),
235  // we need to try finding an exact subset anyway
236 
237  // sometimes it will fail, and so we use the next biggest coin:
238  empty_wallet();
239  add_coin(0.5 * CENT);
240  add_coin(0.6 * CENT);
241  add_coin(0.7 * CENT);
242  add_coin(1111 * CENT);
243  BOOST_CHECK(testWallet.SelectCoinsMinConf(1 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
244  BOOST_CHECK_EQUAL(nValueRet, 1111 * CENT); // we get the bigger coin
245  BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
246 
247  // but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
248  empty_wallet();
249  add_coin(0.4 * MIN_CHANGE);
250  add_coin(0.6 * MIN_CHANGE);
251  add_coin(0.8 * MIN_CHANGE);
252  add_coin(1111 * MIN_CHANGE);
253  BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
254  BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
255  BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
256 
257  // test avoiding small change
258  empty_wallet();
259  add_coin(0.05 * MIN_CHANGE);
260  add_coin(1 * MIN_CHANGE);
261  add_coin(100 * MIN_CHANGE);
262 
263  // trying to make 100.01 from these three coins
264  BOOST_CHECK(testWallet.SelectCoinsMinConf(100.01 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
265  BOOST_CHECK_EQUAL(nValueRet, 101.05 * MIN_CHANGE); // we should get all coins
266  BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
267 
268  // but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
269  BOOST_CHECK(testWallet.SelectCoinsMinConf(99.9 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
270  BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
271  BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
272 
273  // test with many inputs
274  for (CAmount amt=1500; amt < COIN; amt*=10) {
275  empty_wallet();
276  // Create 676 inputs (= MAX_STANDARD_TX_SIZE / 148 bytes per input)
277  for (uint16_t j = 0; j < 676; j++)
278  add_coin(amt);
279  BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
280  if (amt - 2000 < MIN_CHANGE) {
281  // needs more than one input:
282  uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
283  CAmount returnValue = amt * returnSize;
284  BOOST_CHECK_EQUAL(nValueRet, returnValue);
285  BOOST_CHECK_EQUAL(setCoinsRet.size(), returnSize);
286  } else {
287  // one input is sufficient:
288  BOOST_CHECK_EQUAL(nValueRet, amt);
289  BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
290  }
291  }
292 
293  // test randomness
294  {
295  empty_wallet();
296  for (int i2 = 0; i2 < 100; i2++)
297  add_coin(COIN);
298 
299  // picking 50 from 100 coins doesn't depend on the shuffle,
300  // but does depend on randomness in the stochastic approximation code
301  BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, 1, 6, 0, vCoins, setCoinsRet , nValueRet));
302  BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, 1, 6, 0, vCoins, setCoinsRet2, nValueRet));
303  BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2));
304 
305  int fails = 0;
306  for (int j = 0; j < RANDOM_REPEATS; j++)
307  {
308  // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
309  // run the test RANDOM_REPEATS times and only complain if all of them fail
310  BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, 1, 6, 0, vCoins, setCoinsRet , nValueRet));
311  BOOST_CHECK(testWallet.SelectCoinsMinConf(COIN, 1, 6, 0, vCoins, setCoinsRet2, nValueRet));
312  if (equal_sets(setCoinsRet, setCoinsRet2))
313  fails++;
314  }
315  BOOST_CHECK_NE(fails, RANDOM_REPEATS);
316 
317  // add 75 cents in small change. not enough to make 90 cents,
318  // then try making 90 cents. there are multiple competing "smallest bigger" coins,
319  // one of which should be picked at random
320  add_coin(5 * CENT);
321  add_coin(10 * CENT);
322  add_coin(15 * CENT);
323  add_coin(20 * CENT);
324  add_coin(25 * CENT);
325 
326  fails = 0;
327  for (int j = 0; j < RANDOM_REPEATS; j++)
328  {
329  // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
330  // run the test RANDOM_REPEATS times and only complain if all of them fail
331  BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, 1, 6, 0, vCoins, setCoinsRet , nValueRet));
332  BOOST_CHECK(testWallet.SelectCoinsMinConf(90*CENT, 1, 6, 0, vCoins, setCoinsRet2, nValueRet));
333  if (equal_sets(setCoinsRet, setCoinsRet2))
334  fails++;
335  }
336  BOOST_CHECK_NE(fails, RANDOM_REPEATS);
337  }
338  }
339  empty_wallet();
340 }
341 
342 static void AddKey(CWallet& wallet, const CKey& key)
343 {
344  LOCK(wallet.cs_wallet);
345  wallet.AddKeyPubKey(key, key.GetPubKey());
346 }
347 
349 {
350  // Cap last block file size, and mine new block in a new block file.
351  CBlockIndex* const nullBlock = nullptr;
352  CBlockIndex* oldTip = chainActive.Tip();
353  GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
354  CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
355  CBlockIndex* newTip = chainActive.Tip();
356 
357  LOCK(cs_main);
358 
359  // Verify ScanForWalletTransactions picks up transactions in both the old
360  // and new block files.
361  {
362  CWallet wallet("dummy", WalletDatabase::CreateDummy());
363  WITH_LOCK(wallet.cs_wallet, wallet.SetLastBlockProcessed(newTip); );
364  AddKey(wallet, coinbaseKey);
365  WalletRescanReserver reserver(&wallet);
366  reserver.reserve();
367  BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip, nullptr, reserver));
368  BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 500 * COIN);
369  }
370 
371  // !TODO: Prune the older block file.
372  /*
373  PruneOneBlockFile(oldTip->GetBlockPos().nFile);
374  UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
375 
376  // Verify ScanForWalletTransactions only picks transactions in the new block
377  // file.
378  {
379  CWallet wallet("dummy", WalletDatabase::CreateDummy());
380  AddKey(wallet, coinbaseKey);
381  WalletRescanReserver reserver(&wallet);
382  reserver.reserve();
383  BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip, nullptr, reserver));;
384  BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 250 * COIN);
385  }
386  */
387 
388  // Verify importmulti RPC returns failure for a key whose creation time is
389  // before the missing block, and success for a key whose creation time is
390  // after.
391  {
392  CWallet wallet("dummy", WalletDatabase::CreateDummy());
393  WITH_LOCK(wallet.cs_wallet, wallet.SetLastBlockProcessed(newTip); );
394  vpwallets.insert(vpwallets.begin(), &wallet);
395  UniValue keys;
396  keys.setArray();
397  UniValue key;
398  key.setObject();
399  key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey())));
400  key.pushKV("timestamp", 0);
401  key.pushKV("internal", UniValue(true));
402  keys.push_back(key);
403  key.clear();
404  key.setObject();
405  CKey futureKey;
406  futureKey.MakeNewKey(true);
407  key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey())));
408  key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1);
409  key.pushKV("internal", UniValue(true));
410  keys.push_back(key);
411  JSONRPCRequest request;
412  request.params.setArray();
413  request.params.push_back(keys);
414 
415  UniValue response = importmulti(request);
416  // !TODO: after pruning, check that the rescan for the first key fails.
417  BOOST_CHECK_EQUAL(response.write(), "[{\"success\":true},{\"success\":true}]");
418  vpwallets.erase(vpwallets.begin());
419  }
420 }
421 
422 // Verify importwallet RPC starts rescan at earliest block with timestamp
423 // greater or equal than key birthday. Previously there was a bug where
424 // importwallet RPC would start the scan at the latest block with timestamp less
425 // than or equal to key birthday.
427 {
428  // Create one block
429  const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 15;
430  SetMockTime(BLOCK_TIME);
431  coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
432 
433  // Set key birthday to block time increased by the timestamp window, so
434  // rescan will start at the block time.
435  const int64_t KEY_TIME = BLOCK_TIME + TIMESTAMP_WINDOW;
436  SetMockTime(KEY_TIME);
437  coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
438 
439  std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string();
440 
441  // Import key into wallet and call dumpwallet to create backup file.
442  {
443  CWallet wallet("dummy", WalletDatabase::CreateDummy());
444  {
445  LOCK(wallet.cs_wallet);
446  wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
447  wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
448  }
449 
450  JSONRPCRequest request;
451  request.params.setArray();
452  request.params.push_back(backup_file);
453  vpwallets.insert(vpwallets.begin(), &wallet);
454  ::dumpwallet(request);
455  }
456 
457  // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
458  // were scanned, and no prior blocks were scanned.
459  {
460  CWallet wallet("dummy", WalletDatabase::CreateDummy());
461 
462  JSONRPCRequest request;
463  request.params.setArray();
464  request.params.push_back(backup_file);
465  vpwallets[0] = &wallet;
466  ::importwallet(request);
467 
468  LOCK(wallet.cs_wallet);
469  BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 2);
470  BOOST_CHECK_EQUAL(coinbaseTxns.size(), 102);
471  for (size_t i = 0; i < coinbaseTxns.size(); ++i) {
472  bool found = wallet.GetWalletTx(coinbaseTxns[i].GetHash());
473  bool expected = i >= 100;
474  BOOST_CHECK_EQUAL(found, expected);
475  }
476  }
477 
478  SetMockTime(0);
479  vpwallets.erase(vpwallets.begin());
480 }
481 
483 {
484  LOCK(mempool.cs);
485  if (mempool.exists(wtx.GetHash())) {
486  auto it = mempool.mapTx.find(wtx.GetHash());
487  if (it != mempool.mapTx.end()) {
488  mempool.mapTx.erase(it);
489  }
490  }
491 }
492 
496 CBlockIndex* SimpleFakeMine(CWalletTx& wtx, CWallet &wallet, CBlockIndex* pprev = nullptr)
497 {
498  CBlock block;
499  block.vtx.emplace_back(wtx.tx);
500  block.hashMerkleRoot = BlockMerkleRoot(block);
501  if (pprev) block.hashPrevBlock = pprev->GetBlockHash();
502  CBlockIndex* fakeIndex = new CBlockIndex(block);
503  fakeIndex->pprev = pprev;
504  mapBlockIndex.emplace(block.GetHash(), fakeIndex);
505  fakeIndex->phashBlock = &mapBlockIndex.find(block.GetHash())->first;
506  chainActive.SetTip(fakeIndex);
507  BOOST_CHECK(chainActive.Contains(fakeIndex));
508  WITH_LOCK(wallet.cs_wallet, wallet.SetLastBlockProcessed(fakeIndex));
509  wtx.m_confirm = CWalletTx::Confirmation(CWalletTx::Status::CONFIRMED, fakeIndex->nHeight, fakeIndex->GetBlockHash(), 0);
510  removeTxFromMempool(wtx);
511  wtx.fInMempool = false;
512  return fakeIndex;
513 }
514 
515 void fakeMempoolInsertion(const CTransactionRef& wtxCredit)
516 {
517  CTxMemPoolEntry entry(wtxCredit, 0, 0, 0, false, 0);
518  LOCK(mempool.cs);
519  mempool.mapTx.insert(entry);
520 }
521 
522 CWalletTx& BuildAndLoadTxToWallet(const std::vector<CTxIn>& vin,
523  const std::vector<CTxOut>& vout,
524  CWallet& wallet)
525 {
527  mTx.vin = vin;
528  mTx.vout = vout;
529  CTransaction tx(mTx);
530  CWalletTx wtx(&wallet, MakeTransactionRef(tx));
531  wallet.LoadToWallet(wtx);
532  return wallet.mapWallet.at(tx.GetHash());
533 }
534 
535 CWalletTx& ReceiveBalanceWith(const std::vector<CTxOut>& vout,
536  CWallet& wallet)
537 {
538  std::vector<CTxIn> vin;
539  vin.emplace_back(CTxIn(COutPoint(uint256(), 999)));
540  return BuildAndLoadTxToWallet(vin, vout, wallet);
541 }
542 
543 void CheckBalances(const CWalletTx& tx,
544  const CAmount& nCreditAll,
545  const CAmount& nCreditSpendable,
546  const CAmount& nAvailableCredit,
547  const CAmount& nDebitAll,
548  const CAmount& nDebitSpendable)
549 {
550  BOOST_CHECK_EQUAL(tx.GetCredit(ISMINE_ALL), nCreditAll);
551  BOOST_CHECK_EQUAL(tx.GetCredit(ISMINE_SPENDABLE), nCreditSpendable);
553  BOOST_CHECK_EQUAL(tx.GetAvailableCredit(), nAvailableCredit);
555  BOOST_CHECK_EQUAL(tx.GetDebit(ISMINE_ALL), nDebitAll);
556  BOOST_CHECK_EQUAL(tx.GetDebit(ISMINE_SPENDABLE), nDebitSpendable);
558 }
559 
569 BOOST_AUTO_TEST_CASE(cached_balances_tests)
570 {
571  // 1) Receive balance from an external source and verify:
572  // * GetCredit(ISMINE_ALL) correctness (must be equal to 'nCredit' amount)
573  // * GetCredit(ISMINE_SPENDABLE) correctness (must be equal to ISMINE_ALL) + must be cached.
574  // * GetAvailableCredit() correctness (must be equal to ISMINE_ALL)
575  // * GetDebit(ISMINE_ALL) correctness (must be 0)
576  // * wallet.GetUnconfirmedBalance() correctness (must be equal 'nCredit')
577 
578  // 2) Confirm the tx and verify:
579  // * wallet.GetUnconfirmedBalance() correctness (must be 0)
580  // * GetAvailableCredit() correctness (must be equal to (1) ISMINE_ALL)
581 
582  // 3) Spend one of the two outputs of the receiving tx to an external source
583  // and verify:
584  // * creditTx.GetAvailableCredit() correctness (must be equal to 'nCredit' / 2) + must be cached.
585  // * debitTx.GetDebit(ISMINE_ALL) correctness (must be equal to 'nCredit' / 2)
586  // * debitTx.GetDebit(ISMINE_SPENDABLE) correctness (must be equal to 'nCredit' / 2) + must be cached.
587  // * debitTx.GetAvailableCredit() correctness (must be 0).
588 
589  CAmount nCredit = 20 * COIN;
590 
591  // Setup wallet
592  CWallet wallet("testWallet1", WalletDatabase::CreateMock());
593  bool fFirstRun;
594  BOOST_CHECK_EQUAL(wallet.LoadWallet(fFirstRun), DB_LOAD_OK);
595  LOCK2(cs_main, wallet.cs_wallet);
597  wallet.SetupSPKM(false);
599 
600  // Receive balance from an external source
601  auto res = wallet.getNewAddress("receiving_address");
602  BOOST_ASSERT(res);
603  CTxDestination receivingAddr = *res.getObjResult();
604  CTxOut creditOut(nCredit/2, GetScriptForDestination(receivingAddr));
605  CWalletTx& wtxCredit = ReceiveBalanceWith({creditOut, creditOut},wallet);
606 
607  // Validates (1)
609  wtxCredit,
610  nCredit, // CREDIT-ISMINE_ALL
611  nCredit, // CREDIT-ISMINE_SPENDABLE
612  nCredit, // AVAILABLE_CREDIT
613  0, // DEBIT-ISMINE_ALL
614  0 // DEBIT-ISMINE_SPENDABLE
615  );
616 
617  // GetUnconfirmedBalance requires tx in mempool.
618  fakeMempoolInsertion(wtxCredit.tx);
619  wtxCredit.fInMempool = true;
620  BOOST_CHECK_EQUAL(wallet.GetUnconfirmedBalance(), nCredit);
621 
622  // 2) Confirm tx and verify
623  SimpleFakeMine(wtxCredit, wallet);
625  BOOST_CHECK_EQUAL(wtxCredit.GetAvailableCredit(), nCredit);
626 
627  // 3) Spend one of the two outputs of the receiving tx to an external source and verify.
628  // Create debit transaction.
629  CAmount nDebit = nCredit / 2;
630  std::vector<CTxIn> vinDebit = {CTxIn(COutPoint(wtxCredit.GetHash(), 0))};
631  CKey key;
632  key.MakeNewKey(true);
633  std::vector<CTxOut> voutDebit = {CTxOut(nDebit, GetScriptForDestination(key.GetPubKey().GetID()))};
634  CWalletTx& wtxDebit = BuildAndLoadTxToWallet(vinDebit, voutDebit, wallet);
635 
636  // Validates (3)
637 
638  // First the debit tx
640  wtxDebit,
641  0, // CREDIT-ISMINE_ALL
642  0, // CREDIT-ISMINE_SPENDABLE
643  0, // AVAILABLE_CREDIT
644  nDebit, // DEBIT-ISMINE_ALL
645  nDebit // DEBIT-ISMINE_SPENDABLE
646  );
647 
648  // Secondly the prev credit tx update
649 
650  // One output spent, the other one not. Force available credit recalculation.
651  // If we don't request it, it will not happen.
652  BOOST_CHECK_EQUAL(wtxCredit.GetAvailableCredit(false), nCredit - nDebit);
654 
655 }
656 
int64_t CAmount
Amount in PIV (Can be negative)
Definition: amount.h:13
static std::unique_ptr< BerkeleyDatabase > CreateDummy()
Return object for accessing dummy database with no read/write capabilities.
Definition: db.h:132
static std::unique_ptr< BerkeleyDatabase > CreateMock()
Return object for accessing temporary in-memory database.
Definition: db.h:138
unsigned int nSize
number of used bytes of block file
Definition: chain.h:36
uint256 hashPrevBlock
Definition: block.h:28
uint256 hashMerkleRoot
Definition: block.h:29
uint256 GetHash() const
Definition: block.cpp:15
Definition: block.h:80
std::vector< CTransactionRef > vtx
Definition: block.h:83
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:139
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: chain.h:145
FlatFilePos GetBlockPos() const
Definition: chain.cpp:146
uint256 GetBlockHash() const
Definition: chain.h:215
int64_t GetBlockTimeMax() const
Definition: chain.h:217
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:151
const uint256 * phashBlock
pointer to the hash of the block, if any. memory is owned by this CBlockIndex
Definition: chain.h:142
CBlockIndex * Tip(bool fProofOfStake=false) const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:405
void SetTip(CBlockIndex *pindex)
Set/initialize a chain with a given tip.
Definition: chain.cpp:14
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:435
An encapsulated private key.
Definition: key.h:30
void MakeNewKey(bool fCompressed)
Generate a new private key using a cryptographic PRNG.
Definition: key.cpp:158
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:186
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:72
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:167
The basic transaction that is broadcasted on the network and contained in blocks.
Definition: transaction.h:244
const uint256 & GetHash() const
Definition: transaction.h:301
An input of a transaction.
Definition: transaction.h:94
CTxMemPoolEntry stores data about the corresponding transaction, as well as data about all in-mempool...
Definition: txmempool.h:55
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:471
bool exists(uint256 hash) const
Definition: txmempool.h:640
indexed_transaction_set mapTx
Definition: txmempool.h:472
An output of a transaction.
Definition: transaction.h:137
A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,...
Definition: wallet.h:577
std::map< uint256, CWalletTx > mapWallet
Definition: wallet.h:766
RecursiveMutex cs_wallet
Definition: wallet.h:720
void SetLastBlockProcessed(const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
Set last block processed height, currently only use in unit test.
Definition: wallet.h:698
std::map< CKeyID, CKeyMetadata > mapKeyMetadata
Definition: wallet.h:726
A transaction with a bunch of additional info that only the owner cares about.
Definition: wallet.h:325
CTransactionRef tx
Definition: wallet.h:364
Confirmation m_confirm
Definition: wallet.h:394
@ CREDIT
Definition: wallet.h:349
@ AVAILABLE_CREDIT
Definition: wallet.h:349
@ DEBIT
Definition: wallet.h:349
const uint256 & GetHash() const
Definition: wallet.h:561
bool fInMempool
Definition: wallet.h:356
UniValue params
Definition: server.h:47
bool setArray()
Definition: univalue.cpp:94
bool setObject()
Definition: univalue.cpp:101
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
void clear()
Definition: univalue.cpp:15
bool push_back(const UniValue &val)
Definition: univalue.cpp:108
bool pushKV(const std::string &key, const UniValue &val)
Definition: univalue.cpp:133
RAII object to check and reserve a wallet rescan.
Definition: wallet.h:1324
256-bit opaque blob.
Definition: uint256.h:138
BOOST_AUTO_TEST_SUITE_END()
DBErrors LoadWallet(bool &fFirstRunRet)
Definition: wallet.cpp:3584
bool SelectCoinsMinConf(const CAmount &nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector< COutput > vCoins, std::set< std::pair< const CWalletTx *, unsigned int > > &setCoinsRet, CAmount &nValueRet) const
Select coins until nTargetValue is reached.
Definition: wallet.cpp:2754
CAmount GetImmatureBalance() const
Definition: wallet.cpp:2266
CAmount GetUnconfirmedBalance(isminetype filter=ISMINE_SPENDABLE_ALL) const
Definition: wallet.cpp:2258
CallResult< CTxDestination > getNewAddress(const std::string &addressLabel, const std::string purpose, const CChainParams::Base58Type addrType=CChainParams::PUBKEY_ADDRESS)
Definition: wallet.cpp:196
bool IsAmountCached(AmountType type, const isminefilter &filter) const
Definition: wallet.cpp:1574
CBlockIndex * ScanForWalletTransactions(CBlockIndex *pindexStart, CBlockIndex *pindexStop, const WalletRescanReserver &reserver, bool fUpdate=false, bool fromStartup=false)
Scan the block chain (starting in pindexStart) for transactions from or to us.
Definition: wallet.cpp:1896
bool SetMinVersion(enum WalletFeature, WalletBatch *batch_in=nullptr, bool fExplicit=false)
signify that a particular wallet feature is now used. this may change nWalletVersion and nWalletMaxVe...
Definition: wallet.cpp:580
bool LoadToWallet(CWalletTx &wtxIn)
Definition: wallet.cpp:983
CAmount GetDebit(const isminefilter &filter) const
filter decides which addresses will count towards the debit
Definition: wallet.cpp:1580
const CWalletTx * GetWalletTx(const uint256 &hash) const
Definition: wallet.cpp:166
bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) override
Adds a key to the store, and saves it to disk.
Definition: wallet.cpp:252
CAmount GetCredit(const isminefilter &filter, bool recalculate=false) const
Definition: wallet.cpp:1616
bool SetupSPKM(bool newKeypool=true, bool memOnly=false)
Generates hd wallet //.
Definition: wallet.cpp:133
CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter &filter=ISMINE_SPENDABLE) const
Definition: wallet.cpp:1647
@ ISMINE_ALL
Definition: ismine.h:35
@ ISMINE_SPENDABLE
Definition: ismine.h:22
@ LOCK
Definition: lockunlock.h:16
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition: merkle.cpp:154
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:80
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
CScript GetScriptForRawPubKey(const CPubKey &pubKey)
Generate a P2PK script for the given pubkey.
Definition: standard.cpp:286
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a PIVX scriptPubKey for the given CTxDestination.
Definition: standard.cpp:278
boost::variant< CNoDestination, CKeyID, CScriptID, CExchangeKeyID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:72
A mutable version of CTransaction.
Definition: transaction.h:409
std::vector< CTxOut > vout
Definition: transaction.h:411
std::vector< CTxIn > vin
Definition: transaction.h:410
int nFile
Definition: flatfile.h:16
#define LOCK2(cs1, cs2)
Definition: sync.h:221
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:247
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:456
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
void SetMockTime(int64_t nMockTimeIn)
For testing.
Definition: utiltime.cpp:51
CTxMemPool mempool(::minRelayTxFee)
CBlockFileInfo * GetBlockFileInfo(size_t n)
Get block file info entry for one block file.
BlockMap mapBlockIndex
Definition: validation.cpp:82
CChain chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:84
std::vector< CWalletRef > vpwallets
Definition: wallet.cpp:33
@ FEATURE_PRE_SPLIT_KEYPOOL
Definition: wallet.h:116
CWalletTx & ReceiveBalanceWith(const std::vector< CTxOut > &vout, CWallet &wallet)
void removeTxFromMempool(CWalletTx &wtx)
std::set< std::pair< const CWalletTx *, unsigned int > > CoinSet
UniValue dumpwallet(const JSONRPCRequest &request)
Definition: rpcdump.cpp:497
CWalletTx & BuildAndLoadTxToWallet(const std::vector< CTxIn > &vin, const std::vector< CTxOut > &vout, CWallet &wallet)
void fakeMempoolInsertion(const CTransactionRef &wtxCredit)
UniValue importwallet(const JSONRPCRequest &request)
Definition: rpcdump.cpp:330
std::vector< std::unique_ptr< CWalletTx > > wtxn
void CheckBalances(const CWalletTx &tx, const CAmount &nCreditAll, const CAmount &nCreditSpendable, const CAmount &nAvailableCredit, const CAmount &nDebitAll, const CAmount &nDebitSpendable)
CBlockIndex * SimpleFakeMine(CWalletTx &wtx, CWallet &wallet, CBlockIndex *pprev=nullptr)
Mimic block creation.
BOOST_AUTO_TEST_CASE(coin_selection_tests)
#define RANDOM_REPEATS
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
#define RUN_TESTS
UniValue importmulti(const JSONRPCRequest &request)
Definition: rpcdump.cpp:960
@ DB_LOAD_OK
Definition: walletdb.h:51