Bitcoin Core  24.99.0
P2P Digital Currency
wallet_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2012-2022 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.
4 
5 #include <wallet/wallet.h>
6 
7 #include <future>
8 #include <memory>
9 #include <stdint.h>
10 #include <vector>
11 
12 #include <interfaces/chain.h>
13 #include <key_io.h>
14 #include <node/blockstorage.h>
15 #include <policy/policy.h>
16 #include <rpc/server.h>
17 #include <test/util/logging.h>
18 #include <test/util/setup_common.h>
19 #include <util/translation.h>
20 #include <validation.h>
21 #include <wallet/coincontrol.h>
22 #include <wallet/context.h>
23 #include <wallet/receive.h>
24 #include <wallet/spend.h>
25 #include <wallet/test/util.h>
27 
28 #include <boost/test/unit_test.hpp>
29 #include <univalue.h>
30 
33 
34 namespace wallet {
38 
39 // Ensure that fee levels defined in the wallet are at least as high
40 // as the default levels for node policy.
41 static_assert(DEFAULT_TRANSACTION_MINFEE >= DEFAULT_MIN_RELAY_TX_FEE, "wallet minimum fee is smaller than default relay fee");
42 static_assert(WALLET_INCREMENTAL_RELAY_FEE >= DEFAULT_INCREMENTAL_RELAY_FEE, "wallet incremental fee is smaller than default incremental relay fee");
43 
44 BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
45 
47 {
48  DatabaseOptions options;
50  DatabaseStatus status;
52  std::vector<bilingual_str> warnings;
53  auto database = MakeWalletDatabase("", options, status, error);
54  auto wallet = CWallet::Create(context, "", std::move(database), options.create_flags, error, warnings);
56  return wallet;
57 }
58 
59 static void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet)
60 {
62  wallet->m_chain_notifications_handler.reset();
63  UnloadWallet(std::move(wallet));
64 }
65 
66 static CMutableTransaction TestSimpleSpend(const CTransaction& from, uint32_t index, const CKey& key, const CScript& pubkey)
67 {
69  mtx.vout.push_back({from.vout[index].nValue - DEFAULT_TRANSACTION_MAXFEE, pubkey});
70  mtx.vin.push_back({CTxIn{from.GetHash(), index}});
71  FillableSigningProvider keystore;
72  keystore.AddKey(key);
73  std::map<COutPoint, Coin> coins;
74  coins[mtx.vin[0].prevout].out = from.vout[index];
75  std::map<int, bilingual_str> input_errors;
76  BOOST_CHECK(SignTransaction(mtx, &keystore, coins, SIGHASH_ALL, input_errors));
77  return mtx;
78 }
79 
80 static void AddKey(CWallet& wallet, const CKey& key)
81 {
82  LOCK(wallet.cs_wallet);
83  FlatSigningProvider provider;
84  std::string error;
85  std::unique_ptr<Descriptor> desc = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
86  assert(desc);
87  WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
88  if (!wallet.AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
89 }
90 
91 BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
92 {
93  // Cap last block file size, and mine new block in a new block file.
94  CBlockIndex* oldTip = WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain().Tip());
95  WITH_LOCK(::cs_main, m_node.chainman->m_blockman.GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE);
96  CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
97  CBlockIndex* newTip = WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain().Tip());
98 
99  // Verify ScanForWalletTransactions fails to read an unknown start block.
100  {
102  {
103  LOCK(wallet.cs_wallet);
104  LOCK(Assert(m_node.chainman)->GetMutex());
105  wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
106  wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
107  }
108  AddKey(wallet, coinbaseKey);
109  WalletRescanReserver reserver(wallet);
110  reserver.reserve();
111  CWallet::ScanResult result = wallet.ScanForWalletTransactions(/*start_block=*/{}, /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false);
116  BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 0);
117  }
118 
119  // Verify ScanForWalletTransactions picks up transactions in both the old
120  // and new block files.
121  {
123  {
124  LOCK(wallet.cs_wallet);
125  LOCK(Assert(m_node.chainman)->GetMutex());
126  wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
127  wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
128  }
129  AddKey(wallet, coinbaseKey);
130  WalletRescanReserver reserver(wallet);
131  std::chrono::steady_clock::time_point fake_time;
132  reserver.setNow([&] { fake_time += 60s; return fake_time; });
133  reserver.reserve();
134 
135  {
136  CBlockLocator locator;
137  BOOST_CHECK(!WalletBatch{wallet.GetDatabase()}.ReadBestBlock(locator));
138  BOOST_CHECK(locator.IsNull());
139  }
140 
141  CWallet::ScanResult result = wallet.ScanForWalletTransactions(/*start_block=*/oldTip->GetBlockHash(), /*start_height=*/oldTip->nHeight, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/true);
144  BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
145  BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
146  BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 100 * COIN);
147 
148  {
149  CBlockLocator locator;
150  BOOST_CHECK(WalletBatch{wallet.GetDatabase()}.ReadBestBlock(locator));
151  BOOST_CHECK(!locator.IsNull());
152  }
153  }
154 
155  // Prune the older block file.
156  int file_number;
157  {
158  LOCK(cs_main);
159  file_number = oldTip->GetBlockPos().nFile;
160  Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
161  }
162  UnlinkPrunedFiles({file_number});
163 
164  // Verify ScanForWalletTransactions only picks transactions in the new block
165  // file.
166  {
168  {
169  LOCK(wallet.cs_wallet);
170  LOCK(Assert(m_node.chainman)->GetMutex());
171  wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
172  wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
173  }
174  AddKey(wallet, coinbaseKey);
175  WalletRescanReserver reserver(wallet);
176  reserver.reserve();
177  CWallet::ScanResult result = wallet.ScanForWalletTransactions(/*start_block=*/oldTip->GetBlockHash(), /*start_height=*/oldTip->nHeight, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false);
180  BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
181  BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
182  BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 50 * COIN);
183  }
184 
185  // Prune the remaining block file.
186  {
187  LOCK(cs_main);
188  file_number = newTip->GetBlockPos().nFile;
189  Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
190  }
191  UnlinkPrunedFiles({file_number});
192 
193  // Verify ScanForWalletTransactions scans no blocks.
194  {
196  {
197  LOCK(wallet.cs_wallet);
198  LOCK(Assert(m_node.chainman)->GetMutex());
199  wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
200  wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
201  }
202  AddKey(wallet, coinbaseKey);
203  WalletRescanReserver reserver(wallet);
204  reserver.reserve();
205  CWallet::ScanResult result = wallet.ScanForWalletTransactions(/*start_block=*/oldTip->GetBlockHash(), /*start_height=*/oldTip->nHeight, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false);
207  BOOST_CHECK_EQUAL(result.last_failed_block, newTip->GetBlockHash());
210  BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 0);
211  }
212 }
213 
215 {
216  // Cap last block file size, and mine new block in a new block file.
217  CBlockIndex* oldTip = WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain().Tip());
218  WITH_LOCK(::cs_main, m_node.chainman->m_blockman.GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE);
219  CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
220  CBlockIndex* newTip = WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain().Tip());
221 
222  // Prune the older block file.
223  int file_number;
224  {
225  LOCK(cs_main);
226  file_number = oldTip->GetBlockPos().nFile;
227  Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
228  }
229  UnlinkPrunedFiles({file_number});
230 
231  // Verify importmulti RPC returns failure for a key whose creation time is
232  // before the missing block, and success for a key whose creation time is
233  // after.
234  {
235  const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
236  wallet->SetupLegacyScriptPubKeyMan();
237  WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
239  context.args = &m_args;
241  UniValue keys;
242  keys.setArray();
243  UniValue key;
244  key.setObject();
245  key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey())));
246  key.pushKV("timestamp", 0);
247  key.pushKV("internal", UniValue(true));
248  keys.push_back(key);
249  key.clear();
250  key.setObject();
251  CKey futureKey;
252  futureKey.MakeNewKey(true);
253  key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey())));
254  key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1);
255  key.pushKV("internal", UniValue(true));
256  keys.push_back(key);
257  JSONRPCRequest request;
258  request.context = &context;
259  request.params.setArray();
260  request.params.push_back(keys);
261 
262  UniValue response = importmulti().HandleRequest(request);
263  BOOST_CHECK_EQUAL(response.write(),
264  strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation "
265  "timestamp %d. There was an error reading a block from time %d, which is after or within %d "
266  "seconds of key creation, and could contain transactions pertaining to the key. As a result, "
267  "transactions and coins using this key may not appear in the wallet. This error could be caused "
268  "by pruning or data corruption (see bitcoind log for details) and could be dealt with by "
269  "downloading and rescanning the relevant blocks (see -reindex option and rescanblockchain "
270  "RPC).\"}},{\"success\":true}]",
271  0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
272  RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
273  }
274 }
275 
276 // Verify importwallet RPC starts rescan at earliest block with timestamp
277 // greater or equal than key birthday. Previously there was a bug where
278 // importwallet RPC would start the scan at the latest block with timestamp less
279 // than or equal to key birthday.
281 {
282  // Create two blocks with same timestamp to verify that importwallet rescan
283  // will pick up both blocks, not just the first.
284  const int64_t BLOCK_TIME = WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain().Tip()->GetBlockTimeMax() + 5);
285  SetMockTime(BLOCK_TIME);
286  m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
287  m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
288 
289  // Set key birthday to block time increased by the timestamp window, so
290  // rescan will start at the block time.
291  const int64_t KEY_TIME = BLOCK_TIME + TIMESTAMP_WINDOW;
292  SetMockTime(KEY_TIME);
293  m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
294 
295  std::string backup_file = fs::PathToString(m_args.GetDataDirNet() / "wallet.backup");
296 
297  // Import key into wallet and call dumpwallet to create backup file.
298  {
300  context.args = &m_args;
301  const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
302  {
303  auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
304  LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
305  spk_man->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
306  spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
307 
309  LOCK(Assert(m_node.chainman)->GetMutex());
310  wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
311  }
312  JSONRPCRequest request;
313  request.context = &context;
314  request.params.setArray();
315  request.params.push_back(backup_file);
316 
318  RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
319  }
320 
321  // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
322  // were scanned, and no prior blocks were scanned.
323  {
324  const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
325  LOCK(wallet->cs_wallet);
326  wallet->SetupLegacyScriptPubKeyMan();
327 
329  context.args = &m_args;
330  JSONRPCRequest request;
331  request.context = &context;
332  request.params.setArray();
333  request.params.push_back(backup_file);
335  LOCK(Assert(m_node.chainman)->GetMutex());
336  wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
338  RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
339 
340  BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
341  BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U);
342  for (size_t i = 0; i < m_coinbase_txns.size(); ++i) {
343  bool found = wallet->GetWalletTx(m_coinbase_txns[i]->GetHash());
344  bool expected = i >= 100;
345  BOOST_CHECK_EQUAL(found, expected);
346  }
347  }
348 }
349 
350 // Check that GetImmatureCredit() returns a newly calculated value instead of
351 // the cached value after a MarkDirty() call.
352 //
353 // This is a regression test written to verify a bugfix for the immature credit
354 // function. Similar tests probably should be written for the other credit and
355 // debit functions.
356 BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
357 {
359 
360  LOCK(wallet.cs_wallet);
361  LOCK(Assert(m_node.chainman)->GetMutex());
362  CWalletTx wtx{m_coinbase_txns.back(), TxStateConfirmed{m_node.chainman->ActiveChain().Tip()->GetBlockHash(), m_node.chainman->ActiveChain().Height(), /*index=*/0}};
363  wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
364  wallet.SetupDescriptorScriptPubKeyMans();
365 
366  wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
367 
368  // Call GetImmatureCredit() once before adding the key to the wallet to
369  // cache the current immature credit amount, which is 0.
371 
372  // Invalidate the cached value, add the key, and make sure a new immature
373  // credit amount is calculated.
374  wtx.MarkDirty();
375  AddKey(wallet, coinbaseKey);
377 }
378 
379 static int64_t AddTx(ChainstateManager& chainman, CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
380 {
382  TxState state = TxStateInactive{};
383  tx.nLockTime = lockTime;
384  SetMockTime(mockTime);
385  CBlockIndex* block = nullptr;
386  if (blockTime > 0) {
387  LOCK(cs_main);
388  auto inserted = chainman.BlockIndex().emplace(std::piecewise_construct, std::make_tuple(GetRandHash()), std::make_tuple());
389  assert(inserted.second);
390  const uint256& hash = inserted.first->first;
391  block = &inserted.first->second;
392  block->nTime = blockTime;
393  block->phashBlock = &hash;
394  state = TxStateConfirmed{hash, block->nHeight, /*index=*/0};
395  }
396  return wallet.AddToWallet(MakeTransactionRef(tx), state, [&](CWalletTx& wtx, bool /* new_tx */) {
397  // Assign wtx.m_state to simplify test and avoid the need to simulate
398  // reorg events. Without this, AddToWallet asserts false when the same
399  // transaction is confirmed in different blocks.
400  wtx.m_state = state;
401  return true;
402  })->nTimeSmart;
403 }
404 
405 // Simple test to verify assignment of CWalletTx::nSmartTime value. Could be
406 // expanded to cover more corner cases of smart time logic.
407 BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
408 {
409  // New transaction should use clock time if lower than block time.
410  BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 1, 100, 120), 100);
411 
412  // Test that updating existing transaction does not change smart time.
413  BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 1, 200, 220), 100);
414 
415  // New transaction should use clock time if there's no block time.
416  BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 2, 300, 0), 300);
417 
418  // New transaction should use block time if lower than clock time.
419  BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 3, 420, 400), 400);
420 
421  // New transaction should use latest entry time if higher than
422  // min(block time, clock time).
423  BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 4, 500, 390), 400);
424 
425  // If there are future entries, new transaction should use time of the
426  // newest entry that is no more than 300 seconds ahead of the clock time.
427  BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 5, 50, 600), 300);
428 }
429 
430 BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
431 {
432  CTxDestination dest = PKHash();
433  LOCK(m_wallet.cs_wallet);
434  WalletBatch batch{m_wallet.GetDatabase()};
435  m_wallet.SetAddressUsed(batch, dest, true);
436  m_wallet.SetAddressReceiveRequest(batch, dest, "0", "val_rr0");
437  m_wallet.SetAddressReceiveRequest(batch, dest, "1", "val_rr1");
438 
439  auto values = m_wallet.GetAddressReceiveRequests();
440  BOOST_CHECK_EQUAL(values.size(), 2U);
441  BOOST_CHECK_EQUAL(values[0], "val_rr0");
442  BOOST_CHECK_EQUAL(values[1], "val_rr1");
443 }
444 
445 // Test some watch-only LegacyScriptPubKeyMan methods by the procedure of loading (LoadWatchOnly),
446 // checking (HaveWatchOnly), getting (GetWatchPubKey) and removing (RemoveWatchOnly) a
447 // given PubKey, resp. its corresponding P2PK Script. Results of the impact on
448 // the address -> PubKey map is dependent on whether the PubKey is a point on the curve
449 static void TestWatchOnlyPubKey(LegacyScriptPubKeyMan* spk_man, const CPubKey& add_pubkey)
450 {
451  CScript p2pk = GetScriptForRawPubKey(add_pubkey);
452  CKeyID add_address = add_pubkey.GetID();
453  CPubKey found_pubkey;
454  LOCK(spk_man->cs_KeyStore);
455 
456  // all Scripts (i.e. also all PubKeys) are added to the general watch-only set
457  BOOST_CHECK(!spk_man->HaveWatchOnly(p2pk));
458  spk_man->LoadWatchOnly(p2pk);
459  BOOST_CHECK(spk_man->HaveWatchOnly(p2pk));
460 
461  // only PubKeys on the curve shall be added to the watch-only address -> PubKey map
462  bool is_pubkey_fully_valid = add_pubkey.IsFullyValid();
463  if (is_pubkey_fully_valid) {
464  BOOST_CHECK(spk_man->GetWatchPubKey(add_address, found_pubkey));
465  BOOST_CHECK(found_pubkey == add_pubkey);
466  } else {
467  BOOST_CHECK(!spk_man->GetWatchPubKey(add_address, found_pubkey));
468  BOOST_CHECK(found_pubkey == CPubKey()); // passed key is unchanged
469  }
470 
471  spk_man->RemoveWatchOnly(p2pk);
472  BOOST_CHECK(!spk_man->HaveWatchOnly(p2pk));
473 
474  if (is_pubkey_fully_valid) {
475  BOOST_CHECK(!spk_man->GetWatchPubKey(add_address, found_pubkey));
476  BOOST_CHECK(found_pubkey == add_pubkey); // passed key is unchanged
477  }
478 }
479 
480 // Cryptographically invalidate a PubKey whilst keeping length and first byte
481 static void PollutePubKey(CPubKey& pubkey)
482 {
483  std::vector<unsigned char> pubkey_raw(pubkey.begin(), pubkey.end());
484  std::fill(pubkey_raw.begin()+1, pubkey_raw.end(), 0);
485  pubkey = CPubKey(pubkey_raw);
486  assert(!pubkey.IsFullyValid());
487  assert(pubkey.IsValid());
488 }
489 
490 // Test watch-only logic for PubKeys
491 BOOST_AUTO_TEST_CASE(WatchOnlyPubKeys)
492 {
493  CKey key;
494  CPubKey pubkey;
495  LegacyScriptPubKeyMan* spk_man = m_wallet.GetOrCreateLegacyScriptPubKeyMan();
496 
497  BOOST_CHECK(!spk_man->HaveWatchOnly());
498 
499  // uncompressed valid PubKey
500  key.MakeNewKey(false);
501  pubkey = key.GetPubKey();
502  assert(!pubkey.IsCompressed());
503  TestWatchOnlyPubKey(spk_man, pubkey);
504 
505  // uncompressed cryptographically invalid PubKey
506  PollutePubKey(pubkey);
507  TestWatchOnlyPubKey(spk_man, pubkey);
508 
509  // compressed valid PubKey
510  key.MakeNewKey(true);
511  pubkey = key.GetPubKey();
512  assert(pubkey.IsCompressed());
513  TestWatchOnlyPubKey(spk_man, pubkey);
514 
515  // compressed cryptographically invalid PubKey
516  PollutePubKey(pubkey);
517  TestWatchOnlyPubKey(spk_man, pubkey);
518 
519  // invalid empty PubKey
520  pubkey = CPubKey();
521  TestWatchOnlyPubKey(spk_man, pubkey);
522 }
523 
525 {
526 public:
528  {
530  wallet = CreateSyncedWallet(*m_node.chain, WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return m_node.chainman->ActiveChain()), coinbaseKey);
531  }
532 
534  {
535  wallet.reset();
536  }
537 
539  {
540  CTransactionRef tx;
541  CCoinControl dummy;
542  {
543  constexpr int RANDOM_CHANGE_POSITION = -1;
544  auto res = CreateTransaction(*wallet, {recipient}, RANDOM_CHANGE_POSITION, dummy);
545  BOOST_CHECK(res);
546  tx = res->tx;
547  }
548  wallet->CommitTransaction(tx, {}, {});
549  CMutableTransaction blocktx;
550  {
551  LOCK(wallet->cs_wallet);
552  blocktx = CMutableTransaction(*wallet->mapWallet.at(tx->GetHash()).tx);
553  }
555 
556  LOCK(wallet->cs_wallet);
557  LOCK(Assert(m_node.chainman)->GetMutex());
558  wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
559  auto it = wallet->mapWallet.find(tx->GetHash());
560  BOOST_CHECK(it != wallet->mapWallet.end());
561  it->second.m_state = TxStateConfirmed{m_node.chainman->ActiveChain().Tip()->GetBlockHash(), m_node.chainman->ActiveChain().Height(), /*index=*/1};
562  return it->second;
563  }
564 
565  std::unique_ptr<CWallet> wallet;
566 };
567 
569 {
570  std::string coinbaseAddress = coinbaseKey.GetPubKey().GetID().ToString();
571 
572  // Confirm ListCoins initially returns 1 coin grouped under coinbaseKey
573  // address.
574  std::map<CTxDestination, std::vector<COutput>> list;
575  {
576  LOCK(wallet->cs_wallet);
577  list = ListCoins(*wallet);
578  }
579  BOOST_CHECK_EQUAL(list.size(), 1U);
580  BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
581  BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U);
582 
583  // Check initial balance from one mature coinbase transaction.
585 
586  // Add a transaction creating a change address, and confirm ListCoins still
587  // returns the coin associated with the change address underneath the
588  // coinbaseKey pubkey, even though the change address has a different
589  // pubkey.
590  AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, /*subtract_fee=*/false});
591  {
592  LOCK(wallet->cs_wallet);
593  list = ListCoins(*wallet);
594  }
595  BOOST_CHECK_EQUAL(list.size(), 1U);
596  BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
597  BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
598 
599  // Lock both coins. Confirm number of available coins drops to 0.
600  {
601  LOCK(wallet->cs_wallet);
603  }
604  for (const auto& group : list) {
605  for (const auto& coin : group.second) {
606  LOCK(wallet->cs_wallet);
607  wallet->LockCoin(coin.outpoint);
608  }
609  }
610  {
611  LOCK(wallet->cs_wallet);
613  }
614  // Confirm ListCoins still returns same result as before, despite coins
615  // being locked.
616  {
617  LOCK(wallet->cs_wallet);
618  list = ListCoins(*wallet);
619  }
620  BOOST_CHECK_EQUAL(list.size(), 1U);
621  BOOST_CHECK_EQUAL(std::get<PKHash>(list.begin()->first).ToString(), coinbaseAddress);
622  BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U);
623 }
624 
625 void TestCoinsResult(ListCoinsTest& context, OutputType out_type, CAmount amount,
626  std::map<OutputType, size_t>& expected_coins_sizes)
627 {
628  LOCK(context.wallet->cs_wallet);
629  util::Result<CTxDestination> dest = Assert(context.wallet->GetNewDestination(out_type, ""));
630  CWalletTx& wtx = context.AddTx(CRecipient{{GetScriptForDestination(*dest)}, amount, /*fSubtractFeeFromAmount=*/true});
631  CoinFilterParams filter;
632  filter.skip_locked = false;
633  CoinsResult available_coins = AvailableCoins(*context.wallet, nullptr, std::nullopt, filter);
634  // Lock outputs so they are not spent in follow-up transactions
635  for (uint32_t i = 0; i < wtx.tx->vout.size(); i++) context.wallet->LockCoin({wtx.GetHash(), i});
636  for (const auto& [type, size] : expected_coins_sizes) BOOST_CHECK_EQUAL(size, available_coins.coins[type].size());
637 }
638 
639 BOOST_FIXTURE_TEST_CASE(BasicOutputTypesTest, ListCoinsTest)
640 {
641  std::map<OutputType, size_t> expected_coins_sizes;
642  for (const auto& out_type : OUTPUT_TYPES) { expected_coins_sizes[out_type] = 0U; }
643 
644  // Verify our wallet has one usable coinbase UTXO before starting
645  // This UTXO is a P2PK, so it should show up in the Other bucket
646  expected_coins_sizes[OutputType::UNKNOWN] = 1U;
647  CoinsResult available_coins = WITH_LOCK(wallet->cs_wallet, return AvailableCoins(*wallet));
648  BOOST_CHECK_EQUAL(available_coins.Size(), expected_coins_sizes[OutputType::UNKNOWN]);
649  BOOST_CHECK_EQUAL(available_coins.coins[OutputType::UNKNOWN].size(), expected_coins_sizes[OutputType::UNKNOWN]);
650 
651  // We will create a self transfer for each of the OutputTypes and
652  // verify it is put in the correct bucket after running GetAvailablecoins
653  //
654  // For each OutputType, We expect 2 UTXOs in our wallet following the self transfer:
655  // 1. One UTXO as the recipient
656  // 2. One UTXO from the change, due to payment address matching logic
657 
658  for (const auto& out_type : OUTPUT_TYPES) {
659  if (out_type == OutputType::UNKNOWN) continue;
660  expected_coins_sizes[out_type] = 2U;
661  TestCoinsResult(*this, out_type, 1 * COIN, expected_coins_sizes);
662  }
663 }
664 
666 {
667  {
668  const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
669  wallet->SetupLegacyScriptPubKeyMan();
670  wallet->SetMinVersion(FEATURE_LATEST);
672  BOOST_CHECK(!wallet->TopUpKeyPool(1000));
673  BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, ""));
674  }
675  {
676  const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateDummyWalletDatabase());
677  LOCK(wallet->cs_wallet);
678  wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
679  wallet->SetMinVersion(FEATURE_LATEST);
681  BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, ""));
682  }
683 }
684 
685 // Explicit calculation which is used to test the wallet constant
686 // We get the same virtual size due to rounding(weight/4) for both use_max_sig values
687 static size_t CalculateNestedKeyhashInputSize(bool use_max_sig)
688 {
689  // Generate ephemeral valid pubkey
690  CKey key;
691  key.MakeNewKey(true);
692  CPubKey pubkey = key.GetPubKey();
693 
694  // Generate pubkey hash
695  uint160 key_hash(Hash160(pubkey));
696 
697  // Create inner-script to enter into keystore. Key hash can't be 0...
698  CScript inner_script = CScript() << OP_0 << std::vector<unsigned char>(key_hash.begin(), key_hash.end());
699 
700  // Create outer P2SH script for the output
701  uint160 script_id(Hash160(inner_script));
702  CScript script_pubkey = CScript() << OP_HASH160 << std::vector<unsigned char>(script_id.begin(), script_id.end()) << OP_EQUAL;
703 
704  // Add inner-script to key store and key to watchonly
705  FillableSigningProvider keystore;
706  keystore.AddCScript(inner_script);
707  keystore.AddKeyPubKey(key, pubkey);
708 
709  // Fill in dummy signatures for fee calculation.
710  SignatureData sig_data;
711 
712  if (!ProduceSignature(keystore, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, script_pubkey, sig_data)) {
713  // We're hand-feeding it correct arguments; shouldn't happen
714  assert(false);
715  }
716 
717  CTxIn tx_in;
718  UpdateInput(tx_in, sig_data);
719  return (size_t)GetVirtualTransactionInputSize(tx_in);
720 }
721 
723 {
726 }
727 
728 bool malformed_descriptor(std::ios_base::failure e)
729 {
730  std::string s(e.what());
731  return s.find("Missing checksum") != std::string::npos;
732 }
733 
735 {
736  std::vector<unsigned char> malformed_record;
737  CVectorWriter vw(0, 0, malformed_record, 0);
738  vw << std::string("notadescriptor");
739  vw << uint64_t{0};
740  vw << int32_t{0};
741  vw << int32_t{0};
742  vw << int32_t{1};
743 
744  SpanReader vr{0, 0, malformed_record};
745  WalletDescriptor w_desc;
746  BOOST_CHECK_EXCEPTION(vr >> w_desc, std::ios_base::failure, malformed_descriptor);
747 }
748 
768 {
769  m_args.ForceSetArg("-unsafesqlitesync", "1");
770  // Create new wallet with known key and unload it.
772  context.args = &m_args;
773  context.chain = m_node.chain.get();
774  auto wallet = TestLoadWallet(context);
775  CKey key;
776  key.MakeNewKey(true);
777  AddKey(*wallet, key);
778  TestUnloadWallet(std::move(wallet));
779 
780 
781  // Add log hook to detect AddToWallet events from rescans, blockConnected,
782  // and transactionAddedToMempool notifications
783  int addtx_count = 0;
784  DebugLogHelper addtx_counter("[default wallet] AddToWallet", [&](const std::string* s) {
785  if (s) ++addtx_count;
786  return false;
787  });
788 
789 
790  bool rescan_completed = false;
791  DebugLogHelper rescan_check("[default wallet] Rescan completed", [&](const std::string* s) {
792  if (s) rescan_completed = true;
793  return false;
794  });
795 
796 
797  // Block the queue to prevent the wallet receiving blockConnected and
798  // transactionAddedToMempool notifications, and create block and mempool
799  // transactions paying to the wallet
800  std::promise<void> promise;
802  promise.get_future().wait();
803  });
804  std::string error;
805  m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
806  auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
807  m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
808  auto mempool_tx = TestSimpleSpend(*m_coinbase_txns[1], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
809  BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error));
810 
811 
812  // Reload wallet and make sure new transactions are detected despite events
813  // being blocked
815  BOOST_CHECK(rescan_completed);
816  // AddToWallet events for block_tx and mempool_tx
817  BOOST_CHECK_EQUAL(addtx_count, 2);
818  {
819  LOCK(wallet->cs_wallet);
820  BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_tx.GetHash()), 1U);
821  BOOST_CHECK_EQUAL(wallet->mapWallet.count(mempool_tx.GetHash()), 1U);
822  }
823 
824 
825  // Unblock notification queue and make sure stale blockConnected and
826  // transactionAddedToMempool events are processed
827  promise.set_value();
829  // AddToWallet events for block_tx and mempool_tx events are counted a
830  // second time as the notification queue is processed
831  BOOST_CHECK_EQUAL(addtx_count, 4);
832 
833 
834  TestUnloadWallet(std::move(wallet));
835 
836 
837  // Load wallet again, this time creating new block and mempool transactions
838  // paying to the wallet as the wallet finishes loading and syncing the
839  // queue so the events have to be handled immediately. Releasing the wallet
840  // lock during the sync is a little artificial but is needed to avoid a
841  // deadlock during the sync and simulates a new block notification happening
842  // as soon as possible.
843  addtx_count = 0;
844  auto handler = HandleLoadWallet(context, [&](std::unique_ptr<interfaces::Wallet> wallet) {
845  BOOST_CHECK(rescan_completed);
846  m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
847  block_tx = TestSimpleSpend(*m_coinbase_txns[2], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
848  m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
849  mempool_tx = TestSimpleSpend(*m_coinbase_txns[3], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
850  BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, false, error));
852  });
854  BOOST_CHECK_EQUAL(addtx_count, 2);
855  {
856  LOCK(wallet->cs_wallet);
857  BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_tx.GetHash()), 1U);
858  BOOST_CHECK_EQUAL(wallet->mapWallet.count(mempool_tx.GetHash()), 1U);
859  }
860 
861 
862  TestUnloadWallet(std::move(wallet));
863 }
864 
865 BOOST_FIXTURE_TEST_CASE(CreateWalletWithoutChain, BasicTestingSetup)
866 {
868  context.args = &m_args;
869  auto wallet = TestLoadWallet(context);
871  UnloadWallet(std::move(wallet));
872 }
873 
875 {
876  m_args.ForceSetArg("-unsafesqlitesync", "1");
878  context.args = &m_args;
879  context.chain = m_node.chain.get();
880  auto wallet = TestLoadWallet(context);
881  CKey key;
882  key.MakeNewKey(true);
883  AddKey(*wallet, key);
884 
885  std::string error;
886  m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
887  auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));
888  CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
889 
891 
892  {
893  auto block_hash = block_tx.GetHash();
894  auto prev_tx = m_coinbase_txns[0];
895 
896  LOCK(wallet->cs_wallet);
897  BOOST_CHECK(wallet->HasWalletSpend(prev_tx));
898  BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 1u);
899 
900  std::vector<uint256> vHashIn{ block_hash }, vHashOut;
901  BOOST_CHECK_EQUAL(wallet->ZapSelectTx(vHashIn, vHashOut), DBErrors::LOAD_OK);
902 
903  BOOST_CHECK(!wallet->HasWalletSpend(prev_tx));
904  BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 0u);
905  }
906 
907  TestUnloadWallet(std::move(wallet));
908 }
909 
911 {
912 public:
913  Status Next(DataStream& key, DataStream& value) override { return Status::FAIL; }
914 };
915 
917 class FailBatch : public DatabaseBatch
918 {
919 private:
920  bool m_pass{true};
921  bool ReadKey(DataStream&& key, DataStream& value) override { return m_pass; }
922  bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override { return m_pass; }
923  bool EraseKey(DataStream&& key) override { return m_pass; }
924  bool HasKey(DataStream&& key) override { return m_pass; }
925 
926 public:
927  explicit FailBatch(bool pass) : m_pass(pass) {}
928  void Flush() override {}
929  void Close() override {}
930 
931  std::unique_ptr<DatabaseCursor> GetNewCursor() override { return std::make_unique<FailCursor>(); }
932  bool TxnBegin() override { return false; }
933  bool TxnCommit() override { return false; }
934  bool TxnAbort() override { return false; }
935 };
936 
939 {
940 public:
941  bool m_pass{true}; // false when this db should fail
942 
943  void Open() override {};
944  void AddRef() override {}
945  void RemoveRef() override {}
946  bool Rewrite(const char* pszSkip=nullptr) override { return true; }
947  bool Backup(const std::string& strDest) const override { return true; }
948  void Close() override {}
949  void Flush() override {}
950  bool PeriodicFlush() override { return true; }
951  void IncrementUpdateCounter() override { ++nUpdateCounter; }
952  void ReloadDbEnv() override {}
953  std::string Filename() override { return "faildb"; }
954  std::string Format() override { return "faildb"; }
955  std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<FailBatch>(m_pass); }
956 };
957 
962 BOOST_FIXTURE_TEST_CASE(wallet_sync_tx_invalid_state_test, TestingSetup)
963 {
964  CWallet wallet(m_node.chain.get(), "", std::make_unique<FailDatabase>());
965  {
966  LOCK(wallet.cs_wallet);
967  wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
968  wallet.SetupDescriptorScriptPubKeyMans();
969  }
970 
971  // Add tx to wallet
972  const auto& op_dest = wallet.GetNewDestination(OutputType::BECH32M, "");
973  BOOST_ASSERT(op_dest);
974 
976  mtx.vout.push_back({COIN, GetScriptForDestination(*op_dest)});
977  mtx.vin.push_back(CTxIn(g_insecure_rand_ctx.rand256(), 0));
978  const auto& tx_id_to_spend = wallet.AddToWallet(MakeTransactionRef(mtx), TxStateInMempool{})->GetHash();
979 
980  {
981  // Cache and verify available balance for the wtx
982  LOCK(wallet.cs_wallet);
983  const CWalletTx* wtx_to_spend = wallet.GetWalletTx(tx_id_to_spend);
985  }
986 
987  // Now the good case:
988  // 1) Add a transaction that spends the previously created transaction
989  // 2) Verify that the available balance of this new tx and the old one is updated (prev tx is marked dirty)
990 
991  mtx.vin.clear();
992  mtx.vin.push_back(CTxIn(tx_id_to_spend, 0));
993  wallet.transactionAddedToMempool(MakeTransactionRef(mtx));
994  const uint256& good_tx_id = mtx.GetHash();
995 
996  {
997  // Verify balance update for the new tx and the old one
998  LOCK(wallet.cs_wallet);
999  const CWalletTx* new_wtx = wallet.GetWalletTx(good_tx_id);
1001 
1002  // Now the old wtx
1003  const CWalletTx* wtx_to_spend = wallet.GetWalletTx(tx_id_to_spend);
1005  }
1006 
1007  // Now the bad case:
1008  // 1) Make db always fail
1009  // 2) Try to add a transaction that spends the previously created transaction and
1010  // verify that we are not moving forward if the wallet cannot store it
1011  static_cast<FailDatabase&>(wallet.GetDatabase()).m_pass = false;
1012  mtx.vin.clear();
1013  mtx.vin.push_back(CTxIn(good_tx_id, 0));
1014  BOOST_CHECK_EXCEPTION(wallet.transactionAddedToMempool(MakeTransactionRef(mtx)),
1015  std::runtime_error,
1016  HasReason("DB error adding transaction to wallet, write failed"));
1017 }
1018 
1020 } // namespace wallet
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
static constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition: amount.h:15
node::NodeContext m_node
Definition: bitcoin-gui.cpp:37
static constexpr int64_t TIMESTAMP_WINDOW
Timestamp window used as a grace period by code that compares external timestamps (such as timestamps...
Definition: chain.h:32
#define Assert(val)
Identity function.
Definition: check.h:73
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:151
uint32_t nTime
Definition: chain.h:205
uint256 GetBlockHash() const
Definition: chain.h:259
int64_t GetBlockTimeMax() const
Definition: chain.h:284
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:163
FlatFilePos GetBlockPos() const EXCLUSIVE_LOCKS_REQUIRED(
Definition: chain.h:224
const uint256 * phashBlock
pointer to the hash of the block, if any. Memory is owned by this CBlockIndex
Definition: chain.h:154
An encapsulated private key.
Definition: key.h:27
void MakeNewKey(bool fCompressed)
Generate a new private key using a cryptographic PRNG.
Definition: key.cpp:160
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:187
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:24
An encapsulated public key.
Definition: pubkey.h:34
const unsigned char * end() const
Definition: pubkey.h:115
bool IsCompressed() const
Check whether this is a compressed public key.
Definition: pubkey.h:198
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:164
bool IsValid() const
Definition: pubkey.h:189
bool IsFullyValid() const
fully validate whether this is a valid public key (more expensive than IsValid())
Definition: pubkey.cpp:296
const unsigned char * begin() const
Definition: pubkey.h:114
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:411
The basic transaction that is broadcasted on the network and contained in blocks.
Definition: transaction.h:295
const std::vector< CTxOut > vout
Definition: transaction.h:306
const uint256 & GetHash() const
Definition: transaction.h:337
An input of a transaction.
Definition: transaction.h:75
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:866
node::BlockMap & BlockIndex() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:1053
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:186
uint256 rand256() noexcept
generate a random uint256.
Definition: random.cpp:605
Fillable signing provider that keeps keys in an address->secret map.
virtual bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey)
virtual bool AddCScript(const CScript &redeemScript)
virtual bool AddKey(const CKey &key)
RecursiveMutex cs_KeyStore
BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
Definition: setup_common.h:220
UniValue params
Definition: request.h:33
std::any context
Definition: request.h:38
UniValue HandleRequest(const JSONRPCRequest &request) const
Definition: util.cpp:547
Minimal stream for reading from an existing byte array by Span.
Definition: streams.h:135
void push_back(UniValue val)
Definition: univalue.cpp:104
void setArray()
Definition: univalue.cpp:92
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
void clear()
Definition: univalue.cpp:18
void setObject()
Definition: univalue.cpp:98
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:126
constexpr bool IsNull() const
Definition: uint256.h:41
constexpr unsigned char * end()
Definition: uint256.h:68
constexpr unsigned char * begin()
Definition: uint256.h:67
160-bit opaque blob.
Definition: uint256.h:94
256-bit opaque blob.
Definition: uint256.h:105
Coin Control Features.
Definition: coincontrol.h:30
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:237
static std::shared_ptr< CWallet > Create(WalletContext &context, const std::string &name, std::unique_ptr< WalletDatabase > database, uint64_t wallet_creation_flags, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: wallet.cpp:2910
A transaction with a bunch of additional info that only the owner cares about.
Definition: transaction.h:138
CTransactionRef tx
Definition: transaction.h:219
RAII class that provides access to a WalletDatabase.
Definition: db.h:46
RAII class that provides access to a FailDatabase.
std::unique_ptr< DatabaseCursor > GetNewCursor() override
bool TxnBegin() override
bool TxnAbort() override
bool WriteKey(DataStream &&key, DataStream &&value, bool overwrite=true) override
bool ReadKey(DataStream &&key, DataStream &value) override
FailBatch(bool pass)
void Close() override
void Flush() override
bool TxnCommit() override
bool HasKey(DataStream &&key) override
bool EraseKey(DataStream &&key) override
Status Next(DataStream &key, DataStream &value) override
A dummy WalletDatabase that does nothing, only fails if needed.
void ReloadDbEnv() override
std::string Filename() override
Return path to main database file for logs and error messages.
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a DatabaseBatch connected to this database.
void Open() override
Open the database if it is not already opened.
void RemoveRef() override
Indicate that database user has stopped using the database and that it could be flushed or closed.
std::string Format() override
void IncrementUpdateCounter() override
bool Rewrite(const char *pszSkip=nullptr) override
Rewrite the entire database on disk, with the exception of key pszSkip if non-zero.
void AddRef() override
Indicate the a new database user has began using the database.
void Flush() override
Make sure all changes are flushed to database file.
void Close() override
Flush to the database file and close the database.
bool PeriodicFlush() override
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
bool HaveWatchOnly(const CScript &dest) const
Returns whether the watch-only script is in the wallet.
bool LoadWatchOnly(const CScript &dest)
Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const
Fetches a pubkey from mapWatchKeys if it exists there.
bool RemoveWatchOnly(const CScript &dest)
Remove a watch only script from the keystore.
std::unique_ptr< CWallet > wallet
CWalletTx & AddTx(CRecipient recipient)
Access to the wallet database.
Definition: walletdb.h:189
An instance of this class represents one database.
Definition: db.h:123
std::atomic< unsigned int > nUpdateCounter
Definition: db.h:167
Descriptor with some wallet metadata.
Definition: walletutil.h:77
RAII object to check and reserve a wallet rescan.
Definition: wallet.h:957
void setNow(NowFn now)
Definition: wallet.h:987
bool reserve(bool with_passphrase=false)
Definition: wallet.h:967
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
BOOST_AUTO_TEST_SUITE_END()
std::unique_ptr< Descriptor > Parse(const std::string &descriptor, FlatSigningProvider &out, std::string &error, bool require_checksum)
Parse a descriptor string.
uint160 Hash160(const T1 &in1)
Compute the 160-bit hash an object.
Definition: hash.h:93
@ SIGHASH_ALL
Definition: interpreter.h:28
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:216
bool error(const char *fmt, const Args &... args)
Definition: logging.h:261
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:150
void UnlinkPrunedFiles(const std::set< int > &setFilesToPrune)
Actually unlink the specified files.
static const unsigned int MAX_BLOCKFILE_SIZE
The maximum size of a blk?????.dat file (since 0.8)
Definition: blockstorage.h:45
Definition: node.h:39
static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE
Pre-calculated constants for input size estimation in virtual size
Definition: wallet.h:117
void UnloadWallet(std::shared_ptr< CWallet > &&wallet)
Explicitly unload and delete the wallet.
Definition: wallet.cpp:211
static CMutableTransaction TestSimpleSpend(const CTransaction &from, uint32_t index, const CKey &key, const CScript &pubkey)
CAmount CachedTxGetAvailableCredit(const CWallet &wallet, const CWalletTx &wtx, const isminefilter &filter)
Definition: receive.cpp:159
Balance GetBalance(const CWallet &wallet, const int min_depth, bool avoid_reuse)
Definition: receive.cpp:293
CAmount GetAvailableBalance(const CWallet &wallet, const CCoinControl *coinControl)
Definition: spend.cpp:359
static void PollutePubKey(CPubKey &pubkey)
std::unique_ptr< WalletDatabase > CreateMockWalletDatabase(DatabaseOptions &options)
Return object for accessing temporary in-memory database.
Definition: walletdb.cpp:1242
constexpr CAmount DEFAULT_TRANSACTION_MAXFEE
-maxtxfee default
Definition: wallet.h:111
RPCHelpMan importwallet()
Definition: backup.cpp:494
BOOST_FIXTURE_TEST_CASE(wallet_coinsresult_test, BasicTestingSetup)
RPCHelpMan importmulti()
Definition: backup.cpp:1246
std::unique_ptr< interfaces::Handler > HandleLoadWallet(WalletContext &context, LoadWalletFn load_wallet)
Definition: wallet.cpp:172
std::unique_ptr< CWallet > CreateSyncedWallet(interfaces::Chain &chain, CChain &cchain, const CKey &key)
Definition: util.cpp:17
static const CAmount DEFAULT_TRANSACTION_MINFEE
-mintxfee default
Definition: wallet.h:84
util::Result< CreatedTransactionResult > CreateTransaction(CWallet &wallet, const std::vector< CRecipient > &vecSend, int change_pos, const CCoinControl &coin_control, bool sign)
Create a new transaction paying the recipients with a set of coins selected by SelectCoins(); Also cr...
Definition: spend.cpp:1136
std::unique_ptr< WalletDatabase > MakeWalletDatabase(const std::string &name, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error_string)
Definition: wallet.cpp:2886
static std::shared_ptr< CWallet > TestLoadWallet(WalletContext &context)
void NotifyWalletLoaded(WalletContext &context, const std::shared_ptr< CWallet > &wallet)
Definition: wallet.cpp:179
static int64_t AddTx(ChainstateManager &chainman, CWallet &wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
RPCHelpMan dumpwallet()
Definition: backup.cpp:687
std::map< CTxDestination, std::vector< COutput > > ListCoins(const CWallet &wallet)
Return list of available coins and locked coins grouped by non-change output address.
Definition: spend.cpp:385
static void TestWatchOnlyPubKey(LegacyScriptPubKeyMan *spk_man, const CPubKey &add_pubkey)
@ ISMINE_SPENDABLE
Definition: ismine.h:44
CoinsResult AvailableCoinsListUnspent(const CWallet &wallet, const CCoinControl *coinControl, CoinFilterParams params)
Wrapper function for AvailableCoins which skips the feerate and CoinFilterParams::only_spendable para...
Definition: spend.cpp:353
std::unique_ptr< WalletDatabase > CreateDummyWalletDatabase()
Return object for accessing dummy database with no read/write capabilities.
Definition: walletdb.cpp:1236
static void AddKey(CWallet &wallet, const CKey &key)
bool AddWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet)
Definition: wallet.cpp:112
static const CAmount WALLET_INCREMENTAL_RELAY_FEE
minimum recommended increment for replacement txs
Definition: wallet.h:98
@ FEATURE_LATEST
Definition: walletutil.h:30
static void TestUnloadWallet(std::shared_ptr< CWallet > &&wallet)
CAmount CachedTxGetImmatureCredit(const CWallet &wallet, const CWalletTx &wtx, const isminefilter &filter)
Definition: receive.cpp:148
bool malformed_descriptor(std::ios_base::failure e)
std::shared_ptr< CWallet > CreateWallet(WalletContext &context, const std::string &name, std::optional< bool > load_on_start, DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: wallet.cpp:340
void TestCoinsResult(ListCoinsTest &context, OutputType out_type, CAmount amount, std::map< OutputType, size_t > &expected_coins_sizes)
BOOST_AUTO_TEST_CASE(bnb_search_test)
@ WALLET_FLAG_DESCRIPTORS
Indicate that this wallet supports DescriptorScriptPubKeyMan.
Definition: walletutil.h:66
@ WALLET_FLAG_DISABLE_PRIVATE_KEYS
Definition: walletutil.h:51
static size_t CalculateNestedKeyhashInputSize(bool use_max_sig)
std::variant< TxStateConfirmed, TxStateInMempool, TxStateConflicted, TxStateInactive, TxStateUnrecognized > TxState
All possible CWalletTx states.
Definition: transaction.h:66
DatabaseStatus
Definition: db.h:239
bool RemoveWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet, std::optional< bool > load_on_start, std::vector< bilingual_str > &warnings)
Definition: wallet.cpp:124
CoinsResult AvailableCoins(const CWallet &wallet, const CCoinControl *coinControl, std::optional< CFeeRate > feerate, const CoinFilterParams &params)
Populate the CoinsResult struct with vectors of available COutputs, organized by OutputType.
Definition: spend.cpp:204
std::shared_ptr< CWallet > wallet
WalletContext context
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
OutputType
Definition: outputtype.h:17
static constexpr auto OUTPUT_TYPES
Definition: outputtype.h:25
int64_t GetVirtualTransactionInputSize(const CTxIn &txin, int64_t nSigOpCost, unsigned int bytes_per_sigop)
Definition: policy.cpp:305
static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE
Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or rep...
Definition: policy.h:35
static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE
Default for -minrelaytxfee, minimum relay fee for transactions.
Definition: policy.h:57
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:422
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:421
uint256 GetRandHash() noexcept
Definition: random.cpp:591
void SignTransaction(CMutableTransaction &mtx, const SigningProvider *keystore, const std::map< COutPoint, Coin > &coins, const UniValue &hashType, UniValue &result)
Sign a transaction with the given keystore and previous transactions.
bool(* handler)(const std::any &context, HTTPRequest *req, const std::string &strReq)
Definition: rest.cpp:988
@ OP_EQUAL
Definition: script.h:142
static const int64_t values[]
A selection of numbers that do not trigger int64_t overflow when added/subtracted.
FastRandomContext g_insecure_rand_ctx
This global and the helpers that use it are not thread-safe.
bool ProduceSignature(const SigningProvider &provider, const BaseSignatureCreator &creator, const CScript &fromPubKey, SignatureData &sigdata)
Produce a script signature using a generic signature creator.
Definition: sign.cpp:470
void UpdateInput(CTxIn &input, const SignatureData &data)
Definition: sign.cpp:643
const BaseSignatureCreator & DUMMY_MAXIMUM_SIGNATURE_CREATOR
A signature creator that just produces 72-byte empty signatures.
Definition: sign.cpp:734
const BaseSignatureCreator & DUMMY_SIGNATURE_CREATOR
A signature creator that just produces 71-byte empty signatures.
Definition: sign.cpp:733
CScript GetScriptForRawPubKey(const CPubKey &pubKey)
Generate a P2PK script for the given pubkey.
Definition: standard.cpp:339
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:334
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:149
Basic testing setup.
Definition: setup_common.h:79
node::NodeContext m_node
Definition: setup_common.h:80
Describes a place in the block chain to another node such that if the other node doesn't have the sam...
Definition: block.h:121
bool IsNull() const
Definition: block.h:141
A mutable version of CTransaction.
Definition: transaction.h:380
uint256 GetHash() const
Compute the hash of this CMutableTransaction.
Definition: transaction.cpp:68
std::vector< CTxOut > vout
Definition: transaction.h:382
std::vector< CTxIn > vin
Definition: transaction.h:381
int nFile
Definition: flatfile.h:16
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Definition: setup_common.h:128
CBlock CreateAndProcessBlock(const std::vector< CMutableTransaction > &txns, const CScript &scriptPubKey, Chainstate *chainstate=nullptr)
Create a new block with just given transactions, coinbase paying to scriptPubKey, and try to add it t...
Testing setup that configures a complete environment.
Definition: setup_common.h:102
Bilingual messages:
Definition: translation.h:18
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:54
std::unique_ptr< interfaces::Chain > chain
Definition: context.h:57
std::optional< int > last_scanned_height
Definition: wallet.h:533
uint256 last_scanned_block
Hash and height of most recent block that was successfully scanned.
Definition: wallet.h:532
uint256 last_failed_block
Height of the most recent block that could not be scanned due to read errors or pruning.
Definition: wallet.h:539
enum wallet::CWallet::ScanResult::@17 status
COutputs available for spending, stored by OutputType.
Definition: spend.h:40
size_t Size() const
The following methods are provided so that CoinsResult can mimic a vector, i.e., methods can work wit...
Definition: spend.cpp:85
std::map< OutputType, std::vector< COutput > > coins
Definition: spend.h:41
uint64_t create_flags
Definition: db.h:229
State of transaction confirmed in a block.
Definition: transaction.h:24
State of transaction added to mempool.
Definition: transaction.h:33
State of transaction not confirmed or conflicting with a known block and not in the mempool.
Definition: transaction.h:48
WalletContext struct containing references to state shared between CWallet instances,...
Definition: context.h:35
interfaces::Chain * chain
Definition: context.h:36
ArgsManager * args
Definition: context.h:37
#define LOCK2(cs1, cs2)
Definition: sync.h:259
#define LOCK(cs)
Definition: sync.h:258
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:302
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:89
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1162
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
assert(!tx.IsCoinBase())
void CallFunctionInValidationInterfaceQueue(std::function< void()> func)
Pushes a function to callback onto the notification queue, guaranteeing any callbacks generated prior...
void SyncWithValidationInterfaceQueue()
This is a synonym for the following, which asserts certain locks are not held: std::promise<void> pro...
std::shared_ptr< CWallet > m_wallet
Definition: interfaces.cpp:522