Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
501for (const auto& out_type : OUTPUT_TYPES) { expected_coins_sizes[out_type] = 0U; }503// Verify our wallet has one usable coinbase UTXO before starting504// This UTXO is a P2PK, so it should show up in the Other bucket505expected_coins_sizes[OutputType::UNKNOWN] = 1U;506CoinsResult available_coins = WITH_LOCK(wallet->cs_wallet, return AvailableCoins(*wallet));507BOOST_CHECK_EQUAL(available_coins.Size(), expected_coins_sizes[OutputType::UNKNOWN]);508BOOST_CHECK_EQUAL(available_coins.coins[OutputType::UNKNOWN].size(), expected_coins_sizes[OutputType::UNKNOWN]);510// We will create a self transfer for each of the OutputTypes and511// verify it is put in the correct bucket after running GetAvailablecoins512//513// For each OutputType, We expect 2 UTXOs in our wallet following the self transfer:514// 1. One UTXO as the recipient515// 2. One UTXO from the change, due to payment address matching logic517for (const auto& out_type : OUTPUT_TYPES) {518if (out_type == OutputType::UNKNOWN) continue;519expected_coins_sizes[out_type] = 2U;520TestCoinsResult(*this, out_type, 1 * COIN, expected_coins_sizes);521}522}524BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)525{526const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());527LOCK(wallet->cs_wallet);528wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);529wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);530BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, ""));531}533// Explicit calculation which is used to test the wallet constant534// We get the same virtual size due to rounding(weight/4) for both use_max_sig values535static size_t CalculateNestedKeyhashInputSize(bool use_max_sig)536{537// Generate ephemeral valid pubkey538CKey key = GenerateRandomKey();539CPubKey pubkey = key.GetPubKey();541// Generate pubkey hash542uint160 key_hash(Hash160(pubkey));544// Create inner-script to enter into keystore. Key hash can't be 0...545CScript inner_script = CScript() << OP_0 << std::vector<unsigned char>(key_hash.begin(), key_hash.end());547// Create outer P2SH script for the output548uint160 script_id(Hash160(inner_script));549CScript script_pubkey = CScript() << OP_HASH160 << std::vector<unsigned char>(script_id.begin(), script_id.end()) << OP_EQUAL;551// Add inner-script to key store and key to watchonly552FillableSigningProvider keystore;553keystore.AddCScript(inner_script);554keystore.AddKeyPubKey(key, pubkey);556// Fill in dummy signatures for fee calculation.557SignatureData sig_data;559if (!ProduceSignature(keystore, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, script_pubkey, sig_data)) {560// We're hand-feeding it correct arguments; shouldn't happen561assert(false);562}564CTxIn tx_in;565UpdateInput(tx_in, sig_data);566return (size_t)GetVirtualTransactionInputSize(tx_in);567}569BOOST_FIXTURE_TEST_CASE(dummy_input_size_test, TestChain100Setup)570{571BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(false), DUMMY_NESTED_P2WPKH_INPUT_SIZE);572BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(true), DUMMY_NESTED_P2WPKH_INPUT_SIZE);573}575bool malformed_descriptor(std::ios_base::failure e)576{577std::string s(e.what());578return s.find("Missing checksum") != std::string::npos;579}581BOOST_FIXTURE_TEST_CASE(wallet_descriptor_test, BasicTestingSetup)582{583std::vector<unsigned char> malformed_record;584VectorWriter vw{malformed_record, 0};585vw << std::string("notadescriptor");586vw << uint64_t{0};587vw << int32_t{0};588vw << int32_t{0};589vw << int32_t{1};591SpanReader vr{malformed_record};592std::optional<WalletDescriptor> w_desc;593BOOST_CHECK_EXCEPTION(w_desc.emplace(WalletDescriptor::FromStream(deserialize, vr)), std::ios_base::failure, malformed_descriptor);594}596//! Test CWallet::CreateNew() and its behavior handling potential race597//! conditions if it's called the same time an incoming transaction shows up in598//! the mempool or a new block.599//!600//! It isn't possible to verify there aren't race condition in every case, so601//! this test just checks two specific cases and ensures that timing of602//! notifications in these cases doesn't prevent the wallet from detecting603//! transactions.604//!605//! In the first case, block and mempool transactions are created before the606//! wallet is loaded, but notifications about these transactions are delayed607//! until after it is loaded. The notifications are superfluous in this case, so608//! the test verifies the transactions are detected before they arrive.609//!610//! In the second case, block and mempool transactions are created after the611//! wallet rescan and notifications are immediately synced, to verify the wallet612//! must already have a handler in place for them, and there's no gap after613//! rescanning where new transactions in new blocks could be lost.614BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)615{616m_args.ForceSetArg("-unsafesqlitesync", "1");617// Create new wallet with known key and unload it.618WalletContext context;619context.args = &m_args;620context.chain = m_node.chain.get();621auto wallet = TestCreateWallet(context);622CKey key = GenerateRandomKey();623AddKey(*wallet, key);624TestUnloadWallet(std::move(wallet));627// Add log hook to detect AddToWallet events from rescans, blockConnected,628// and transactionAddedToMempool notifications629int addtx_count = 0;630DebugLogHelper addtx_counter("[default wallet] AddToWallet", [&](const std::string* s) {631if (s) ++addtx_count;632return false;633});636bool rescan_completed = false;637DebugLogHelper rescan_check("[default wallet] Rescan completed", [&](const std::string* s) {638if (s) rescan_completed = true;639return false;640});643// Block the queue to prevent the wallet receiving blockConnected and644// transactionAddedToMempool notifications, and create block and mempool645// transactions paying to the wallet646std::promise<void> promise;647m_node.validation_signals->CallFunctionInValidationInterfaceQueue([&promise] {648promise.get_future().wait();649});650std::string error;651m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);652auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));653m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);654auto mempool_tx = TestSimpleSpend(*m_coinbase_txns[1], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));655BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, node::TxBroadcast::MEMPOOL_NO_BROADCAST, error));658// Reload wallet and make sure new transactions are detected despite events659// being blocked660// Loading will also ask for current mempool transactions661wallet = TestLoadWallet(context);662BOOST_CHECK(rescan_completed);663// AddToWallet events for block_tx and mempool_tx (x2)664BOOST_CHECK_EQUAL(addtx_count, 3);665{666LOCK(wallet->cs_wallet);667BOOST_CHECK(wallet->mapWallet.contains(block_tx.GetHash()));668BOOST_CHECK(wallet->mapWallet.contains(mempool_tx.GetHash()));669}672// Unblock notification queue and make sure stale blockConnected and673// transactionAddedToMempool events are processed674promise.set_value();675m_node.validation_signals->SyncWithValidationInterfaceQueue();676// AddToWallet events for block_tx and mempool_tx events are counted a677// second time as the notification queue is processed678BOOST_CHECK_EQUAL(addtx_count, 5);681TestUnloadWallet(std::move(wallet));684// Load wallet again, this time creating new block and mempool transactions685// paying to the wallet as the wallet finishes loading and syncing the686// queue so the events have to be handled immediately. Releasing the wallet687// lock during the sync is a little artificial but is needed to avoid a688// deadlock during the sync and simulates a new block notification happening689// as soon as possible.690addtx_count = 0;691auto handler = HandleLoadWallet(context, [&](std::unique_ptr<interfaces::Wallet> wallet) {692BOOST_CHECK(rescan_completed);693m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);694block_tx = TestSimpleSpend(*m_coinbase_txns[2], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));695m_coinbase_txns.push_back(CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);696mempool_tx = TestSimpleSpend(*m_coinbase_txns[3], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));697BOOST_CHECK(m_node.chain->broadcastTransaction(MakeTransactionRef(mempool_tx), DEFAULT_TRANSACTION_MAXFEE, node::TxBroadcast::MEMPOOL_NO_BROADCAST, error));698m_node.validation_signals->SyncWithValidationInterfaceQueue();699});700wallet = TestLoadWallet(context);701// Since mempool transactions are requested at the end of loading, there will702// be 2 additional AddToWallet calls, one from the previous test, and a duplicate for mempool_tx703BOOST_CHECK_EQUAL(addtx_count, 2 + 2);704{705LOCK(wallet->cs_wallet);706BOOST_CHECK(wallet->mapWallet.contains(block_tx.GetHash()));707BOOST_CHECK(wallet->mapWallet.contains(mempool_tx.GetHash()));708}711TestUnloadWallet(std::move(wallet));712}714BOOST_FIXTURE_TEST_CASE(CreateWalletWithoutChain, BasicTestingSetup)715{716WalletContext context;717context.args = &m_args;718auto wallet = TestCreateWallet(context);719BOOST_CHECK(wallet);720WaitForDeleteWallet(std::move(wallet));721}723BOOST_FIXTURE_TEST_CASE(RemoveTxs, TestChain100Setup)724{725m_args.ForceSetArg("-unsafesqlitesync", "1");726WalletContext context;727context.args = &m_args;728context.chain = m_node.chain.get();729auto wallet = TestCreateWallet(context);730CKey key = GenerateRandomKey();731AddKey(*wallet, key);733m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);734auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey()));735CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));737m_node.validation_signals->SyncWithValidationInterfaceQueue();739{740auto block_hash = block_tx.GetHash();741auto prev_tx = m_coinbase_txns[0];743LOCK(wallet->cs_wallet);744BOOST_CHECK(wallet->HasWalletSpend(prev_tx));745BOOST_CHECK(wallet->mapWallet.contains(block_hash));747std::vector<Txid> vHashIn{ block_hash };748BOOST_CHECK(wallet->RemoveTxs(vHashIn));750BOOST_CHECK(!wallet->HasWalletSpend(prev_tx));
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.