Skip to content

Commit

Permalink
Merge f37bd15 into merged_master (Bitcoin PR bitcoin/bitcoin#25685)
Browse files Browse the repository at this point in the history
  • Loading branch information
delta1 committed Feb 11, 2025
2 parents ca0a68b + f37bd15 commit fc1020b
Show file tree
Hide file tree
Showing 14 changed files with 354 additions and 156 deletions.
1 change: 1 addition & 0 deletions src/Makefile.bench.include
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ if ENABLE_WALLET
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
bench_bench_bitcoin_SOURCES += bench/wallet_loading.cpp
bench_bench_bitcoin_SOURCES += bench/wallet_create_tx.cpp
bench_bench_bitcoin_LDADD += $(BDB_LIBS) $(SQLITE_LIBS)
endif

Expand Down
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 <test/util/wallet.h>
#include <validation.h>
#include <wallet/spend.h>
#include <wallet/wallet.h>

using wallet::CWallet;
using wallet::CreateMockWalletDatabase;
using wallet::DBErrors;
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, 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, const OutputType output_type, bool allow_other_inputs, std::optional<PreSelectInputs> preset_inputs)
{
const auto test_setup = MakeNoLogFileContext<const TestingSetup>();

CWallet wallet{test_setup->m_node.chain.get(), "", gArgs, CreateMockWalletDatabase()};
{
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, output_type));

// 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 200 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 = wallet::GetAvailableBalance(wallet); // Cache
CAmountMap expected{{::policyAsset, (50 * COIN * (chain_size - COINBASE_MATURITY))}};
assert(bal == expected);

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

CAmount target = 0;
if (preset_inputs) {
// Select inputs, each has 49 BTC
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.coins.at(output_type)[i]};
target += coin.value;
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, ::policyAsset, CPubKey(), true}};

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

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

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

BENCHMARK(WalletCreateTxUseOnlyPresetInputs, benchmark::PriorityLevel::LOW)
BENCHMARK(WalletCreateTxUsePresetInputsAndCoinSelection, benchmark::PriorityLevel::LOW)
2 changes: 2 additions & 0 deletions src/qt/sendcoinsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa

updateCoinControlState();

CCoinControl coin_control = *m_coin_control;
coin_control.m_allow_other_inputs = !coin_control.HasSelected(); // future, could introduce a checkbox to customize this value.
prepareStatus = model->prepareTransaction(*m_current_transaction, m_current_blind_details.get(), *m_coin_control);

// process prepareStatus and on error generate message shown to user
Expand Down
7 changes: 6 additions & 1 deletion src/test/util/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqq
std::string getnewaddress(CWallet& w)
{
constexpr auto output_type = OutputType::BECH32;
return EncodeDestination(*Assert(w.GetNewDestination(output_type, "")));
return EncodeDestination(getNewDestination(w, output_type));
}

CTxDestination getNewDestination(CWallet& w, OutputType output_type)
{
return *Assert(w.GetNewDestination(output_type, ""));
}

#endif // ENABLE_WALLET
5 changes: 4 additions & 1 deletion src/test/util/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef BITCOIN_TEST_UTIL_WALLET_H
#define BITCOIN_TEST_UTIL_WALLET_H

#include <outputtype.h>
#include <string>

namespace wallet {
Expand All @@ -19,8 +20,10 @@ extern const std::string ADDRESS_BCRT1_UNSPENDABLE;

/** Import the address to the wallet */
void importaddress(wallet::CWallet& wallet, const std::string& address);
/** Returns a new address from the wallet */
/** Returns a new encoded destination from the wallet (hardcoded to BECH32) */
std::string getnewaddress(wallet::CWallet& w);
/** Returns a new destination, of an specific type, from the wallet */
CTxDestination getNewDestination(wallet::CWallet& w, OutputType output_type);


#endif // BITCOIN_TEST_UTIL_WALLET_H
2 changes: 1 addition & 1 deletion src/wallet/coincontrol.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class CCoinControl
bool m_include_unsafe_inputs = false;
//! If true, the selection process can add extra unselected inputs from the wallet
//! while requires all selected inputs be used
bool m_allow_other_inputs = false;
bool m_allow_other_inputs = true;
//! Includes watch only addresses which are solvable
bool fAllowWatchOnly = false;
//! Override automatic min/max checks on fee, m_feerate must be set if true
Expand Down
6 changes: 6 additions & 0 deletions src/wallet/coinselection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,12 @@ void SelectionResult::AddInput(const OutputGroup& group)
m_use_effective = !group.m_subtract_fee_outputs;
}

void SelectionResult::AddInputs(const std::set<COutput>& inputs, bool subtract_fee_outputs)
{
util::insert(m_selected_inputs, inputs);
m_use_effective = !subtract_fee_outputs;
}

// ELEMENTS
void SelectionResult::AddInput(const SelectionResult& result) {
util::insert(m_selected_inputs, result.GetInputSet());
Expand Down
1 change: 1 addition & 0 deletions src/wallet/coinselection.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ struct SelectionResult
void Clear();

void AddInput(const OutputGroup& group);
void AddInputs(const std::set<COutput>& inputs, bool subtract_fee_outputs);
// ELEMENTS
void AddInput(const SelectionResult& result);

Expand Down
Loading

0 comments on commit fc1020b

Please sign in to comment.