diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index aea01f04e..331b5fa8d 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -1,96 +1,97 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. bin_PROGRAMS += bench/bench_bitcoin BENCH_SRCDIR = bench BENCH_BINARY = bench/bench_bitcoin$(EXEEXT) RAW_BENCH_FILES = \ bench/data/block413567.raw GENERATED_BENCH_FILES = $(RAW_BENCH_FILES:.raw=.raw.h) bench_bench_bitcoin_SOURCES = \ $(RAW_BENCH_FILES) \ bench/bench_bitcoin.cpp \ bench/bench.cpp \ bench/bench.h \ bench/block_assemble.cpp \ bench/cashaddr.cpp \ bench/checkblock.cpp \ bench/checkqueue.cpp \ bench/data.h \ bench/data.cpp \ bench/duplicate_inputs.cpp \ bench/examples.cpp \ bench/rollingbloom.cpp \ bench/crypto_aes.cpp \ bench/crypto_hash.cpp \ bench/ccoins_caching.cpp \ bench/gcs_filter.cpp \ bench/merkle_root.cpp \ bench/mempool_eviction.cpp \ bench/rpc_blockchain.cpp \ bench/rpc_mempool.cpp \ bench/util_time.cpp \ bench/base58.cpp \ bench/lockedpool.cpp \ bench/prevector.cpp \ test/setup_common.h \ test/setup_common.cpp \ test/util.h \ test/util.cpp nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/ bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bench_bench_bitcoin_LDADD = \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ $(LIBLEVELDB) \ $(LIBLEVELDB_SSE42) \ $(LIBMEMENV) \ $(LIBSECP256K1) \ $(LIBUNIVALUE) \ $(EVENT_PTHREADS_LIBS) \ $(EVENT_LIBS) if ENABLE_ZMQ bench_bench_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif if ENABLE_WALLET bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp +bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp endif bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES) CLEANFILES += $(CLEAN_BITCOIN_BENCH) bench/data.cpp: bench/data/block413567.raw.h bitcoin_bench: $(BENCH_BINARY) bench: $(BENCH_BINARY) FORCE $(BENCH_BINARY) bitcoin_bench_clean : FORCE rm -f $(CLEAN_BITCOIN_BENCH) $(bench_bench_bitcoin_OBJECTS) $(BENCH_BINARY) %.raw.h: %.raw @$(MKDIR_P) $(@D) @{ \ echo "static unsigned const char $(*F)_raw[] = {" && \ $(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' && \ echo "};"; \ } > "$@.new" && mv -f "$@.new" "$@" @echo "Generated $@" diff --git a/src/bench/CMakeLists.txt b/src/bench/CMakeLists.txt index 85a166798..cfcc905ec 100644 --- a/src/bench/CMakeLists.txt +++ b/src/bench/CMakeLists.txt @@ -1,76 +1,80 @@ # Copyright (c) 2018 The Bitcoin developers project(bitcoin-bench) set(BENCH_DATA_RAW_FILES data/block413567.raw ) # Process raw files. file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/data") foreach(_raw_file ${BENCH_DATA_RAW_FILES}) string(APPEND _generated_header_output "${CMAKE_CURRENT_BINARY_DIR}/${_raw_file}" ".h" ) list(APPEND BENCH_DATA_GENERATED_HEADERS ${_generated_header_output}) get_filename_component(_test_name ${_raw_file} NAME_WE) add_custom_command( OUTPUT "${_generated_header_output}" COMMAND "${Python_EXECUTABLE}" "data/convert-raw-to-header.py" "${_test_name}" "${_raw_file}" > "${_generated_header_output}" COMMENT "Transforming raw file ${_raw_file} into header" MAIN_DEPEDENCY "${_raw_file}" DEPENDS "data/convert-raw-to-header.py" VERBATIM WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) endforeach() add_executable(bitcoin-bench base58.cpp bench.cpp bench_bitcoin.cpp block_assemble.cpp cashaddr.cpp ccoins_caching.cpp checkblock.cpp checkqueue.cpp crypto_aes.cpp crypto_hash.cpp data.cpp duplicate_inputs.cpp examples.cpp gcs_filter.cpp lockedpool.cpp mempool_eviction.cpp merkle_root.cpp prevector.cpp rollingbloom.cpp rpc_blockchain.cpp rpc_mempool.cpp util_time.cpp # TODO: make a test library ../test/setup_common.cpp ../test/util.cpp # Add the generated headers to trigger the conversion command ${BENCH_DATA_GENERATED_HEADERS} ) target_link_libraries(bitcoin-bench common bitcoinconsensus server) if(BUILD_BITCOIN_WALLET) - target_sources(bitcoin-bench PRIVATE coin_selection.cpp) + target_sources(bitcoin-bench + PRIVATE + coin_selection.cpp + wallet_balance.cpp + ) target_link_libraries(bitcoin-bench wallet) endif() add_custom_target(bench-bitcoin COMMAND bitcoin-bench USES_TERMINAL) diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp new file mode 100644 index 000000000..a9c1778bc --- /dev/null +++ b/src/bench/wallet_balance.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2012-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include // For Params() +#include +#include +#include +#include +#include +#include +#include + +struct WalletTestingSetup { + std::unique_ptr m_chain = interfaces::MakeChain(); + CWallet m_wallet; + + WalletTestingSetup() + : m_wallet{Params(), m_chain.get(), WalletLocation(), + WalletDatabase::CreateMock()} {} + + void handleNotifications() { + m_wallet.m_chain_notifications_handler = + m_chain->handleNotifications(m_wallet); + } +}; + +static void WalletBalance(benchmark::State &state, const bool set_dirty, + const bool add_watchonly, const bool add_mine) { + const auto &ADDRESS_WATCHONLY = ADDRESS_BCHREG_UNSPENDABLE; + + WalletTestingSetup wallet_t{}; + auto &wallet = wallet_t.m_wallet; + { + bool first_run; + if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) { + assert(false); + } + wallet_t.handleNotifications(); + } + + const Config &config = GetConfig(); + + const Optional address_mine{ + add_mine ? Optional{getnewaddress(config, wallet)} + : nullopt}; + if (add_watchonly) { + importaddress(wallet, ADDRESS_WATCHONLY); + } + + for (int i = 0; i < 100; ++i) { + generatetoaddress(config, address_mine.get_value_or(ADDRESS_WATCHONLY)); + generatetoaddress(config, ADDRESS_WATCHONLY); + } + SyncWithValidationInterfaceQueue(); + + // Cache + auto bal = wallet.GetBalance(); + + while (state.KeepRunning()) { + if (set_dirty) { + wallet.MarkDirty(); + } + bal = wallet.GetBalance(); + if (add_mine) { + assert(bal.m_mine_trusted > Amount::zero()); + } + if (add_watchonly) { + assert(bal.m_watchonly_trusted > Amount::zero()); + } + } +} + +static void WalletBalanceDirty(benchmark::State &state) { + WalletBalance(state, /* set_dirty */ true, /* add_watchonly */ true, + /* add_mine */ true); +} +static void WalletBalanceClean(benchmark::State &state) { + WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ true, + /* add_mine */ true); +} +static void WalletBalanceMine(benchmark::State &state) { + WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ false, + /* add_mine */ true); +} +static void WalletBalanceWatch(benchmark::State &state) { + WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ true, + /* add_mine */ false); +} + +BENCHMARK(WalletBalanceDirty, 2500); +BENCHMARK(WalletBalanceClean, 8000); +BENCHMARK(WalletBalanceMine, 16000); +BENCHMARK(WalletBalanceWatch, 8000); diff --git a/src/test/util.cpp b/src/test/util.cpp index fbae302da..88c7fcfd2 100644 --- a/src/test/util.cpp +++ b/src/test/util.cpp @@ -1,40 +1,87 @@ // Copyright (c) 2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include +#include #include #include #include +#ifdef ENABLE_WALLET +#include +#endif // ENABLE_WALLET + +const std::string ADDRESS_BCHREG_UNSPENDABLE = + "bchreg:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqha9s37tt"; + +#ifdef ENABLE_WALLET +std::string getnewaddress(const Config &config, CWallet &w) { + constexpr auto output_type = OutputType::LEGACY; + + CPubKey new_key; + if (!w.GetKeyFromPool(new_key)) { + assert(false); + } + + w.LearnRelatedScripts(new_key, output_type); + const auto dest = GetDestinationForKey(new_key, output_type); + + w.SetAddressBook(dest, /* label */ "", "receive"); + + return EncodeDestination(dest, config); +} + +void importaddress(CWallet &wallet, const std::string &address) { + LOCK(wallet.cs_wallet); + const auto dest = DecodeDestination(address, wallet.chainParams); + assert(IsValidDestination(dest)); + const auto script = GetScriptForDestination(dest); + wallet.MarkDirty(); + assert(!wallet.HaveWatchOnly(script)); + if (!wallet.AddWatchOnly(script, 0 /* nCreateTime */)) { + assert(false); + } + wallet.SetAddressBook(dest, /* label */ "", "receive"); +} +#endif // ENABLE_WALLET + +CTxIn generatetoaddress(const Config &config, const std::string &address) { + const auto dest = DecodeDestination(address, config.GetChainParams()); + assert(IsValidDestination(dest)); + const auto coinbase_script = GetScriptForDestination(dest); + + return MineBlock(config, coinbase_script); +} + CTxIn MineBlock(const Config &config, const CScript &coinbase_scriptPubKey) { auto block = PrepareBlock(config, coinbase_scriptPubKey); while (!CheckProofOfWork(block->GetHash(), block->nBits, config.GetChainParams().GetConsensus())) { ++block->nNonce; assert(block->nNonce); } bool processed{ProcessNewBlock(config, block, true, nullptr)}; assert(processed); return CTxIn{block->vtx[0]->GetId(), 0}; } std::shared_ptr PrepareBlock(const Config &config, const CScript &coinbase_scriptPubKey) { auto block = std::make_shared(BlockAssembler{config, ::g_mempool} .CreateNewBlock(coinbase_scriptPubKey) ->block); block->nTime = ::ChainActive().Tip()->GetMedianTimePast() + 1; block->hashMerkleRoot = BlockMerkleRoot(*block); return block; } diff --git a/src/test/util.h b/src/test/util.h index 303f040be..419001b95 100644 --- a/src/test/util.h +++ b/src/test/util.h @@ -1,68 +1,82 @@ // Copyright (c) 2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_TEST_UTIL_H #define BITCOIN_TEST_UTIL_H #include +#include class CBlock; class Config; class CScript; class CTxIn; class CWallet; +// Constants // + +extern const std::string ADDRESS_BCHREG_UNSPENDABLE; + // Lower-level utils // /** Returns the generated coin */ CTxIn MineBlock(const Config &config, const CScript &coinbase_scriptPubKey); /** Prepare a block to be mined */ std::shared_ptr PrepareBlock(const Config &config, const CScript &coinbase_scriptPubKey); /** * Increment a string. Useful to enumerate all fixed length strings with * characters in [min_char, max_char]. */ template bool NextString(CharType (&string)[StringLength], CharType min_char, CharType max_char) { for (CharType &elem : string) { bool has_next = elem != max_char; elem = elem < min_char || elem >= max_char ? min_char : CharType(elem + 1); if (has_next) { return true; } } return false; } /** * Iterate over string values and call function for each string without * successive duplicate characters. */ template void ForEachNoDup(CharType (&string)[StringLength], CharType min_char, CharType max_char, Fn &&fn) { for (bool has_next = true; has_next; has_next = NextString(string, min_char, max_char)) { int prev = -1; bool skip_string = false; for (CharType c : string) { if (c == prev) { skip_string = true; } if (skip_string || c < min_char || c > max_char) { break; } prev = c; } if (!skip_string) { fn(); } } } +// RPC-like // + +/** Import the address to the wallet */ +void importaddress(CWallet &wallet, const std::string &address); +/** Returns a new address from the wallet */ +std::string getnewaddress(const Config &config, CWallet &w); +/** Returns the generated coin */ +CTxIn generatetoaddress(const Config &config, const std::string &address); + #endif // BITCOIN_TEST_UTIL_H