Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2a38c10
Merge bitcoin/bitcoin#25647: wallet: return change from SelectionResult
thepastaclaw Jul 12, 2026
974a73b
Merge bitcoin/bitcoin#26021: wallet: bugfix, load a wallet with an un…
thepastaclaw Jul 12, 2026
b84f087
Merge bitcoin/bitcoin#26203: wallet: Use correct effective value when…
thepastaclaw Jul 12, 2026
ad0cbe8
Merge bitcoin/bitcoin#25685: wallet: Faster transaction creation by r…
thepastaclaw Jul 12, 2026
cf7271a
Merge bitcoin/bitcoin#25796: rpc: add `descriptorprocesspsbt` rpc
achow101 May 22, 2023
84a95ba
refactor: unify test/util/wallet.h with wallet/test/util.h
furszy Aug 19, 2022
03335ed
Merge bitcoin/bitcoin#26532: wallet: bugfix, invalid crypted key "che…
thepastaclaw Jul 12, 2026
88857f3
Merge bitcoin/bitcoin#26560: wallet: bugfix, invalid CoinsResult cach…
thepastaclaw Jul 12, 2026
d619f41
Merge bitcoin/bitcoin#26661: wallet: Coin Selection, return accurate …
thepastaclaw Jul 12, 2026
5211856
Merge bitcoin/bitcoin#25806: wallet: group outputs only once, decoupl…
thepastaclaw Jul 12, 2026
69649d9
Merge bitcoin/bitcoin#26699: wallet, gui: bugfix, getAvailableBalance…
thepastaclaw Jul 12, 2026
0cd039f
Merge bitcoin/bitcoin#26715: Introduce `MockableDatabase` for wallet …
thepastaclaw Jul 12, 2026
16f90a6
Merge bitcoin/bitcoin#27665: walletdb: Remove unused CreateMockWallet…
thepastaclaw Jul 12, 2026
df2132a
Merge bitcoin/bitcoin#27666: wallet, bench: Move commonly used functi…
thepastaclaw Jul 12, 2026
3a85748
fix: recover from CompactSize-boundary fee shortfall in CreateTransac…
thepastaclaw Jul 12, 2026
1901b50
fix: close wallet DB cursor before UNKNOWN_DESCRIPTOR early return
thepastaclaw Jul 12, 2026
7be7854
fix: adapt wallet create tx bench to BlockInfo
thepastaclaw Aug 3, 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 @@ -229,7 +229,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 @@ -250,8 +252,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
143 changes: 143 additions & 0 deletions src/bench/wallet_create_tx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// 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 <kernel/chain.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(kernel::MakeBlockInfo(pindex, &block));
}

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