Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
b288d9e
Merge bitcoin/bitcoin#25647: wallet: return change from SelectionResult
thepastaclaw Jul 12, 2026
79cb403
Merge bitcoin/bitcoin#26021: wallet: bugfix, load a wallet with an un…
thepastaclaw Jul 12, 2026
2b81fa1
Merge bitcoin/bitcoin#26203: wallet: Use correct effective value when…
thepastaclaw Jul 12, 2026
cfbfa6e
Merge bitcoin/bitcoin#25685: wallet: Faster transaction creation by r…
thepastaclaw Jul 12, 2026
df3e5d8
Merge bitcoin/bitcoin#25796: rpc: add `descriptorprocesspsbt` rpc
achow101 May 22, 2023
95db517
refactor: unify test/util/wallet.h with wallet/test/util.h
furszy Aug 19, 2022
81a5d25
Merge bitcoin/bitcoin#26532: wallet: bugfix, invalid crypted key "che…
thepastaclaw Jul 12, 2026
61bd071
Merge bitcoin/bitcoin#26560: wallet: bugfix, invalid CoinsResult cach…
thepastaclaw Jul 12, 2026
7234a37
Merge bitcoin/bitcoin#26661: wallet: Coin Selection, return accurate …
thepastaclaw Jul 12, 2026
bb2e6c4
Merge bitcoin/bitcoin#25806: wallet: group outputs only once, decoupl…
thepastaclaw Jul 12, 2026
579d5d8
Merge bitcoin/bitcoin#26699: wallet, gui: bugfix, getAvailableBalance…
thepastaclaw Jul 12, 2026
a080d63
Merge bitcoin/bitcoin#26715: Introduce `MockableDatabase` for wallet …
thepastaclaw Jul 12, 2026
994bccf
Merge bitcoin/bitcoin#27665: walletdb: Remove unused CreateMockWallet…
thepastaclaw Jul 12, 2026
4dd66a5
Merge bitcoin/bitcoin#27666: wallet, bench: Move commonly used functi…
thepastaclaw Jul 12, 2026
859e53d
fix: recover from CompactSize-boundary fee shortfall in CreateTransac…
thepastaclaw Jul 12, 2026
8045006
fix: close wallet DB cursor before UNKNOWN_DESCRIPTOR early return
thepastaclaw Jul 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Makefile.bench.include
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ if ENABLE_WALLET
bench_bench_dash_SOURCES += bench/coin_selection.cpp
bench_bench_dash_SOURCES += bench/wallet_balance.cpp
bench_bench_dash_SOURCES += bench/wallet_create.cpp
bench_bench_dash_SOURCES += bench/wallet_create_tx.cpp
bench_bench_dash_SOURCES += bench/wallet_loading.cpp
bench_bench_dash_LDADD += $(BDB_LIBS) $(SQLITE_LIBS)
endif
Expand Down
6 changes: 3 additions & 3 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,9 @@ BITCOIN_TESTS += \
wallet/test/init_tests.cpp \
wallet/test/ismine_tests.cpp \
wallet/test/rpc_util_tests.cpp \
wallet/test/scriptpubkeyman_tests.cpp
wallet/test/scriptpubkeyman_tests.cpp \
wallet/test/walletload_tests.cpp \
wallet/test/group_outputs_tests.cpp

FUZZ_SUITE_LD_COMMON +=\
$(SQLITE_LIBS) \
Expand All @@ -245,8 +247,6 @@ FUZZ_WALLET_SRC += \
endif # USE_SQLITE

BITCOIN_TEST_SUITE += \
wallet/test/util.cpp \
wallet/test/util.h \
wallet/test/wallet_test_fixture.cpp \
wallet/test/wallet_test_fixture.h \
wallet/test/init_test_fixture.cpp \
Expand Down
12 changes: 10 additions & 2 deletions src/Makefile.test_util.include
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ TEST_UTIL_H = \
test/util/str.h \
test/util/transaction_utils.h \
test/util/txmempool.h \
test/util/wallet.h \
test/util/validation.h \
test/util/xoroshiro128plusplus.h

if ENABLE_WALLET
TEST_UTIL_H += \
wallet/test/util.h
endif # ENABLE_WALLET

libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS)
libtest_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libtest_util_a_SOURCES = \
Expand All @@ -44,5 +48,9 @@ libtest_util_a_SOURCES = \
test/util/transaction_utils.cpp \
test/util/txmempool.cpp \
test/util/validation.cpp \
test/util/wallet.cpp \
$(TEST_UTIL_H)

if ENABLE_WALLET
libtest_util_a_SOURCES += \
wallet/test/util.cpp
endif # ENABLE_WALLET
1 change: 0 additions & 1 deletion src/bench/block_assemble.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <test/util/mining.h>
#include <test/util/script.h>
#include <test/util/setup_common.h>
#include <test/util/wallet.h>
#include <txmempool.h>
#include <validation.h>

Expand Down
11 changes: 7 additions & 4 deletions src/bench/coin_selection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <policy/policy.h>
#include <wallet/coinselection.h>
#include <wallet/spend.h>
#include <wallet/test/util.h>
#include <wallet/wallet.h>

#include <set>
Expand All @@ -17,7 +18,7 @@ using wallet::CHANGE_LOWER;
using wallet::CoinEligibilityFilter;
using wallet::CoinSelectionParams;
using wallet::COutput;
using wallet::CreateDummyWalletDatabase;
using wallet::CreateMockableWalletDatabase;
using wallet::CWallet;
using wallet::CWalletTx;
using wallet::OutputGroup;
Expand All @@ -44,7 +45,7 @@ static void CoinSelection(benchmark::Bench& bench)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
CWallet wallet(chain.get(), /*coinjoin_loader=*/nullptr, "", gArgs, CreateDummyWalletDatabase());
CWallet wallet(chain.get(), /*coinjoin_loader=*/nullptr, "", gArgs, CreateMockableWalletDatabase());
std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);

Expand Down Expand Up @@ -73,8 +74,10 @@ static void CoinSelection(benchmark::Bench& bench)
/*tx_noinputs_size=*/ 0,
/*avoid_partial=*/ false,
};
wallet::Groups groups = wallet::GroupOutputs(wallet, available_coins.legacy, coin_selection_params, filter_standard);
wallet::Groups mixed_groups = wallet::GroupOutputs(wallet, available_coins.all(), coin_selection_params, filter_standard);
bench.run([&] {
auto result = AttemptSelection(wallet, 1003 * COIN, filter_standard, available_coins, coin_selection_params, /*allow_mixed_output_types=*/true);
auto result = AttemptSelection(wallet, 1003 * COIN, groups, mixed_groups, coin_selection_params, /*allow_mixed_output_types=*/true);
assert(result);
assert(result->GetSelectedValue() == 1003 * COIN);
assert(result->GetInputSet().size() == 2);
Expand All @@ -89,7 +92,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
tx.vout[nInput].nValue = nValue;
COutput output(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 0, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ true, /*fees=*/ 0);
set.emplace_back();
set.back().Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
set.back().Insert(std::make_shared<COutput>(output), /*ancestors=*/ 0, /*descendants=*/ 0);
}
// Copied from src/wallet/test/coinselector_tests.cpp
static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
Expand Down
12 changes: 4 additions & 8 deletions src/bench/wallet_balance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,24 @@
#include <node/context.h>
#include <test/util/mining.h>
#include <test/util/setup_common.h>
#include <test/util/wallet.h>
#include <validationinterface.h>
#include <wallet/receive.h>
#include <wallet/test/util.h>
#include <wallet/wallet.h>

#include <optional>

using wallet::CreateMockWalletDatabase;
using wallet::CWallet;
using wallet::DBErrors;
using wallet::WALLET_FLAG_DESCRIPTORS;

namespace wallet {
static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const bool add_mine, const uint32_t epoch_iters)
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();
const auto& ADDRESS_WATCHONLY = ADDRESS_B58T_UNSPENDABLE;

CWallet wallet{test_setup->m_node.chain.get(), test_setup->m_node.coinjoin_loader.get(), "", gArgs, CreateMockWalletDatabase()};
CWallet wallet{test_setup->m_node.chain.get(), test_setup->m_node.coinjoin_loader.get(), "", gArgs, CreateMockableWalletDatabase()};
{
LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetupDescriptorScriptPubKeyMans("", "");
if (wallet.LoadWallet() != DBErrors::LOAD_OK) assert(false);
}
auto handler = test_setup->m_node.chain->handleNotifications({&wallet, [](CWallet*) {}});

Expand Down Expand Up @@ -59,3 +54,4 @@ BENCHMARK(WalletBalanceDirty, benchmark::PriorityLevel::HIGH);
BENCHMARK(WalletBalanceClean, benchmark::PriorityLevel::HIGH);
BENCHMARK(WalletBalanceMine, benchmark::PriorityLevel::HIGH);
BENCHMARK(WalletBalanceWatch, benchmark::PriorityLevel::HIGH);
} // namespace wallet
142 changes: 142 additions & 0 deletions src/bench/wallet_create_tx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright (c) 2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php.

#include <bench/bench.h>
#include <chainparams.h>
#include <wallet/coincontrol.h>
#include <consensus/merkle.h>
#include <node/context.h>
#include <test/util/setup_common.h>
#include <validation.h>
#include <wallet/spend.h>
#include <wallet/test/util.h>
#include <wallet/wallet.h>

using wallet::CWallet;
using wallet::CreateMockableWalletDatabase;
using wallet::DBErrors;
using wallet::getNewDestination;
using wallet::WALLET_FLAG_DESCRIPTORS;

struct TipBlock
{
uint256 prev_block_hash;
int64_t prev_block_time;
int tip_height;
};

TipBlock getTip(const CChainParams& params, const node::NodeContext& context)
{
auto tip = WITH_LOCK(::cs_main, return context.chainman->ActiveTip());
return (tip) ? TipBlock{tip->GetBlockHash(), tip->GetBlockTime(), tip->nHeight} :
TipBlock{params.GenesisBlock().GetHash(), params.GenesisBlock().GetBlockTime(), 0};
}

void generateFakeBlock(const CChainParams& params,
const node::NodeContext& context,
CWallet& wallet,
const CScript& coinbase_out_script)
{
TipBlock tip{getTip(params, context)};

// Create block
CBlock block;
CMutableTransaction coinbase_tx;
coinbase_tx.vin.resize(1);
coinbase_tx.vin[0].prevout.SetNull();
coinbase_tx.vout.resize(2);
coinbase_tx.vout[0].scriptPubKey = coinbase_out_script;
coinbase_tx.vout[0].nValue = 49 * COIN;
coinbase_tx.vin[0].scriptSig = CScript() << ++tip.tip_height << OP_0;
coinbase_tx.vout[1].scriptPubKey = coinbase_out_script; // extra output
coinbase_tx.vout[1].nValue = 1 * COIN;
block.vtx = {MakeTransactionRef(std::move(coinbase_tx))};

block.nVersion = VERSIONBITS_LAST_OLD_BLOCK_VERSION;
block.hashPrevBlock = tip.prev_block_hash;
block.hashMerkleRoot = BlockMerkleRoot(block);
block.nTime = ++tip.prev_block_time;
block.nBits = params.GenesisBlock().nBits;
block.nNonce = 0;

{
LOCK(::cs_main);
// Add it to the index
CBlockIndex* pindex{context.chainman->m_blockman.AddToBlockIndex(block, block.GetHash(), context.chainman->m_best_header)};
// add it to the chain
context.chainman->ActiveChain().SetTip(*pindex);
}

// notify wallet
const auto& pindex = WITH_LOCK(::cs_main, return context.chainman->ActiveChain().Tip());
wallet.blockConnected(block, pindex->nHeight);
}

struct PreSelectInputs {
// How many coins from the wallet the process should select
int num_of_internal_inputs;
// future: this could have external inputs as well.
};

static void WalletCreateTx(benchmark::Bench& bench, bool allow_other_inputs, std::optional<PreSelectInputs> preset_inputs)
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();

CWallet wallet{test_setup->m_node.chain.get(), test_setup->m_node.coinjoin_loader.get(), "", gArgs, CreateMockableWalletDatabase()};
{
LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetupDescriptorScriptPubKeyMans("", "");
if (wallet.LoadWallet() != DBErrors::LOAD_OK) assert(false);
}

// Generate destinations
CScript dest = GetScriptForDestination(getNewDestination(wallet));

// Generate chain; each coinbase will have two outputs to fill-up the wallet
const auto& params = Params();
unsigned int chain_size = 5000; // 5k blocks means 10k UTXO for the wallet (minus 100 due COINBASE_MATURITY)
for (unsigned int i = 0; i < chain_size; ++i) {
generateFakeBlock(params, test_setup->m_node, wallet, dest);
}

// Check available balance
auto bal = WITH_LOCK(wallet.cs_wallet, return wallet::AvailableCoins(wallet).total_amount); // Cache
assert(bal == 50 * COIN * (chain_size - COINBASE_MATURITY));

wallet::CCoinControl coin_control;
coin_control.m_allow_other_inputs = allow_other_inputs;

CAmount target = 0;
if (preset_inputs) {
// Select inputs, each has 49 DASH
const auto& res = WITH_LOCK(wallet.cs_wallet,
return wallet::AvailableCoins(wallet, nullptr, std::nullopt, 1, MAX_MONEY,
MAX_MONEY, preset_inputs->num_of_internal_inputs));
for (int i = 0; i < preset_inputs->num_of_internal_inputs; i++) {
const auto& coin{res.legacy[i]};
target += coin.txout.nValue;
coin_control.Select(coin.outpoint);
}
}

// If automatic coin selection is enabled, add the value of another UTXO to the target
if (coin_control.m_allow_other_inputs) target += 50 * COIN;
std::vector<wallet::CRecipient> recipients = {{dest, target, true}};

bench.epochIterations(5).run([&] {
LOCK(wallet.cs_wallet);
const auto& tx_res = CreateTransaction(wallet, recipients, wallet::RANDOM_CHANGE_POSITION, coin_control);
assert(tx_res);
});
}

static void WalletCreateTxUseOnlyPresetInputs(benchmark::Bench& bench) { WalletCreateTx(bench, /*allow_other_inputs=*/false,
{{/*num_of_internal_inputs=*/4}}); }

static void WalletCreateTxUsePresetInputsAndCoinSelection(benchmark::Bench& bench) { WalletCreateTx(bench, /*allow_other_inputs=*/true,
{{/*num_of_internal_inputs=*/4}}); }

BENCHMARK(WalletCreateTxUseOnlyPresetInputs, benchmark::PriorityLevel::LOW)
BENCHMARK(WalletCreateTxUsePresetInputsAndCoinSelection, benchmark::PriorityLevel::LOW)
Loading
Loading