aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am19
-rw-r--r--src/Makefile.bench.include7
-rw-r--r--src/Makefile.qt.include2
-rw-r--r--src/Makefile.qttest.include14
-rw-r--r--src/Makefile.test.include31
-rw-r--r--src/Makefile.test_util.include31
-rw-r--r--src/bench/bench.cpp2
-rw-r--r--src/bench/verify_script.cpp2
-rw-r--r--src/bitcoin-cli.cpp2
-rw-r--r--src/bloom.h3
-rw-r--r--src/chainparams.cpp4
-rw-r--r--src/compat/cpuid.h24
-rw-r--r--src/crypto/sha256.cpp20
-rw-r--r--src/crypto/sha512.h1
-rw-r--r--src/init.cpp63
-rw-r--r--src/interfaces/chain.cpp16
-rw-r--r--src/interfaces/chain.h13
-rw-r--r--src/interfaces/wallet.cpp64
-rw-r--r--src/interfaces/wallet.h12
-rw-r--r--src/key.cpp20
-rw-r--r--src/key.h10
-rw-r--r--src/logging.cpp19
-rw-r--r--src/logging.h22
-rw-r--r--src/logging/timer.h104
-rw-r--r--src/miner.cpp2
-rw-r--r--src/net.cpp3
-rw-r--r--src/net.h23
-rw-r--r--src/net_processing.cpp17
-rw-r--r--src/node/coinstats.cpp6
-rw-r--r--src/node/coinstats.h21
-rw-r--r--src/node/context.h7
-rw-r--r--src/node/utxo_snapshot.h50
-rw-r--r--src/noui.cpp17
-rw-r--r--src/noui.h6
-rw-r--r--src/psbt.h2
-rw-r--r--src/pubkey.cpp18
-rw-r--r--src/pubkey.h22
-rw-r--r--src/qt/bitcoin.cpp15
-rw-r--r--src/qt/coincontroldialog.cpp2
-rw-r--r--src/qt/forms/receivecoinsdialog.ui4
-rw-r--r--src/qt/forms/sendcoinsdialog.ui2
-rw-r--r--src/qt/guiutil.cpp2
-rw-r--r--src/qt/openuridialog.cpp2
-rw-r--r--src/qt/paymentserver.h6
-rw-r--r--src/qt/receiverequestdialog.cpp1
-rw-r--r--src/qt/receiverequestdialog.h4
-rw-r--r--src/qt/recentrequeststablemodel.cpp1
-rw-r--r--src/qt/recentrequeststablemodel.h4
-rw-r--r--src/qt/sendcoinsdialog.cpp76
-rw-r--r--src/qt/sendcoinsdialog.h3
-rw-r--r--src/qt/sendcoinsentry.cpp1
-rw-r--r--src/qt/sendcoinsentry.h6
-rw-r--r--src/qt/sendcoinsrecipient.h74
-rw-r--r--src/qt/signverifymessagedialog.cpp2
-rw-r--r--src/qt/test/addressbooktests.cpp2
-rw-r--r--src/qt/test/apptests.cpp2
-rw-r--r--src/qt/test/rpcnestedtests.cpp3
-rw-r--r--src/qt/test/test_main.cpp2
-rw-r--r--src/qt/test/wallettests.cpp4
-rw-r--r--src/qt/transactiondesc.cpp5
-rw-r--r--src/qt/utilitydialog.cpp8
-rw-r--r--src/qt/walletcontroller.cpp7
-rw-r--r--src/qt/walletcontroller.h4
-rw-r--r--src/qt/walletframe.cpp5
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/qt/walletmodel.h58
-rw-r--r--src/qt/walletmodeltransaction.h3
-rw-r--r--src/qt/walletview.h2
-rw-r--r--src/random.cpp167
-rw-r--r--src/random.h6
-rw-r--r--src/randomenv.cpp507
-rw-r--r--src/randomenv.h17
-rw-r--r--src/rpc/blockchain.cpp140
-rw-r--r--src/rpc/blockchain.h2
-rw-r--r--src/rpc/mining.cpp13
-rw-r--r--src/rpc/protocol.h3
-rw-r--r--src/rpc/rawtransaction.cpp4
-rw-r--r--src/rpc/rawtransaction_util.cpp8
-rw-r--r--src/rpc/rawtransaction_util.h4
-rw-r--r--src/rpc/util.cpp8
-rw-r--r--src/scheduler.cpp2
-rw-r--r--src/script/descriptor.cpp4
-rw-r--r--src/script/interpreter.cpp8
-rw-r--r--src/script/sign.h2
-rw-r--r--src/script/standard.cpp8
-rw-r--r--src/support/lockedpool.cpp8
-rw-r--r--src/test/README.md4
-rw-r--r--src/test/addrman_tests.cpp2
-rw-r--r--src/test/allocator_tests.cpp2
-rw-r--r--src/test/amount_tests.cpp2
-rw-r--r--src/test/arith_uint256_tests.cpp2
-rw-r--r--src/test/base32_tests.cpp2
-rw-r--r--src/test/base58_tests.cpp2
-rw-r--r--src/test/base64_tests.cpp2
-rw-r--r--src/test/bech32_tests.cpp16
-rw-r--r--src/test/bip32_tests.cpp2
-rw-r--r--src/test/blockchain_tests.cpp2
-rw-r--r--src/test/blockencodings_tests.cpp2
-rw-r--r--src/test/blockfilter_index_tests.cpp4
-rw-r--r--src/test/blockfilter_tests.cpp2
-rw-r--r--src/test/bloom_tests.cpp4
-rw-r--r--src/test/bswap_tests.cpp2
-rw-r--r--src/test/checkqueue_tests.cpp18
-rw-r--r--src/test/coins_tests.cpp4
-rw-r--r--src/test/compilerbug_tests.cpp2
-rw-r--r--src/test/compress_tests.cpp2
-rw-r--r--src/test/crypto_tests.cpp2
-rw-r--r--src/test/cuckoocache_tests.cpp12
-rw-r--r--src/test/dbwrapper_tests.cpp2
-rw-r--r--src/test/denialofservice_tests.cpp2
-rw-r--r--src/test/descriptor_tests.cpp2
-rw-r--r--src/test/flatfile_tests.cpp2
-rw-r--r--src/test/fs_tests.cpp2
-rw-r--r--src/test/fuzz/bech32.cpp43
-rw-r--r--src/test/fuzz/psbt.cpp79
-rw-r--r--src/test/getarg_tests.cpp2
-rw-r--r--src/test/hash_tests.cpp2
-rw-r--r--src/test/key_io_tests.cpp2
-rw-r--r--src/test/key_properties.cpp2
-rw-r--r--src/test/key_tests.cpp2
-rw-r--r--src/test/limitedmap_tests.cpp2
-rw-r--r--src/test/logging_tests.cpp36
-rw-r--r--src/test/mempool_tests.cpp39
-rw-r--r--src/test/merkle_tests.cpp6
-rw-r--r--src/test/merkleblock_tests.cpp2
-rw-r--r--src/test/miner_tests.cpp100
-rw-r--r--src/test/multisig_tests.cpp2
-rw-r--r--src/test/net_tests.cpp16
-rw-r--r--src/test/netbase_tests.cpp2
-rw-r--r--src/test/pmt_tests.cpp3
-rw-r--r--src/test/policyestimator_tests.cpp2
-rw-r--r--src/test/pow_tests.cpp2
-rw-r--r--src/test/prevector_tests.cpp2
-rw-r--r--src/test/raii_event_tests.cpp2
-rw-r--r--src/test/random_tests.cpp2
-rw-r--r--src/test/reverselock_tests.cpp2
-rw-r--r--src/test/rpc_tests.cpp2
-rw-r--r--src/test/sanity_tests.cpp2
-rw-r--r--src/test/scheduler_tests.cpp2
-rw-r--r--src/test/script_p2sh_tests.cpp2
-rw-r--r--src/test/script_standard_tests.cpp2
-rw-r--r--src/test/script_tests.cpp4
-rw-r--r--src/test/scriptnum_tests.cpp2
-rw-r--r--src/test/serialize_tests.cpp2
-rw-r--r--src/test/settings_tests.cpp163
-rw-r--r--src/test/sighash_tests.cpp4
-rw-r--r--src/test/sigopcount_tests.cpp2
-rw-r--r--src/test/skiplist_tests.cpp2
-rw-r--r--src/test/streams_tests.cpp4
-rw-r--r--src/test/sync_tests.cpp2
-rw-r--r--src/test/timedata_tests.cpp10
-rw-r--r--src/test/torcontrol_tests.cpp2
-rw-r--r--src/test/transaction_tests.cpp15
-rw-r--r--src/test/txindex_tests.cpp2
-rw-r--r--src/test/txvalidation_tests.cpp8
-rw-r--r--src/test/txvalidationcache_tests.cpp28
-rw-r--r--src/test/uint256_tests.cpp2
-rw-r--r--src/test/util/README.md11
-rw-r--r--src/test/util/blockfilter.cpp (renamed from src/test/lib/blockfilter.cpp)2
-rw-r--r--src/test/util/blockfilter.h (renamed from src/test/lib/blockfilter.h)6
-rw-r--r--src/test/util/logging.cpp32
-rw-r--r--src/test/util/logging.h29
-rw-r--r--src/test/util/setup_common.cpp (renamed from src/test/setup_common.cpp)44
-rw-r--r--src/test/util/setup_common.h (renamed from src/test/setup_common.h)20
-rw-r--r--src/test/util/str.cpp21
-rw-r--r--src/test/util/str.h12
-rw-r--r--src/test/util/transaction_utils.cpp (renamed from src/test/lib/transaction_utils.cpp)2
-rw-r--r--src/test/util/transaction_utils.h (renamed from src/test/lib/transaction_utils.h)6
-rw-r--r--src/test/util_tests.cpp156
-rw-r--r--src/test/util_threadnames_tests.cpp2
-rw-r--r--src/test/validation_block_tests.cpp17
-rw-r--r--src/test/validation_tests.cpp2
-rw-r--r--src/test/versionbits_tests.cpp2
-rw-r--r--src/txmempool.cpp8
-rw-r--r--src/txmempool.h2
-rw-r--r--src/util/fees.cpp3
-rw-r--r--src/util/settings.cpp169
-rw-r--r--src/util/settings.h87
-rw-r--r--src/util/system.cpp378
-rw-r--r--src/util/system.h4
-rw-r--r--src/validation.cpp42
-rw-r--r--src/validation.h11
-rw-r--r--src/validationinterface.cpp42
-rw-r--r--src/validationinterface.h13
-rw-r--r--src/wallet/db.cpp30
-rw-r--r--src/wallet/feebumper.cpp12
-rw-r--r--src/wallet/psbtwallet.cpp27
-rw-r--r--src/wallet/rpcdump.cpp67
-rw-r--r--src/wallet/rpcwallet.cpp166
-rw-r--r--src/wallet/rpcwallet.h2
-rw-r--r--src/wallet/scriptpubkeyman.cpp45
-rw-r--r--src/wallet/scriptpubkeyman.h6
-rw-r--r--src/wallet/test/coinselector_tests.cpp54
-rw-r--r--src/wallet/test/db_tests.cpp2
-rw-r--r--src/wallet/test/init_test_fixture.h2
-rw-r--r--src/wallet/test/init_tests.cpp30
-rw-r--r--src/wallet/test/ismine_tests.cpp2
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp2
-rw-r--r--src/wallet/test/wallet_crypto_tests.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp44
-rw-r--r--src/wallet/wallet.cpp335
-rw-r--r--src/wallet/wallet.h105
-rw-r--r--src/wallet/walletdb.cpp2
-rw-r--r--src/zmq/zmqnotificationinterface.cpp2
-rw-r--r--src/zmq/zmqnotificationinterface.h2
206 files changed, 3248 insertions, 1454 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 507e5cbb9f..27c87688b4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,7 +19,7 @@ else
LIBUNIVALUE = $(UNIVALUE_LIBS)
endif
-BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS)
+BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS)
@@ -119,6 +119,7 @@ BITCOIN_CORE_H = \
compat.h \
compat/assumptions.h \
compat/byteswap.h \
+ compat/cpuid.h \
compat/endian.h \
compat/sanity.h \
compressor.h \
@@ -146,6 +147,7 @@ BITCOIN_CORE_H = \
dbwrapper.h \
limitedmap.h \
logging.h \
+ logging/timer.h \
memusage.h \
merkleblock.h \
miner.h \
@@ -161,6 +163,7 @@ BITCOIN_CORE_H = \
node/context.h \
node/psbt.h \
node/transaction.h \
+ node/utxo_snapshot.h \
noui.h \
optional.h \
outputtype.h \
@@ -173,6 +176,7 @@ BITCOIN_CORE_H = \
protocol.h \
psbt.h \
random.h \
+ randomenv.h \
reverse_iterator.h \
reverselock.h \
rpc/blockchain.h \
@@ -217,6 +221,7 @@ BITCOIN_CORE_H = \
util/memory.h \
util/moneystr.h \
util/rbf.h \
+ util/settings.h \
util/string.h \
util/threadnames.h \
util/time.h \
@@ -500,6 +505,7 @@ libbitcoin_util_a_SOURCES = \
interfaces/handler.cpp \
logging.cpp \
random.cpp \
+ randomenv.cpp \
rpc/request.cpp \
support/cleanse.cpp \
sync.cpp \
@@ -511,6 +517,7 @@ libbitcoin_util_a_SOURCES = \
util/system.cpp \
util/moneystr.cpp \
util/rbf.cpp \
+ util/settings.cpp \
util/threadnames.cpp \
util/spanparsing.cpp \
util/strencodings.cpp \
@@ -564,7 +571,7 @@ bitcoind_LDADD = \
$(LIBMEMENV) \
$(LIBSECP256K1)
-bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
+bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
# bitcoin-cli binary #
bitcoin_cli_SOURCES = bitcoin-cli.cpp
@@ -582,7 +589,7 @@ bitcoin_cli_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO)
-bitcoin_cli_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS)
+bitcoin_cli_LDADD += $(BOOST_LIBS) $(EVENT_LIBS)
#
# bitcoin-tx binary #
@@ -603,7 +610,7 @@ bitcoin_tx_LDADD = \
$(LIBBITCOIN_CRYPTO) \
$(LIBSECP256K1)
-bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+bitcoin_tx_LDADD += $(BOOST_LIBS)
#
# bitcoin-wallet binary #
@@ -630,7 +637,7 @@ bitcoin_wallet_LDADD = \
$(LIBSECP256K1) \
$(LIBUNIVALUE)
-bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS)
+bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS)
#
# bitcoinconsensus library #
@@ -710,6 +717,8 @@ if EMBEDDED_LEVELDB
include Makefile.leveldb.include
endif
+include Makefile.test_util.include
+
if ENABLE_TESTS
include Makefile.test.include
endif
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index fbcab86d8f..9e70db116b 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -40,10 +40,6 @@ bench_bench_bitcoin_SOURCES = \
bench/lockedpool.cpp \
bench/poly1305.cpp \
bench/prevector.cpp \
- test/lib/transaction_utils.h \
- test/lib/transaction_utils.cpp \
- test/setup_common.h \
- test/setup_common.cpp \
test/util.h \
test/util.cpp
@@ -59,6 +55,7 @@ bench_bench_bitcoin_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
+ $(LIBTEST_UTIL) \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
@@ -76,7 +73,7 @@ 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) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS)
+bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS)
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 13b1470b58..cf09eee2cb 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -136,6 +136,7 @@ BITCOIN_QT_H = \
qt/rpcconsole.h \
qt/sendcoinsdialog.h \
qt/sendcoinsentry.h \
+ qt/sendcoinsrecipient.h \
qt/signverifymessagedialog.h \
qt/splashscreen.h \
qt/trafficgraphwidget.h \
@@ -314,7 +315,6 @@ endif
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
-qt_bitcoin_qt_LDADD += $(CRYPTO_LIBS)
qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
qt_bitcoin_qt_LIBTOOLFLAGS = $(AM_LIBTOOLFLAGS) --tag CXX
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index c309340fd7..8c47fabad9 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -26,12 +26,6 @@ TEST_QT_H = \
qt/test/util.h \
qt/test/wallettests.h
-TEST_BITCOIN_CPP = \
- test/setup_common.cpp
-
-TEST_BITCOIN_H = \
- test/setup_common.h
-
qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
$(QT_INCLUDES) $(QT_TEST_INCLUDES)
@@ -42,9 +36,7 @@ qt_test_test_bitcoin_qt_SOURCES = \
qt/test/test_main.cpp \
qt/test/uritests.cpp \
qt/test/util.cpp \
- $(TEST_QT_H) \
- $(TEST_BITCOIN_CPP) \
- $(TEST_BITCOIN_H)
+ $(TEST_QT_H)
if ENABLE_WALLET
qt_test_test_bitcoin_qt_SOURCES += \
qt/test/addressbooktests.cpp \
@@ -54,7 +46,7 @@ endif # ENABLE_WALLET
nodist_qt_test_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP)
-qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER)
+qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) $(LIBTEST_UTIL)
if ENABLE_WALLET
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET)
endif
@@ -63,7 +55,7 @@ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
- $(QR_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
+ $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index a4cf78d61b..b4ca01b1fe 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -7,6 +7,7 @@ FUZZ_TARGETS = \
test/fuzz/address_deserialize \
test/fuzz/addrman_deserialize \
test/fuzz/banentry_deserialize \
+ test/fuzz/bech32 \
test/fuzz/block_deserialize \
test/fuzz/blockheader_deserialize \
test/fuzz/blocklocator_deserialize \
@@ -23,6 +24,7 @@ FUZZ_TARGETS = \
test/fuzz/messageheader_deserialize \
test/fuzz/netaddr_deserialize \
test/fuzz/parse_iso8601 \
+ test/fuzz/psbt \
test/fuzz/script \
test/fuzz/script_flags \
test/fuzz/service_deserialize \
@@ -56,17 +58,10 @@ RAW_TEST_FILES =
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
BITCOIN_TEST_SUITE = \
- test/lib/blockfilter.cpp \
- test/lib/blockfilter.h \
- test/lib/transaction_utils.cpp \
- test/lib/transaction_utils.h \
test/main.cpp \
- test/setup_common.h \
- test/setup_common.cpp
+ $(TEST_UTIL_H)
FUZZ_SUITE = \
- test/setup_common.h \
- test/setup_common.cpp \
test/fuzz/fuzz.cpp \
test/fuzz/fuzz.h \
test/fuzz/FuzzedDataProvider.h
@@ -75,6 +70,7 @@ FUZZ_SUITE_LD_COMMON = \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
+ $(LIBTEST_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBUNIVALUE) \
@@ -84,7 +80,6 @@ FUZZ_SUITE_LD_COMMON = \
$(LIBMEMENV) \
$(LIBSECP256K1) \
$(EVENT_LIBS) \
- $(CRYPTO_LIBS) \
$(EVENT_PTHREADS_LIBS)
# test_bitcoin binary #
@@ -120,6 +115,7 @@ BITCOIN_TESTS =\
test/key_io_tests.cpp \
test/key_tests.cpp \
test/limitedmap_tests.cpp \
+ test/logging_tests.cpp \
test/dbwrapper_tests.cpp \
test/validation_tests.cpp \
test/mempool_tests.cpp \
@@ -144,6 +140,7 @@ BITCOIN_TESTS =\
test/script_standard_tests.cpp \
test/scriptnum_tests.cpp \
test/serialize_tests.cpp \
+ test/settings_tests.cpp \
test/sighash_tests.cpp \
test/sigopcount_tests.cpp \
test/skiplist_tests.cpp \
@@ -189,7 +186,7 @@ endif
test_test_bitcoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS)
-test_test_bitcoin_LDADD =
+test_test_bitcoin_LDADD = $(LIBTEST_UTIL)
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
endif
@@ -198,7 +195,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-test_test_bitcoin_LDADD += $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
+test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
if ENABLE_ZMQ
@@ -242,6 +239,12 @@ test_fuzz_banentry_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_banentry_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_banentry_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_bech32_SOURCES = $(FUZZ_SUITE) test/fuzz/bech32.cpp
+test_fuzz_bech32_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_bech32_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_bech32_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_bech32_LDADD = $(FUZZ_SUITE_LD_COMMON)
+
test_fuzz_txundo_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_txundo_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTXUNDO_DESERIALIZE=1
test_fuzz_txundo_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
@@ -278,6 +281,12 @@ test_fuzz_parse_iso8601_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_iso8601_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_parse_iso8601_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_psbt_SOURCES = $(FUZZ_SUITE) test/fuzz/psbt.cpp
+test_fuzz_psbt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_psbt_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_psbt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_psbt_LDADD = $(FUZZ_SUITE_LD_COMMON)
+
test_fuzz_script_SOURCES = $(FUZZ_SUITE) test/fuzz/script.cpp
test_fuzz_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
new file mode 100644
index 0000000000..cf55d141b0
--- /dev/null
+++ b/src/Makefile.test_util.include
@@ -0,0 +1,31 @@
+# Copyright (c) 2013-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.
+
+LIBTEST_UTIL=libtest_util.a
+
+EXTRA_LIBRARIES += \
+ $(LIBTEST_UTIL)
+
+TEST_UTIL_H = \
+ test/util/blockfilter.h \
+ test/util/logging.h \
+ test/util/setup_common.h \
+ test/util/str.h \
+ test/util/transaction_utils.h
+
+libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libtest_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+libtest_util_a_SOURCES = \
+ test/util/blockfilter.cpp \
+ test/util/logging.cpp \
+ test/util/setup_common.cpp \
+ test/util/str.cpp \
+ test/util/transaction_utils.cpp \
+ $(TEST_UTIL_H)
+
+LIBTEST_UTIL += $(LIBBITCOIN_SERVER)
+LIBTEST_UTIL += $(LIBBITCOIN_COMMON)
+LIBTEST_UTIL += $(LIBBITCOIN_UTIL)
+LIBTEST_UTIL += $(LIBBITCOIN_CRYPTO_BASE)
+
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 1b6b1736a9..cc159eb191 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -5,7 +5,7 @@
#include <bench/bench.h>
#include <chainparams.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <validation.h>
#include <algorithm>
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index c9947f192e..1c025e29d3 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -10,7 +10,7 @@
#include <script/script.h>
#include <script/standard.h>
#include <streams.h>
-#include <test/lib/transaction_utils.h>
+#include <test/util/transaction_utils.h>
#include <array>
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index d7b6891503..592fcbe8dd 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -265,7 +265,7 @@ public:
result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]);
result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
- if (!batch[ID_WALLETINFO].isNull()) {
+ if (!batch[ID_WALLETINFO]["result"].isNull()) {
result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]);
result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]);
result.pushKV("keypoololdest", batch[ID_WALLETINFO]["result"]["keypoololdest"]);
diff --git a/src/bloom.h b/src/bloom.h
index 7d3aa878b0..c3f64ba4bc 100644
--- a/src/bloom.h
+++ b/src/bloom.h
@@ -115,9 +115,6 @@ public:
class CRollingBloomFilter
{
public:
- // A random bloom filter calls GetRand() at creation time.
- // Don't create global CRollingBloomFilter objects, as they may be
- // constructed before the randomizer is properly initialized.
CRollingBloomFilter(const unsigned int nElements, const double nFPRate);
void insert(const std::vector<unsigned char>& vKey);
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index dd4d3e97ac..0a1cb858ef 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -71,7 +71,7 @@ public:
consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5
consensus.SegwitHeight = 481824; // 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893
- consensus.MinBIP9WarningHeight = consensus.SegwitHeight + consensus.nMinerConfirmationWindow;
+ consensus.MinBIP9WarningHeight = 483840; // segwit activation height + miner confirmation window
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
@@ -178,7 +178,7 @@ public:
consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
consensus.CSVHeight = 770112; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb
consensus.SegwitHeight = 834624; // 00000000002b980fcd729daaa248fd9316a5200e9b367f4ff2c42453e84201ca
- consensus.MinBIP9WarningHeight = consensus.SegwitHeight + consensus.nMinerConfirmationWindow;
+ consensus.MinBIP9WarningHeight = 836640; // segwit activation height + miner confirmation window
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
diff --git a/src/compat/cpuid.h b/src/compat/cpuid.h
new file mode 100644
index 0000000000..0877ad47d3
--- /dev/null
+++ b/src/compat/cpuid.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2017-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_COMPAT_CPUID_H
+#define BITCOIN_COMPAT_CPUID_H
+
+#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
+#define HAVE_GETCPUID
+
+#include <cpuid.h>
+
+// We can't use cpuid.h's __get_cpuid as it does not support subleafs.
+void static inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
+{
+#ifdef __GNUC__
+ __cpuid_count(leaf, subleaf, a, b, c, d);
+#else
+ __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
+#endif
+}
+
+#endif // defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
+#endif // BITCOIN_COMPAT_CPUID_H
diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp
index 3257ee7f97..dda7e5230f 100644
--- a/src/crypto/sha256.cpp
+++ b/src/crypto/sha256.cpp
@@ -8,9 +8,10 @@
#include <assert.h>
#include <string.h>
+#include <compat/cpuid.h>
+
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
#if defined(USE_ASM)
-#include <cpuid.h>
namespace sha256_sse4
{
void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks);
@@ -546,18 +547,7 @@ bool SelfTest() {
return true;
}
-
#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__))
-// We can't use cpuid.h's __get_cpuid as it does not support subleafs.
-void inline cpuid(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
-{
-#ifdef __GNUC__
- __cpuid_count(leaf, subleaf, a, b, c, d);
-#else
- __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
-#endif
-}
-
/** Check whether the OS has enabled AVX registers. */
bool AVXEnabled()
{
@@ -572,7 +562,7 @@ bool AVXEnabled()
std::string SHA256AutoDetect()
{
std::string ret = "standard";
-#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__))
+#if defined(USE_ASM) && defined(HAVE_GETCPUID)
bool have_sse4 = false;
bool have_xsave = false;
bool have_avx = false;
@@ -589,7 +579,7 @@ std::string SHA256AutoDetect()
(void)enabled_avx;
uint32_t eax, ebx, ecx, edx;
- cpuid(1, 0, eax, ebx, ecx, edx);
+ GetCPUID(1, 0, eax, ebx, ecx, edx);
have_sse4 = (ecx >> 19) & 1;
have_xsave = (ecx >> 27) & 1;
have_avx = (ecx >> 28) & 1;
@@ -597,7 +587,7 @@ std::string SHA256AutoDetect()
enabled_avx = AVXEnabled();
}
if (have_sse4) {
- cpuid(7, 0, eax, ebx, ecx, edx);
+ GetCPUID(7, 0, eax, ebx, ecx, edx);
have_avx2 = (ebx >> 5) & 1;
have_shani = (ebx >> 29) & 1;
}
diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h
index 4118ac1b18..fc7dd1b87e 100644
--- a/src/crypto/sha512.h
+++ b/src/crypto/sha512.h
@@ -23,6 +23,7 @@ public:
CSHA512& Write(const unsigned char* data, size_t len);
void Finalize(unsigned char hash[OUTPUT_SIZE]);
CSHA512& Reset();
+ uint64_t Size() const { return bytes; }
};
#endif // BITCOIN_CRYPTO_SHA512_H
diff --git a/src/init.cpp b/src/init.cpp
index 1a99ca9abc..e7dda59590 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -281,9 +281,9 @@ void Shutdown(NodeContext& node)
node.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
- GetMainSignals().UnregisterWithMempoolSignals(mempool);
globalVerifyHandle.reset();
ECC_Stop();
+ if (node.mempool) node.mempool = nullptr;
LogPrintf("%s: done\n", __func__);
}
@@ -372,7 +372,7 @@ void SetupServerArgs()
gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- gArgs.AddArg("-loadblock=<file>", "Imports blocks from external blk000??.dat file on startup", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-loadblock=<file>", "Imports blocks from external file on startup", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-maxmempool=<n>", strprintf("Keep the transaction memory pool below <n> megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-maxorphantx=<n>", strprintf("Keep at most <n> unconnectable transactions in memory (default: %u)", DEFAULT_MAX_ORPHAN_TRANSACTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-mempoolexpiry=<n>", strprintf("Do not keep transactions in the mempool longer than <n> hours (default: %u)", DEFAULT_MEMPOOL_EXPIRY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -563,9 +563,7 @@ std::string LicenseInfo()
"\n" +
"\n" +
_("This is experimental software.").translated + "\n" +
- strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s").translated, "COPYING", "<https://opensource.org/licenses/MIT>") + "\n" +
- "\n" +
- strprintf(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.").translated, "<https://www.openssl.org>") +
+ strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s").translated, "COPYING", "<https://opensource.org/licenses/MIT>") +
"\n";
}
@@ -684,20 +682,6 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
LoadGenesisBlock(chainparams);
}
- // hardcoded $DATADIR/bootstrap.dat
- fs::path pathBootstrap = GetDataDir() / "bootstrap.dat";
- if (fs::exists(pathBootstrap)) {
- FILE *file = fsbridge::fopen(pathBootstrap, "rb");
- if (file) {
- fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
- LogPrintf("Importing bootstrap.dat...\n");
- LoadExternalBlockFile(chainparams, file);
- RenameOver(pathBootstrap, pathBootstrapOld);
- } else {
- LogPrintf("Warning: Could not open bootstrap file %s\n", pathBootstrap.string());
- }
- }
-
// -loadblock=
for (const fs::path& path : vImportFiles) {
FILE *file = fsbridge::fopen(path, "rb");
@@ -1061,15 +1045,6 @@ bool AppInitParameterInteraction()
incrementalRelayFee = CFeeRate(n);
}
- // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
- nScriptCheckThreads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
- if (nScriptCheckThreads <= 0)
- nScriptCheckThreads += GetNumCores();
- if (nScriptCheckThreads <= 1)
- nScriptCheckThreads = 0;
- else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)
- nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;
-
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
int64_t nPruneArg = gArgs.GetArg("-prune", 0);
if (nPruneArg < 0) {
@@ -1256,18 +1231,37 @@ bool AppInitMain(NodeContext& node)
InitSignatureCache();
InitScriptExecutionCache();
- LogPrintf("Script verification uses %d additional threads\n", std::max(nScriptCheckThreads - 1, 0));
- if (nScriptCheckThreads) {
- for (int i=0; i<nScriptCheckThreads-1; i++)
+ int script_threads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
+ if (script_threads <= 0) {
+ // -par=0 means autodetect (number of cores - 1 script threads)
+ // -par=-n means "leave n cores free" (number of cores - n - 1 script threads)
+ script_threads += GetNumCores();
+ }
+
+ // Subtract 1 because the main thread counts towards the par threads
+ script_threads = std::max(script_threads - 1, 0);
+
+ // Number of script-checking threads <= MAX_SCRIPTCHECK_THREADS
+ script_threads = std::min(script_threads, MAX_SCRIPTCHECK_THREADS);
+
+ LogPrintf("Script verification uses %d additional threads\n", script_threads);
+ if (script_threads >= 1) {
+ g_parallel_script_checks = true;
+ for (int i = 0; i < script_threads; ++i) {
threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
+ }
}
// Start the lightweight task scheduler thread
CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler);
threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
+ // Gather some entropy once per minute.
+ scheduler.scheduleEvery([]{
+ RandAddPeriodic();
+ }, 60000);
+
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
- GetMainSignals().RegisterWithMempoolSignals(mempool);
// Create client interfaces for wallets that are supposed to be loaded
// according to -wallet and -disablewallet options. This only constructs
@@ -1640,6 +1634,11 @@ bool AppInitMain(NodeContext& node)
return false;
}
+ // Now that the chain state is loaded, make mempool generally available in the node context. For example the
+ // connection manager, wallet, or RPC threads, which are all started after this, may use it from the node context.
+ assert(!node.mempool);
+ node.mempool = &::mempool;
+
fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION);
// Allowed to fail as this file IS missing on first startup.
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 23099a7799..26856a00d3 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -58,12 +58,6 @@ class LockImpl : public Chain::Lock, public UniqueLock<CCriticalSection>
}
return nullopt;
}
- int getBlockDepth(const uint256& hash) override
- {
- const Optional<int> tip_height = getHeight();
- const Optional<int> height = getBlockHeight(hash);
- return tip_height && height ? *tip_height - *height + 1 : 0;
- }
uint256 getBlockHash(int height) override
{
LockAssertion lock(::cs_main);
@@ -182,11 +176,11 @@ public:
const CBlockIndex* index,
const std::vector<CTransactionRef>& tx_conflicted) override
{
- m_notifications->BlockConnected(*block, tx_conflicted);
+ m_notifications->BlockConnected(*block, tx_conflicted, index->nHeight);
}
- void BlockDisconnected(const std::shared_ptr<const CBlock>& block) override
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* index) override
{
- m_notifications->BlockDisconnected(*block);
+ m_notifications->BlockDisconnected(*block, index->nHeight);
}
void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override
{
@@ -353,13 +347,11 @@ public:
{
return MakeUnique<NotificationsHandlerImpl>(*this, notifications);
}
- void waitForNotificationsIfNewBlocksConnected(const uint256& old_tip) override
+ void waitForNotificationsIfTipChanged(const uint256& old_tip) override
{
if (!old_tip.IsNull()) {
LOCK(::cs_main);
if (old_tip == ::ChainActive().Tip()->GetBlockHash()) return;
- CBlockIndex* block = LookupBlockIndex(old_tip);
- if (block && block->GetAncestor(::ChainActive().Height()) == ::ChainActive().Tip()) return;
}
SyncWithValidationInterfaceQueue();
}
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 82eeba1160..349af152d5 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -76,10 +76,6 @@ public:
//! included in the current chain.
virtual Optional<int> getBlockHeight(const uint256& hash) = 0;
- //! Get block depth. Returns 1 for chain tip, 2 for preceding block, and
- //! so on. Returns 0 for a block not included in the current chain.
- virtual int getBlockDepth(const uint256& hash) = 0;
-
//! Get block hash. Height must be valid or this function will abort.
virtual uint256 getBlockHash(int height) = 0;
@@ -226,8 +222,8 @@ public:
virtual ~Notifications() {}
virtual void TransactionAddedToMempool(const CTransactionRef& tx) {}
virtual void TransactionRemovedFromMempool(const CTransactionRef& ptx) {}
- virtual void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& tx_conflicted) {}
- virtual void BlockDisconnected(const CBlock& block) {}
+ virtual void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& tx_conflicted, int height) {}
+ virtual void BlockDisconnected(const CBlock& block, int height) {}
virtual void UpdatedBlockTip() {}
virtual void ChainStateFlushed(const CBlockLocator& locator) {}
};
@@ -236,9 +232,8 @@ public:
virtual std::unique_ptr<Handler> handleNotifications(Notifications& notifications) = 0;
//! Wait for pending notifications to be processed unless block hash points to the current
- //! chain tip, or to a possible descendant of the current chain tip that isn't currently
- //! connected.
- virtual void waitForNotificationsIfNewBlocksConnected(const uint256& old_tip) = 0;
+ //! chain tip.
+ virtual void waitForNotificationsIfTipChanged(const uint256& old_tip) = 0;
//! Register handler for RPC. Command is not copied, so reference
//! needs to remain valid until Handler is disconnected.
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index b6ede08b14..94c6e8c7b7 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -18,8 +18,9 @@
#include <wallet/feebumper.h>
#include <wallet/fees.h>
#include <wallet/ismine.h>
-#include <wallet/rpcwallet.h>
#include <wallet/load.h>
+#include <wallet/psbtwallet.h>
+#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <memory>
@@ -31,7 +32,7 @@ namespace interfaces {
namespace {
//! Construct wallet tx struct.
-WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, const CWalletTx& wtx)
+WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
{
WalletTx result;
result.tx = wtx.tx;
@@ -49,7 +50,7 @@ WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, co
wallet.IsMine(result.txout_address.back()) :
ISMINE_NO);
}
- result.credit = wtx.GetCredit(locked_chain, ISMINE_ALL);
+ result.credit = wtx.GetCredit(ISMINE_ALL);
result.debit = wtx.GetDebit(ISMINE_ALL);
result.change = wtx.GetChange();
result.time = wtx.GetTxTime();
@@ -63,21 +64,20 @@ WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const C
{
WalletTxStatus result;
result.block_height = locked_chain.getBlockHeight(wtx.m_confirm.hashBlock).get_value_or(std::numeric_limits<int>::max());
- result.blocks_to_maturity = wtx.GetBlocksToMaturity(locked_chain);
- result.depth_in_main_chain = wtx.GetDepthInMainChain(locked_chain);
+ result.blocks_to_maturity = wtx.GetBlocksToMaturity();
+ result.depth_in_main_chain = wtx.GetDepthInMainChain();
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
result.is_final = locked_chain.checkFinalTx(*wtx.tx);
result.is_trusted = wtx.IsTrusted(locked_chain);
result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase();
- result.is_in_main_chain = wtx.IsInMainChain(locked_chain);
+ result.is_in_main_chain = wtx.IsInMainChain();
return result;
}
//! Construct wallet TxOut struct.
-WalletTxOut MakeWalletTxOut(interfaces::Chain::Lock& locked_chain,
- CWallet& wallet,
+WalletTxOut MakeWalletTxOut(CWallet& wallet,
const CWalletTx& wtx,
int n,
int depth) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@@ -86,7 +86,7 @@ WalletTxOut MakeWalletTxOut(interfaces::Chain::Lock& locked_chain,
result.txout = wtx.tx->vout[n];
result.time = wtx.GetTxTime();
result.depth_in_main_chain = depth;
- result.is_spent = wallet.IsSpent(locked_chain, wtx.GetHash(), n);
+ result.is_spent = wallet.IsSpent(wtx.GetHash(), n);
return result;
}
@@ -117,8 +117,22 @@ public:
std::string error;
return m_wallet->GetNewDestination(type, label, dest, error);
}
- bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet->GetLegacyScriptPubKeyMan()->GetPubKey(address, pub_key); }
- bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet->GetLegacyScriptPubKeyMan()->GetKey(address, key); }
+ bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) override
+ {
+ const SigningProvider* provider = m_wallet->GetSigningProvider(script);
+ if (provider) {
+ return provider->GetPubKey(address, pub_key);
+ }
+ return false;
+ }
+ bool getPrivKey(const CScript& script, const CKeyID& address, CKey& key) override
+ {
+ const SigningProvider* provider = m_wallet->GetSigningProvider(script);
+ if (provider) {
+ return provider->GetKey(address, key);
+ }
+ return false;
+ }
bool isSpendable(const CTxDestination& dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; }
bool haveWatchOnly() override
{
@@ -170,12 +184,14 @@ public:
bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override
{
LOCK(m_wallet->cs_wallet);
- return m_wallet->AddDestData(dest, key, value);
+ WalletBatch batch{m_wallet->GetDatabase()};
+ return m_wallet->AddDestData(batch, dest, key, value);
}
bool eraseDestData(const CTxDestination& dest, const std::string& key) override
{
LOCK(m_wallet->cs_wallet);
- return m_wallet->EraseDestData(dest, key);
+ WalletBatch batch{m_wallet->GetDatabase()};
+ return m_wallet->EraseDestData(batch, dest, key);
}
std::vector<std::string> getDestValues(const std::string& prefix) override
{
@@ -235,7 +251,7 @@ public:
{
auto locked_chain = m_wallet->chain().lock();
LOCK(m_wallet->cs_wallet);
- return m_wallet->AbandonTransaction(*locked_chain, txid);
+ return m_wallet->AbandonTransaction(txid);
}
bool transactionCanBeBumped(const uint256& txid) override
{
@@ -282,7 +298,7 @@ public:
LOCK(m_wallet->cs_wallet);
auto mi = m_wallet->mapWallet.find(txid);
if (mi != m_wallet->mapWallet.end()) {
- return MakeWalletTx(*locked_chain, *m_wallet, mi->second);
+ return MakeWalletTx(*m_wallet, mi->second);
}
return {};
}
@@ -293,7 +309,7 @@ public:
std::vector<WalletTx> result;
result.reserve(m_wallet->mapWallet.size());
for (const auto& entry : m_wallet->mapWallet) {
- result.emplace_back(MakeWalletTx(*locked_chain, *m_wallet, entry.second));
+ result.emplace_back(MakeWalletTx(*m_wallet, entry.second));
}
return result;
}
@@ -338,10 +354,18 @@ public:
in_mempool = mi->second.InMempool();
order_form = mi->second.vOrderForm;
tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
- return MakeWalletTx(*locked_chain, *m_wallet, mi->second);
+ return MakeWalletTx(*m_wallet, mi->second);
}
return {};
}
+ TransactionError fillPSBT(PartiallySignedTransaction& psbtx,
+ bool& complete,
+ int sighash_type = 1 /* SIGHASH_ALL */,
+ bool sign = true,
+ bool bip32derivs = false) override
+ {
+ return FillPSBT(m_wallet.get(), psbtx, complete, sighash_type, sign, bip32derivs);
+ }
WalletBalances getBalances() override
{
const auto bal = m_wallet->GetBalance();
@@ -407,7 +431,7 @@ public:
auto& group = result[entry.first];
for (const auto& coin : entry.second) {
group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i),
- MakeWalletTxOut(*locked_chain, *m_wallet, *coin.tx, coin.i, coin.nDepth));
+ MakeWalletTxOut(*m_wallet, *coin.tx, coin.i, coin.nDepth));
}
}
return result;
@@ -422,9 +446,9 @@ public:
result.emplace_back();
auto it = m_wallet->mapWallet.find(output.hash);
if (it != m_wallet->mapWallet.end()) {
- int depth = it->second.GetDepthInMainChain(*locked_chain);
+ int depth = it->second.GetDepthInMainChain();
if (depth >= 0) {
- result.back() = MakeWalletTxOut(*locked_chain, *m_wallet, it->second, output.n, depth);
+ result.back() = MakeWalletTxOut(*m_wallet, it->second, output.n, depth);
}
}
}
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index a96b93b4c3..8d2b8a2eca 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -14,6 +14,7 @@
#include <functional>
#include <map>
#include <memory>
+#include <psbt.h>
#include <stdint.h>
#include <string>
#include <tuple>
@@ -81,10 +82,10 @@ public:
virtual bool getNewDestination(const OutputType type, const std::string label, CTxDestination& dest) = 0;
//! Get public key.
- virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0;
+ virtual bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) = 0;
//! Get private key.
- virtual bool getPrivKey(const CKeyID& address, CKey& key) = 0;
+ virtual bool getPrivKey(const CScript& script, const CKeyID& address, CKey& key) = 0;
//! Return whether wallet has private key.
virtual bool isSpendable(const CTxDestination& dest) = 0;
@@ -194,6 +195,13 @@ public:
bool& in_mempool,
int& num_blocks) = 0;
+ //! Fill PSBT.
+ virtual TransactionError fillPSBT(PartiallySignedTransaction& psbtx,
+ bool& complete,
+ int sighash_type = 1 /* SIGHASH_ALL */,
+ bool sign = true,
+ bool bip32derivs = false) = 0;
+
//! Get balances.
virtual WalletBalances getBalances() = 0;
diff --git a/src/key.cpp b/src/key.cpp
index 3ba21753a2..10b6668aae 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -83,13 +83,13 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou
* <http://www.secg.org/sec1-v2.pdf>. The optional parameters and publicKey fields are
* included.
*
- * privkey must point to an output buffer of length at least CKey::PRIVATE_KEY_SIZE bytes.
+ * privkey must point to an output buffer of length at least CKey::SIZE bytes.
* privkeylen must initially be set to the size of the privkey buffer. Upon return it
* will be set to the number of bytes used in the buffer.
* key32 must point to a 32-byte raw private key.
*/
static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, bool compressed) {
- assert(*privkeylen >= CKey::PRIVATE_KEY_SIZE);
+ assert(*privkeylen >= CKey::SIZE);
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) {
@@ -115,11 +115,11 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
- pubkeylen = CPubKey::COMPRESSED_PUBLIC_KEY_SIZE;
+ pubkeylen = CPubKey::COMPRESSED_SIZE;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED);
ptr += pubkeylen;
*privkeylen = ptr - privkey;
- assert(*privkeylen == CKey::COMPRESSED_PRIVATE_KEY_SIZE);
+ assert(*privkeylen == CKey::COMPRESSED_SIZE);
} else {
static const unsigned char begin[] = {
0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20
@@ -141,11 +141,11 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
- pubkeylen = CPubKey::PUBLIC_KEY_SIZE;
+ pubkeylen = CPubKey::SIZE;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
ptr += pubkeylen;
*privkeylen = ptr - privkey;
- assert(*privkeylen == CKey::PRIVATE_KEY_SIZE);
+ assert(*privkeylen == CKey::SIZE);
}
return 1;
}
@@ -173,8 +173,8 @@ CPrivKey CKey::GetPrivKey() const {
CPrivKey privkey;
int ret;
size_t privkeylen;
- privkey.resize(PRIVATE_KEY_SIZE);
- privkeylen = PRIVATE_KEY_SIZE;
+ privkey.resize(SIZE);
+ privkeylen = SIZE;
ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed);
assert(ret);
privkey.resize(privkeylen);
@@ -184,7 +184,7 @@ CPrivKey CKey::GetPrivKey() const {
CPubKey CKey::GetPubKey() const {
assert(fValid);
secp256k1_pubkey pubkey;
- size_t clen = CPubKey::PUBLIC_KEY_SIZE;
+ size_t clen = CPubKey::SIZE;
CPubKey result;
int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, begin());
assert(ret);
@@ -276,7 +276,7 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
if ((nChild >> 31) == 0) {
CPubKey pubkey = GetPubKey();
- assert(pubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE);
+ assert(pubkey.size() == CPubKey::COMPRESSED_SIZE);
BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, vout.data());
} else {
assert(size() == 32);
diff --git a/src/key.h b/src/key.h
index 6a86b0411d..1d401a0c8a 100644
--- a/src/key.h
+++ b/src/key.h
@@ -19,7 +19,7 @@
/**
* secure_allocator is defined in allocators.h
* CPrivKey is a serialized private key, with all parameters included
- * (PRIVATE_KEY_SIZE bytes)
+ * (SIZE bytes)
*/
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
@@ -30,15 +30,15 @@ public:
/**
* secp256k1:
*/
- static const unsigned int PRIVATE_KEY_SIZE = 279;
- static const unsigned int COMPRESSED_PRIVATE_KEY_SIZE = 214;
+ static const unsigned int SIZE = 279;
+ static const unsigned int COMPRESSED_SIZE = 214;
/**
* see www.keylength.com
* script supports up to 75 for single byte push
*/
static_assert(
- PRIVATE_KEY_SIZE >= COMPRESSED_PRIVATE_KEY_SIZE,
- "COMPRESSED_PRIVATE_KEY_SIZE is larger than PRIVATE_KEY_SIZE");
+ SIZE >= COMPRESSED_SIZE,
+ "COMPRESSED_SIZE is larger than SIZE");
private:
//! Whether this private key is valid. We check for correctness when modifying the key
diff --git a/src/logging.cpp b/src/logging.cpp
index 60ab486198..9f6b5ede12 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -67,6 +67,9 @@ bool BCLog::Logger::StartLogging()
if (m_print_to_file) FileWriteStr(s, m_fileout);
if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
+ for (const auto& cb : m_print_callbacks) {
+ cb(s);
+ }
m_msgs_before_open.pop_front();
}
@@ -81,6 +84,7 @@ void BCLog::Logger::DisconnectTestLogger()
m_buffering = true;
if (m_fileout != nullptr) fclose(m_fileout);
m_fileout = nullptr;
+ m_print_callbacks.clear();
}
void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
@@ -91,7 +95,15 @@ void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
bool BCLog::Logger::EnableCategory(const std::string& str)
{
BCLog::LogFlags flag;
- if (!GetLogCategory(flag, str)) return false;
+ if (!GetLogCategory(flag, str)) {
+ if (str == "db") {
+ // DEPRECATION: Added in 0.20, should start returning an error in 0.21
+ LogPrintf("Warning: logging category 'db' is deprecated, use 'walletdb' instead\n");
+ EnableCategory(BCLog::WALLETDB);
+ return true;
+ }
+ return false;
+ }
EnableCategory(flag);
return true;
}
@@ -135,7 +147,7 @@ const CLogCategoryDesc LogCategories[] =
{BCLog::HTTP, "http"},
{BCLog::BENCH, "bench"},
{BCLog::ZMQ, "zmq"},
- {BCLog::DB, "db"},
+ {BCLog::WALLETDB, "walletdb"},
{BCLog::RPC, "rpc"},
{BCLog::ESTIMATEFEE, "estimatefee"},
{BCLog::ADDRMAN, "addrman"},
@@ -270,6 +282,9 @@ void BCLog::Logger::LogPrintStr(const std::string& str)
fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
fflush(stdout);
}
+ for (const auto& cb : m_print_callbacks) {
+ cb(str_prefixed);
+ }
if (m_print_to_file) {
assert(m_fileout != nullptr);
diff --git a/src/logging.h b/src/logging.h
index e37c0c823b..a2caef51a8 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -39,7 +39,7 @@ namespace BCLog {
HTTP = (1 << 3),
BENCH = (1 << 4),
ZMQ = (1 << 5),
- DB = (1 << 6),
+ WALLETDB = (1 << 6),
RPC = (1 << 7),
ESTIMATEFEE = (1 << 8),
ADDRMAN = (1 << 9),
@@ -77,6 +77,9 @@ namespace BCLog {
std::string LogTimestampStr(const std::string& str);
+ /** Slots that connect to the print signal */
+ std::list<std::function<void(const std::string&)>> m_print_callbacks /* GUARDED_BY(m_cs) */ {};
+
public:
bool m_print_to_console = false;
bool m_print_to_file = false;
@@ -95,7 +98,22 @@ namespace BCLog {
bool Enabled() const
{
std::lock_guard<std::mutex> scoped_lock(m_cs);
- return m_buffering || m_print_to_console || m_print_to_file;
+ return m_buffering || m_print_to_console || m_print_to_file || !m_print_callbacks.empty();
+ }
+
+ /** Connect a slot to the print signal and return the connection */
+ std::list<std::function<void(const std::string&)>>::iterator PushBackCallback(std::function<void(const std::string&)> fun)
+ {
+ std::lock_guard<std::mutex> scoped_lock(m_cs);
+ m_print_callbacks.push_back(std::move(fun));
+ return --m_print_callbacks.end();
+ }
+
+ /** Delete a connection */
+ void DeleteCallback(std::list<std::function<void(const std::string&)>>::iterator it)
+ {
+ std::lock_guard<std::mutex> scoped_lock(m_cs);
+ m_print_callbacks.erase(it);
}
/** Start logging (and flush all buffered messages) */
diff --git a/src/logging/timer.h b/src/logging/timer.h
new file mode 100644
index 0000000000..34dbb942c5
--- /dev/null
+++ b/src/logging/timer.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 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_LOGGING_TIMER_H
+#define BITCOIN_LOGGING_TIMER_H
+
+#include <logging.h>
+#include <util/macros.h>
+#include <util/time.h>
+
+#include <chrono>
+#include <string>
+
+
+namespace BCLog {
+
+//! RAII-style object that outputs timing information to logs.
+template <typename TimeType>
+class Timer
+{
+public:
+ //! If log_category is left as the default, end_msg will log unconditionally
+ //! (instead of being filtered by category).
+ Timer(
+ std::string prefix,
+ std::string end_msg,
+ BCLog::LogFlags log_category = BCLog::LogFlags::ALL) :
+ m_prefix(std::move(prefix)),
+ m_title(std::move(end_msg)),
+ m_log_category(log_category)
+ {
+ this->Log(strprintf("%s started", m_title));
+ m_start_t = GetTime<std::chrono::microseconds>();
+ }
+
+ ~Timer()
+ {
+ this->Log(strprintf("%s completed", m_title));
+ }
+
+ void Log(const std::string& msg)
+ {
+ const std::string full_msg = this->LogMsg(msg);
+
+ if (m_log_category == BCLog::LogFlags::ALL) {
+ LogPrintf("%s\n", full_msg);
+ } else {
+ LogPrint(m_log_category, "%s\n", full_msg);
+ }
+ }
+
+ std::string LogMsg(const std::string& msg)
+ {
+ const auto end_time = GetTime<std::chrono::microseconds>() - m_start_t;
+ if (m_start_t.count() <= 0) {
+ return strprintf("%s: %s", m_prefix, msg);
+ }
+
+ std::string units = "";
+ float divisor = 1;
+
+ if (std::is_same<TimeType, std::chrono::microseconds>::value) {
+ units = "μs";
+ } else if (std::is_same<TimeType, std::chrono::milliseconds>::value) {
+ units = "ms";
+ divisor = 1000.;
+ } else if (std::is_same<TimeType, std::chrono::seconds>::value) {
+ units = "s";
+ divisor = 1000. * 1000.;
+ }
+
+ const float time_ms = end_time.count() / divisor;
+ return strprintf("%s: %s (%.2f%s)", m_prefix, msg, time_ms, units);
+ }
+
+private:
+ std::chrono::microseconds m_start_t{};
+
+ //! Log prefix; usually the name of the function this was created in.
+ const std::string m_prefix{};
+
+ //! A descriptive message of what is being timed.
+ const std::string m_title{};
+
+ //! Forwarded on to LogPrint if specified - has the effect of only
+ //! outputing the timing log when a particular debug= category is specified.
+ const BCLog::LogFlags m_log_category{};
+
+};
+
+} // namespace BCLog
+
+
+#define LOG_TIME_MICROS(end_msg, ...) \
+ BCLog::Timer<std::chrono::microseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, ## __VA_ARGS__)
+#define LOG_TIME_MILLIS(end_msg, ...) \
+ BCLog::Timer<std::chrono::milliseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, ## __VA_ARGS__)
+#define LOG_TIME_SECONDS(end_msg, ...) \
+ BCLog::Timer<std::chrono::seconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, ## __VA_ARGS__)
+
+
+#endif // BITCOIN_LOGGING_TIMER_H
diff --git a/src/miner.cpp b/src/miner.cpp
index 1c9174ee07..2f6feb9e02 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -435,7 +435,7 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
++nExtraNonce;
unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
CMutableTransaction txCoinbase(*pblock->vtx[0]);
- txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
+ txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce));
assert(txCoinbase.vin[0].scriptSig.size() <= 100);
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
diff --git a/src/net.cpp b/src/net.cpp
index 674f2ecf24..84692d2a79 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -2666,11 +2666,10 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
addrBind(addrBindIn),
fInbound(fInboundIn),
nKeyedNetGroup(nKeyedNetGroupIn),
- addrKnown(5000, 0.001),
// Don't relay addr messages to peers that we connect to as block-relay-only
// peers (to prevent adversaries from inferring these links from addr
// traffic).
- m_addr_relay_peer(!block_relay_only),
+ m_addr_known{block_relay_only ? nullptr : MakeUnique<CRollingBloomFilter>(5000, 0.001)},
id(idIn),
nLocalHostNonce(nLocalHostNonceIn),
nLocalServices(nLocalServicesIn),
diff --git a/src/net.h b/src/net.h
index 1bbcc89478..9cd3b769ec 100644
--- a/src/net.h
+++ b/src/net.h
@@ -776,13 +776,12 @@ public:
// flood relay
std::vector<CAddress> vAddrToSend;
- CRollingBloomFilter addrKnown;
+ const std::unique_ptr<CRollingBloomFilter> m_addr_known;
bool fGetAddr{false};
int64_t nNextAddrSend GUARDED_BY(cs_sendProcessing){0};
int64_t nNextLocalAddrSend GUARDED_BY(cs_sendProcessing){0};
- const bool m_addr_relay_peer;
- bool IsAddrRelayPeer() const { return m_addr_relay_peer; }
+ bool IsAddrRelayPeer() const { return m_addr_known != nullptr; }
// List of block ids we still have announce.
// There is no final sorting before sending, as they are always sent immediately
@@ -809,7 +808,7 @@ public:
bool fSendMempool GUARDED_BY(cs_tx_inventory){false};
// Last time a "MEMPOOL" request was serviced.
std::atomic<std::chrono::seconds> m_last_mempool_req{std::chrono::seconds{0}};
- int64_t nNextInvSend{0};
+ std::chrono::microseconds nNextInvSend{0};
CCriticalSection cs_feeFilter;
// Minimum fee rate with which to filter inv's to this node
@@ -931,7 +930,8 @@ public:
void AddAddressKnown(const CAddress& _addr)
{
- addrKnown.insert(_addr.GetKey());
+ assert(m_addr_known);
+ m_addr_known->insert(_addr.GetKey());
}
void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand)
@@ -939,7 +939,8 @@ public:
// Known checking here is only to save space from duplicates.
// SendMessages will filter it again for knowns that were added
// after addresses were pushed.
- if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) {
+ assert(m_addr_known);
+ if (_addr.IsValid() && !m_addr_known->contains(_addr.GetKey())) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr;
} else {
@@ -990,11 +991,13 @@ public:
void MaybeSetAddrName(const std::string& addrNameIn);
};
-
-
-
-
/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
int64_t PoissonNextSend(int64_t now, int average_interval_seconds);
+/** Wrapper to return mockable type */
+inline std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, std::chrono::seconds average_interval)
+{
+ return std::chrono::microseconds{PoissonNextSend(now.count(), average_interval.count())};
+}
+
#endif // BITCOIN_NET_H
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index d03817834d..f42a26ca3e 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1340,7 +1340,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connma
// Relay to a limited number of other nodes
// Use deterministic randomness to send to the same nodes for 24 hours
- // at a time so the addrKnowns of the chosen nodes prevent repeats
+ // at a time so the m_addr_knowns of the chosen nodes prevent repeats
uint64_t hashAddr = addr.GetHash();
const CSipHasher hasher = connman->GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60));
FastRandomContext insecure_rand;
@@ -3575,6 +3575,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Address refresh broadcast
int64_t nNow = GetTimeMicros();
+ auto current_time = GetTime<std::chrono::microseconds>();
+
if (pto->IsAddrRelayPeer() && !::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
AdvertiseLocal(pto);
pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
@@ -3587,11 +3589,12 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
std::vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
+ assert(pto->m_addr_known);
for (const CAddress& addr : pto->vAddrToSend)
{
- if (!pto->addrKnown.contains(addr.GetKey()))
+ if (!pto->m_addr_known->contains(addr.GetKey()))
{
- pto->addrKnown.insert(addr.GetKey());
+ pto->m_addr_known->insert(addr.GetKey());
vAddr.push_back(addr);
// receiver rejects addr messages larger than 1000
if (vAddr.size() >= 1000)
@@ -3795,13 +3798,13 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
LOCK(pto->m_tx_relay->cs_tx_inventory);
// Check whether periodic sends should happen
bool fSendTrickle = pto->HasPermission(PF_NOBAN);
- if (pto->m_tx_relay->nNextInvSend < nNow) {
+ if (pto->m_tx_relay->nNextInvSend < current_time) {
fSendTrickle = true;
if (pto->fInbound) {
- pto->m_tx_relay->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
+ pto->m_tx_relay->nNextInvSend = std::chrono::microseconds{connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL)};
} else {
// Use half the delay for outbound peers, as there is less privacy concern for them.
- pto->m_tx_relay->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1);
+ pto->m_tx_relay->nNextInvSend = PoissonNextSend(current_time, std::chrono::seconds{INVENTORY_BROADCAST_INTERVAL >> 1});
}
}
@@ -3916,7 +3919,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
// Detect whether we're stalling
- const auto current_time = GetTime<std::chrono::microseconds>();
+ current_time = GetTime<std::chrono::microseconds>();
// nNow is the current system time (GetTimeMicros is not mockable) and
// should be replaced by the mockable current_time eventually
nNow = GetTimeMicros();
diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp
index 57fa158ad2..a818f06d51 100644
--- a/src/node/coinstats.cpp
+++ b/src/node/coinstats.cpp
@@ -14,9 +14,6 @@
#include <map>
-#include <boost/thread.hpp>
-
-
static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
{
assert(!outputs.empty());
@@ -38,6 +35,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash,
//! Calculate statistics about the unspent transaction output set
bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
{
+ stats = CCoinsStats();
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
assert(pcursor);
@@ -51,7 +49,6 @@ bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
uint256 prevkey;
std::map<uint32_t, Coin> outputs;
while (pcursor->Valid()) {
- boost::this_thread::interruption_point();
COutPoint key;
Coin coin;
if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
@@ -61,6 +58,7 @@ bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
}
prevkey = key.hash;
outputs[key.n] = std::move(coin);
+ stats.coins_count++;
} else {
return error("%s: unable to read value", __func__);
}
diff --git a/src/node/coinstats.h b/src/node/coinstats.h
index 7c11aab8bd..a19af0fd1b 100644
--- a/src/node/coinstats.h
+++ b/src/node/coinstats.h
@@ -15,16 +15,17 @@ class CCoinsView;
struct CCoinsStats
{
- int nHeight;
- uint256 hashBlock;
- uint64_t nTransactions;
- uint64_t nTransactionOutputs;
- uint64_t nBogoSize;
- uint256 hashSerialized;
- uint64_t nDiskSize;
- CAmount nTotalAmount;
-
- CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nBogoSize(0), nDiskSize(0), nTotalAmount(0) {}
+ int nHeight{0};
+ uint256 hashBlock{};
+ uint64_t nTransactions{0};
+ uint64_t nTransactionOutputs{0};
+ uint64_t nBogoSize{0};
+ uint256 hashSerialized{};
+ uint64_t nDiskSize{0};
+ CAmount nTotalAmount{0};
+
+ //! The number of coins contained.
+ uint64_t coins_count{0};
};
//! Calculate statistics about the unspent transaction output set
diff --git a/src/node/context.h b/src/node/context.h
index 2b124af4db..dab5b5d048 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -10,6 +10,7 @@
class BanMan;
class CConnman;
+class CTxMemPool;
class PeerLogicValidation;
namespace interfaces {
class Chain;
@@ -22,13 +23,13 @@ class ChainClient;
//! This is used by init, rpc, and test code to pass object references around
//! without needing to declare the same variables and parameters repeatedly, or
//! to use globals. More variables could be added to this struct (particularly
-//! references to validation and mempool objects) to eliminate use of globals
+//! references to validation objects) to eliminate use of globals
//! and make code more modular and testable. The struct isn't intended to have
//! any member functions. It should just be a collection of references that can
//! be used without pulling in unwanted dependencies or functionality.
-struct NodeContext
-{
+struct NodeContext {
std::unique_ptr<CConnman> connman;
+ CTxMemPool* mempool{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<PeerLogicValidation> peer_logic;
std::unique_ptr<BanMan> banman;
std::unique_ptr<interfaces::Chain> chain;
diff --git a/src/node/utxo_snapshot.h b/src/node/utxo_snapshot.h
new file mode 100644
index 0000000000..702a0cbe53
--- /dev/null
+++ b/src/node/utxo_snapshot.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-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_NODE_UTXO_SNAPSHOT_H
+#define BITCOIN_NODE_UTXO_SNAPSHOT_H
+
+#include <uint256.h>
+#include <serialize.h>
+
+//! Metadata describing a serialized version of a UTXO set from which an
+//! assumeutxo CChainState can be constructed.
+class SnapshotMetadata
+{
+public:
+ //! The hash of the block that reflects the tip of the chain for the
+ //! UTXO set contained in this snapshot.
+ uint256 m_base_blockhash;
+
+ //! The number of coins in the UTXO set contained in this snapshot. Used
+ //! during snapshot load to estimate progress of UTXO set reconstruction.
+ uint64_t m_coins_count = 0;
+
+ //! Necessary to "fake" the base nChainTx so that we can estimate progress during
+ //! initial block download for the assumeutxo chainstate.
+ unsigned int m_nchaintx = 0;
+
+ SnapshotMetadata() { }
+ SnapshotMetadata(
+ const uint256& base_blockhash,
+ uint64_t coins_count,
+ unsigned int nchaintx) :
+ m_base_blockhash(base_blockhash),
+ m_coins_count(coins_count),
+ m_nchaintx(nchaintx) { }
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action)
+ {
+ READWRITE(m_base_blockhash);
+ READWRITE(m_coins_count);
+ READWRITE(m_nchaintx);
+ }
+
+};
+
+#endif // BITCOIN_NODE_UTXO_SNAPSHOT_H
diff --git a/src/noui.cpp b/src/noui.cpp
index 11c8f1e13d..a5b7a2d591 100644
--- a/src/noui.cpp
+++ b/src/noui.cpp
@@ -66,28 +66,31 @@ void noui_connect()
noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessage);
}
-bool noui_ThreadSafeMessageBoxSuppressed(const std::string& message, const std::string& caption, unsigned int style)
+bool noui_ThreadSafeMessageBoxRedirect(const std::string& message, const std::string& caption, unsigned int style)
{
+ LogPrintf("%s: %s\n", caption, message);
return false;
}
-bool noui_ThreadSafeQuestionSuppressed(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
+bool noui_ThreadSafeQuestionRedirect(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
{
+ LogPrintf("%s: %s\n", caption, message);
return false;
}
-void noui_InitMessageSuppressed(const std::string& message)
+void noui_InitMessageRedirect(const std::string& message)
{
+ LogPrintf("init message: %s\n", message);
}
-void noui_suppress()
+void noui_test_redirect()
{
noui_ThreadSafeMessageBoxConn.disconnect();
noui_ThreadSafeQuestionConn.disconnect();
noui_InitMessageConn.disconnect();
- noui_ThreadSafeMessageBoxConn = uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBoxSuppressed);
- noui_ThreadSafeQuestionConn = uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestionSuppressed);
- noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessageSuppressed);
+ noui_ThreadSafeMessageBoxConn = uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBoxRedirect);
+ noui_ThreadSafeQuestionConn = uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestionRedirect);
+ noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessageRedirect);
}
void noui_reconnect()
diff --git a/src/noui.h b/src/noui.h
index 854aeeacca..621e9c2798 100644
--- a/src/noui.h
+++ b/src/noui.h
@@ -17,10 +17,10 @@ void noui_InitMessage(const std::string& message);
/** Connect all bitcoind signal handlers */
void noui_connect();
-/** Suppress all bitcoind signal handlers. Used to suppress output during test runs that produce expected errors */
-void noui_suppress();
+/** Redirect all bitcoind signal handlers to LogPrintf. Used to check or suppress output during test runs that produce expected errors */
+void noui_test_redirect();
-/** Reconnects the regular Non-GUI handlers after having used noui_suppress */
+/** Reconnects the regular Non-GUI handlers after having used noui_test_redirect */
void noui_reconnect();
#endif // BITCOIN_NOUI_H
diff --git a/src/psbt.h b/src/psbt.h
index 6a5c468058..bcff66f7a1 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -171,7 +171,7 @@ struct PSBTInput
case PSBT_IN_PARTIAL_SIG:
{
// Make sure that the key is the size of pubkey + 1
- if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
+ if (key.size() != CPubKey::SIZE + 1 && key.size() != CPubKey::COMPRESSED_SIZE + 1) {
throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey");
}
// Read in the pubkey from key
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index d38df716bd..10953adc35 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -196,8 +196,8 @@ bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned cha
if (!secp256k1_ecdsa_recover(secp256k1_context_verify, &pubkey, &sig, hash.begin())) {
return false;
}
- unsigned char pub[PUBLIC_KEY_SIZE];
- size_t publen = PUBLIC_KEY_SIZE;
+ unsigned char pub[SIZE];
+ size_t publen = SIZE;
secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, fComp ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
Set(pub, pub + publen);
return true;
@@ -217,8 +217,8 @@ bool CPubKey::Decompress() {
if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) {
return false;
}
- unsigned char pub[PUBLIC_KEY_SIZE];
- size_t publen = PUBLIC_KEY_SIZE;
+ unsigned char pub[SIZE];
+ size_t publen = SIZE;
secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
Set(pub, pub + publen);
return true;
@@ -227,7 +227,7 @@ bool CPubKey::Decompress() {
bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const {
assert(IsValid());
assert((nChild >> 31) == 0);
- assert(size() == COMPRESSED_PUBLIC_KEY_SIZE);
+ assert(size() == COMPRESSED_SIZE);
unsigned char out[64];
BIP32Hash(cc, nChild, *begin(), begin()+1, out);
memcpy(ccChild.begin(), out+32, 32);
@@ -238,8 +238,8 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
if (!secp256k1_ec_pubkey_tweak_add(secp256k1_context_verify, &pubkey, out)) {
return false;
}
- unsigned char pub[COMPRESSED_PUBLIC_KEY_SIZE];
- size_t publen = COMPRESSED_PUBLIC_KEY_SIZE;
+ unsigned char pub[COMPRESSED_SIZE];
+ size_t publen = COMPRESSED_SIZE;
secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_COMPRESSED);
pubkeyChild.Set(pub, pub + publen);
return true;
@@ -251,8 +251,8 @@ void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
memcpy(code+9, chaincode.begin(), 32);
- assert(pubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE);
- memcpy(code+41, pubkey.begin(), CPubKey::COMPRESSED_PUBLIC_KEY_SIZE);
+ assert(pubkey.size() == CPubKey::COMPRESSED_SIZE);
+ memcpy(code+41, pubkey.begin(), CPubKey::COMPRESSED_SIZE);
}
void CExtPubKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
diff --git a/src/pubkey.h b/src/pubkey.h
index fd815a871b..76f743da66 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -33,17 +33,17 @@ public:
/**
* secp256k1:
*/
- static constexpr unsigned int PUBLIC_KEY_SIZE = 65;
- static constexpr unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33;
- static constexpr unsigned int SIGNATURE_SIZE = 72;
- static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65;
+ static constexpr unsigned int SIZE = 65;
+ static constexpr unsigned int COMPRESSED_SIZE = 33;
+ static constexpr unsigned int SIGNATURE_SIZE = 72;
+ static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65;
/**
* see www.keylength.com
* script supports up to 75 for single byte push
*/
static_assert(
- PUBLIC_KEY_SIZE >= COMPRESSED_PUBLIC_KEY_SIZE,
- "COMPRESSED_PUBLIC_KEY_SIZE is larger than PUBLIC_KEY_SIZE");
+ SIZE >= COMPRESSED_SIZE,
+ "COMPRESSED_SIZE is larger than SIZE");
private:
@@ -51,15 +51,15 @@ private:
* Just store the serialized data.
* Its length can very cheaply be computed from the first byte.
*/
- unsigned char vch[PUBLIC_KEY_SIZE];
+ unsigned char vch[SIZE];
//! Compute the length of a pubkey with a given first byte.
unsigned int static GetLen(unsigned char chHeader)
{
if (chHeader == 2 || chHeader == 3)
- return COMPRESSED_PUBLIC_KEY_SIZE;
+ return COMPRESSED_SIZE;
if (chHeader == 4 || chHeader == 6 || chHeader == 7)
- return PUBLIC_KEY_SIZE;
+ return SIZE;
return 0;
}
@@ -140,7 +140,7 @@ public:
void Unserialize(Stream& s)
{
unsigned int len = ::ReadCompactSize(s);
- if (len <= PUBLIC_KEY_SIZE) {
+ if (len <= SIZE) {
s.read((char*)vch, len);
} else {
// invalid pubkey, skip available data
@@ -179,7 +179,7 @@ public:
//! Check whether this is a compressed public key.
bool IsCompressed() const
{
- return size() == COMPRESSED_PUBLIC_KEY_SIZE;
+ return size() == COMPRESSED_SIZE;
}
/**
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 234d3865ab..676c15ea43 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -430,16 +430,19 @@ int GuiMain(int argc, char* argv[])
BitcoinApplication app(*node);
- // Register meta types used for QMetaObject::invokeMethod
- qRegisterMetaType< bool* >();
+ // Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
+ qRegisterMetaType<bool*>();
#ifdef ENABLE_WALLET
qRegisterMetaType<WalletModel*>();
#endif
- // Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
- // IMPORTANT if it is no longer a typedef use the normal variant above
- qRegisterMetaType< CAmount >("CAmount");
- qRegisterMetaType< std::function<void()> >("std::function<void()>");
+ // Register typedefs (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
+ // IMPORTANT: if CAmount is no longer a typedef use the normal variant above (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1)
+ qRegisterMetaType<CAmount>("CAmount");
+ qRegisterMetaType<size_t>("size_t");
+
+ qRegisterMetaType<std::function<void()>>("std::function<void()>");
qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
+
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
node->setupServerArgs();
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index f928f1ca2a..3302dde4ed 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -468,7 +468,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{
CPubKey pubkey;
PKHash *pkhash = boost::get<PKHash>(&address);
- if (pkhash && model->wallet().getPubKey(CKeyID(*pkhash), pubkey))
+ if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, CKeyID(*pkhash), pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
}
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 0214356eaa..64f52fcf45 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -63,7 +63,7 @@
<item row="4" column="2">
<widget class="QLineEdit" name="reqLabel">
<property name="toolTip">
- <string>An optional label to associate with the new receiving address.</string>
+ <string>An optional label to associate with the new receiving address (used by you to identify an invoice). It is also attached to the payment request.</string>
</property>
</widget>
</item>
@@ -93,7 +93,7 @@
<item row="6" column="2">
<widget class="QLineEdit" name="reqMessage">
<property name="toolTip">
- <string>An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</string>
+ <string>An optional message that is attached to the payment request and may be displayed to the sender.</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 386d559281..7190d59240 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -797,7 +797,7 @@
<item>
<widget class="QPushButton" name="buttonMinimizeFee">
<property name="toolTip">
- <string>collapse fee-settings</string>
+ <string>Hide transaction fee settings</string>
</property>
<property name="text">
<string>Hide</string>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index c4e0321f28..31e62ab63d 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -7,7 +7,7 @@
#include <qt/bitcoinaddressvalidator.h>
#include <qt/bitcoinunits.h>
#include <qt/qvalidatedlineedit.h>
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <base58.h>
#include <chainparams.h>
diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp
index 199804f84d..6a0143ac7e 100644
--- a/src/qt/openuridialog.cpp
+++ b/src/qt/openuridialog.cpp
@@ -6,7 +6,7 @@
#include <qt/forms/ui_openuridialog.h>
#include <qt/guiutil.h>
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <QUrl>
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index 8b2533508d..6d4ce4a7e4 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -36,13 +36,17 @@
#include <config/bitcoin-config.h>
#endif
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <QObject>
#include <QString>
class OptionsModel;
+namespace interfaces {
+class Node;
+} // namespace interfaces
+
QT_BEGIN_NAMESPACE
class QApplication;
class QByteArray;
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index e492502002..2674c9b953 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -8,6 +8,7 @@
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
+#include <qt/walletmodel.h>
#include <QClipboard>
#include <QPixmap>
diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h
index a6e1a2af16..1bb43ce6d1 100644
--- a/src/qt/receiverequestdialog.h
+++ b/src/qt/receiverequestdialog.h
@@ -5,10 +5,12 @@
#ifndef BITCOIN_QT_RECEIVEREQUESTDIALOG_H
#define BITCOIN_QT_RECEIVEREQUESTDIALOG_H
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <QDialog>
+class WalletModel;
+
namespace Ui {
class ReceiveRequestDialog;
}
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 1611ec823c..6d933f46d6 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -7,6 +7,7 @@
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
+#include <qt/walletmodel.h>
#include <clientversion.h>
#include <streams.h>
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index 130b709d46..30578b7a03 100644
--- a/src/qt/recentrequeststablemodel.h
+++ b/src/qt/recentrequeststablemodel.h
@@ -5,12 +5,14 @@
#ifndef BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H
#define BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <QAbstractTableModel>
#include <QStringList>
#include <QDateTime>
+class WalletModel;
+
class RecentRequestEntry
{
public:
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 8edcca684d..c3d00fa38e 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -21,11 +21,12 @@
#include <chainparams.h>
#include <interfaces/node.h>
#include <key_io.h>
-#include <wallet/coincontrol.h>
-#include <ui_interface.h>
-#include <txmempool.h>
#include <policy/fees.h>
+#include <txmempool.h>
+#include <ui_interface.h>
+#include <wallet/coincontrol.h>
#include <wallet/fees.h>
+#include <wallet/psbtwallet.h>
#include <QFontMetrics>
#include <QScrollBar>
@@ -186,6 +187,11 @@ void SendCoinsDialog::setModel(WalletModel *_model)
// set default rbf checkbox state
ui->optInRBF->setCheckState(Qt::Checked);
+ if (model->privateKeysDisabled()) {
+ ui->sendButton->setText(tr("Cr&eate Unsigned"));
+ ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
+ }
+
// set the smartfee-sliders default value (wallets default conf.target or last stored value)
QSettings settings;
if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
@@ -305,9 +311,19 @@ void SendCoinsDialog::on_sendButton_clicked()
formatted.append(recipientElement);
}
- QString questionString = tr("Are you sure you want to send?");
+ QString questionString;
+ if (model->privateKeysDisabled()) {
+ questionString.append(tr("Do you want to draft this transaction?"));
+ } else {
+ questionString.append(tr("Are you sure you want to send?"));
+ }
+
questionString.append("<br /><span style='font-size:10pt;'>");
- questionString.append(tr("Please, review your transaction."));
+ if (model->privateKeysDisabled()) {
+ questionString.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
+ } else {
+ questionString.append(tr("Please, review your transaction."));
+ }
questionString.append("</span>%1");
if(txFee > 0)
@@ -358,8 +374,9 @@ void SendCoinsDialog::on_sendButton_clicked()
} else {
questionString = questionString.arg("<br /><br />" + formatted.at(0));
}
-
- SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, this);
+ const QString confirmation = model->privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
+ const QString confirmButtonText = model->privateKeysDisabled() ? tr("Copy PSBT to clipboard") : tr("Send");
+ SendConfirmationDialog confirmationDialog(confirmation, questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
confirmationDialog.exec();
QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
@@ -369,17 +386,35 @@ void SendCoinsDialog::on_sendButton_clicked()
return;
}
- // now send the prepared transaction
- WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
- // process sendStatus and on error generate message shown to user
- processSendCoinsReturn(sendStatus);
+ bool send_failure = false;
+ if (model->privateKeysDisabled()) {
+ CMutableTransaction mtx = CMutableTransaction{*(currentTransaction.getWtx())};
+ PartiallySignedTransaction psbtx(mtx);
+ bool complete = false;
+ const TransactionError err = model->wallet().fillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
+ assert(!complete);
+ assert(err == TransactionError::OK);
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+ GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
+ Q_EMIT message(tr("PSBT copied"), "Copied to clipboard", CClientUIInterface::MSG_INFORMATION);
+ } else {
+ // now send the prepared transaction
+ WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
+ // process sendStatus and on error generate message shown to user
+ processSendCoinsReturn(sendStatus);
- if (sendStatus.status == WalletModel::OK)
- {
+ if (sendStatus.status == WalletModel::OK) {
+ Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash());
+ } else {
+ send_failure = true;
+ }
+ }
+ if (!send_failure) {
accept();
CoinControlDialog::coinControl()->UnSelectAll();
coinControlUpdateLabels();
- Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash());
}
fNewRecipientAllowed = true;
}
@@ -611,6 +646,9 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
coin_control = *CoinControlDialog::coinControl();
}
+ // Include watch-only for wallets without private key
+ coin_control.fAllowWatchOnly = model->privateKeysDisabled();
+
// Calculate available amount to send.
CAmount amount = model->wallet().getAvailableBalance(coin_control);
for (int i = 0; i < ui->entries->count(); ++i) {
@@ -663,6 +701,8 @@ void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
// Either custom fee will be used or if not selected, the confirmation target from dropdown box
ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
ctrl.m_signal_bip125_rbf = ui->optInRBF->isChecked();
+ // Include watch-only for wallets without private key
+ ctrl.fAllowWatchOnly = model->privateKeysDisabled();
}
void SendCoinsDialog::updateSmartFeeLabel()
@@ -870,8 +910,8 @@ void SendCoinsDialog::coinControlUpdateLabels()
}
}
-SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, QWidget* parent)
- : QMessageBox(parent), secDelay(_secDelay)
+SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, const QString& _confirmButtonText, QWidget* parent)
+ : QMessageBox(parent), secDelay(_secDelay), confirmButtonText(_confirmButtonText)
{
setIcon(QMessageBox::Question);
setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
@@ -908,11 +948,11 @@ void SendConfirmationDialog::updateYesButton()
if(secDelay > 0)
{
yesButton->setEnabled(false);
- yesButton->setText(tr("Send") + " (" + QString::number(secDelay) + ")");
+ yesButton->setText(confirmButtonText + " (" + QString::number(secDelay) + ")");
}
else
{
yesButton->setEnabled(true);
- yesButton->setText(tr("Send"));
+ yesButton->setText(confirmButtonText);
}
}
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index c6c1816877..ccd8494613 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -108,7 +108,7 @@ class SendConfirmationDialog : public QMessageBox
Q_OBJECT
public:
- SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, QWidget* parent = nullptr);
+ SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "Send", QWidget* parent = nullptr);
int exec();
private Q_SLOTS:
@@ -119,6 +119,7 @@ private:
QAbstractButton *yesButton;
QTimer countDownTimer;
int secDelay;
+ QString confirmButtonText;
};
#endif // BITCOIN_QT_SENDCOINSDIALOG_H
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index be417655b4..ad37e09114 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -14,6 +14,7 @@
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
+#include <qt/walletmodel.h>
#include <QApplication>
#include <QClipboard>
diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h
index 42e2217130..aa69d30f99 100644
--- a/src/qt/sendcoinsentry.h
+++ b/src/qt/sendcoinsentry.h
@@ -5,13 +5,17 @@
#ifndef BITCOIN_QT_SENDCOINSENTRY_H
#define BITCOIN_QT_SENDCOINSENTRY_H
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <QStackedWidget>
class WalletModel;
class PlatformStyle;
+namespace interfaces {
+class Node;
+} // namespace interfaces
+
namespace Ui {
class SendCoinsEntry;
}
diff --git a/src/qt/sendcoinsrecipient.h b/src/qt/sendcoinsrecipient.h
new file mode 100644
index 0000000000..12279fab64
--- /dev/null
+++ b/src/qt/sendcoinsrecipient.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2011-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_QT_SENDCOINSRECIPIENT_H
+#define BITCOIN_QT_SENDCOINSRECIPIENT_H
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <amount.h>
+#include <serialize.h>
+
+#include <string>
+
+#include <QString>
+
+class SendCoinsRecipient
+{
+public:
+ explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { }
+ explicit SendCoinsRecipient(const QString &addr, const QString &_label, const CAmount& _amount, const QString &_message):
+ address(addr), label(_label), amount(_amount), message(_message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
+
+ // If from an unauthenticated payment request, this is used for storing
+ // the addresses, e.g. address-A<br />address-B<br />address-C.
+ // Info: As we don't need to process addresses in here when using
+ // payment requests, we can abuse it for displaying an address list.
+ // Todo: This is a hack, should be replaced with a cleaner solution!
+ QString address;
+ QString label;
+ CAmount amount;
+ // If from a payment request, this is used for storing the memo
+ QString message;
+ // Keep the payment request around as a serialized string to ensure
+ // load/store is lossless.
+ std::string sPaymentRequest;
+ // Empty if no authentication or invalid signature/cert/etc.
+ QString authenticatedMerchant;
+
+ bool fSubtractFeeFromAmount; // memory only
+
+ static const int CURRENT_VERSION = 1;
+ int nVersion;
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ std::string sAddress = address.toStdString();
+ std::string sLabel = label.toStdString();
+ std::string sMessage = message.toStdString();
+ std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString();
+
+ READWRITE(this->nVersion);
+ READWRITE(sAddress);
+ READWRITE(sLabel);
+ READWRITE(amount);
+ READWRITE(sMessage);
+ READWRITE(sPaymentRequest);
+ READWRITE(sAuthenticatedMerchant);
+
+ if (ser_action.ForRead())
+ {
+ address = QString::fromStdString(sAddress);
+ label = QString::fromStdString(sLabel);
+ message = QString::fromStdString(sMessage);
+ authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant);
+ }
+ }
+};
+
+#endif // BITCOIN_QT_SENDCOINSRECIPIENT_H
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 1d0e1323bc..9d250bcb83 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -136,7 +136,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
}
CKey key;
- if (!model->wallet().getPrivKey(CKeyID(*pkhash), key))
+ if (!model->wallet().getPrivKey(GetScriptForDestination(destination), CKeyID(*pkhash), key))
{
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 8b32b70d1e..6e8d383847 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -1,6 +1,6 @@
#include <qt/test/addressbooktests.h>
#include <qt/test/util.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index e730c8f6d5..664826ecf2 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -11,7 +11,7 @@
#include <qt/networkstyle.h>
#include <qt/rpcconsole.h>
#include <shutdown.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <univalue.h>
#include <validation.h>
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 1772de4c1b..374fc9b59b 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -7,7 +7,7 @@
#include <interfaces/node.h>
#include <rpc/server.h>
#include <qt/rpcconsole.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <univalue.h>
#include <util/system.h>
@@ -32,7 +32,6 @@ void RPCNestedTests::rpcNestedTests()
// do some test setup
// could be moved to a more generic place when we add more tests on QT level
tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
- //mempool.setSanityCheck(1.0);
TestingSetup test;
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index e6870cf1be..243c10d7da 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -12,7 +12,7 @@
#include <qt/test/rpcnestedtests.h>
#include <qt/test/uritests.h>
#include <qt/test/compattests.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#ifdef ENABLE_WALLET
#include <qt/test/addressbooktests.h>
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 881653cdac..980de711db 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -13,7 +13,7 @@
#include <qt/transactionview.h>
#include <qt/walletmodel.h>
#include <key_io.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <validation.h>
#include <wallet/wallet.h>
#include <qt/overviewpage.h>
@@ -139,10 +139,12 @@ void TestGUI(interfaces::Node& node)
wallet->LoadWallet(firstRun);
{
auto spk_man = wallet->GetLegacyScriptPubKeyMan();
+ auto locked_chain = wallet->chain().lock();
LOCK(wallet->cs_wallet);
AssertLockHeld(spk_man->cs_wallet);
wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
+ wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());
}
{
auto locked_chain = wallet->chain().lock();
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 318b0756c7..9a93798aef 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -15,11 +15,12 @@
#include <consensus/consensus.h>
#include <interfaces/node.h>
+#include <interfaces/wallet.h>
#include <key_io.h>
-#include <validation.h>
+#include <policy/policy.h>
#include <script/script.h>
#include <util/system.h>
-#include <policy/policy.h>
+#include <validation.h>
#include <wallet/ismine.h>
#include <stdint.h>
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 095c98d26f..220e41b383 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -34,14 +34,6 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo
ui->setupUi(this);
QString version = QString{PACKAGE_NAME} + " " + tr("version") + " " + QString::fromStdString(FormatFullVersion());
- /* On x86 add a bit specifier to the version so that users can distinguish between
- * 32 and 64 bit builds. On other architectures, 32/64 bit may be more ambiguous.
- */
-#if defined(__x86_64__)
- version += " " + tr("(%1-bit)").arg(64);
-#elif defined(__i386__ )
- version += " " + tr("(%1-bit)").arg(32);
-#endif
if (about)
{
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 4c253f8ddd..7413a1f09e 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -2,17 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <qt/walletcontroller.h>
+
#include <qt/askpassphrasedialog.h>
#include <qt/createwalletdialog.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
-#include <qt/walletcontroller.h>
-
-#include <wallet/wallet.h>
+#include <qt/walletmodel.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <util/string.h>
+#include <wallet/wallet.h>
#include <algorithm>
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index e50dd5c7eb..956245775e 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_WALLETCONTROLLER_H
#define BITCOIN_QT_WALLETCONTROLLER_H
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <support/allocators/secure.h>
#include <sync.h>
@@ -23,10 +23,12 @@
class OptionsModel;
class PlatformStyle;
+class WalletModel;
namespace interfaces {
class Handler;
class Node;
+class Wallet;
} // namespace interfaces
class AskPassphraseDialog;
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index d7f0617315..4b2b475883 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -61,11 +61,6 @@ void WalletFrame::addWallet(WalletModel *walletModel)
walletStack->addWidget(walletView);
mapWalletViews[walletModel] = walletView;
- // Ensure a walletView is able to show the main window
- connect(walletView, &WalletView::showNormalIfMinimized, [this]{
- gui->showNormalIfMinimized();
- });
-
connect(walletView, &WalletView::outOfSyncWarningClicked, this, &WalletFrame::outOfSyncWarningClicked);
}
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 33801d3907..fb92e29f21 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -183,7 +183,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
std::string strFailReason;
auto& newTx = transaction.getWtx();
- newTx = m_wallet->createTransaction(vecSend, coinControl, true /* sign */, nChangePosRet, nFeeRequired, strFailReason);
+ newTx = m_wallet->createTransaction(vecSend, coinControl, !privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, strFailReason);
transaction.setTransactionFee(nFeeRequired);
if (fSubtractFeeFromAmount && newTx)
transaction.reassignAmounts(nChangePosRet);
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index a873519a34..8087356f5e 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -9,9 +9,7 @@
#include <config/bitcoin-config.h>
#endif
-#include <amount.h>
#include <key.h>
-#include <serialize.h>
#include <script/standard.h>
#include <qt/walletmodeltransaction.h>
@@ -29,6 +27,7 @@ class AddressTableModel;
class OptionsModel;
class PlatformStyle;
class RecentRequestsTableModel;
+class SendCoinsRecipient;
class TransactionTableModel;
class WalletModelTransaction;
@@ -47,61 +46,6 @@ QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
-class SendCoinsRecipient
-{
-public:
- explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { }
- explicit SendCoinsRecipient(const QString &addr, const QString &_label, const CAmount& _amount, const QString &_message):
- address(addr), label(_label), amount(_amount), message(_message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
-
- // If from an unauthenticated payment request, this is used for storing
- // the addresses, e.g. address-A<br />address-B<br />address-C.
- // Info: As we don't need to process addresses in here when using
- // payment requests, we can abuse it for displaying an address list.
- // Todo: This is a hack, should be replaced with a cleaner solution!
- QString address;
- QString label;
- CAmount amount;
- // If from a payment request, this is used for storing the memo
- QString message;
- // Keep the payment request around as a serialized string to ensure
- // load/store is lossless.
- std::string sPaymentRequest;
- // Empty if no authentication or invalid signature/cert/etc.
- QString authenticatedMerchant;
-
- bool fSubtractFeeFromAmount; // memory only
-
- static const int CURRENT_VERSION = 1;
- int nVersion;
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- std::string sAddress = address.toStdString();
- std::string sLabel = label.toStdString();
- std::string sMessage = message.toStdString();
- std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString();
-
- READWRITE(this->nVersion);
- READWRITE(sAddress);
- READWRITE(sLabel);
- READWRITE(amount);
- READWRITE(sMessage);
- READWRITE(sPaymentRequest);
- READWRITE(sAuthenticatedMerchant);
-
- if (ser_action.ForRead())
- {
- address = QString::fromStdString(sAddress);
- label = QString::fromStdString(sLabel);
- message = QString::fromStdString(sMessage);
- authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant);
- }
- }
-};
-
/** Interface to Bitcoin wallet from Qt view code. */
class WalletModel : public QObject
{
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index 242ba13897..9e5d285e8c 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -5,7 +5,8 @@
#ifndef BITCOIN_QT_WALLETMODELTRANSACTION_H
#define BITCOIN_QT_WALLETMODELTRANSACTION_H
-#include <qt/walletmodel.h>
+#include <primitives/transaction.h>
+#include <qt/sendcoinsrecipient.h>
#include <amount.h>
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index e29c4c52f5..8d5a301cdb 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -115,8 +115,6 @@ public Q_SLOTS:
void requestedSyncWarningInfo();
Q_SIGNALS:
- /** Signal that we want to show the main window */
- void showNormalIfMinimized();
/** Fired when a message should be reported to the user */
void message(const QString &title, const QString &message, unsigned int style);
/** Encryption status of wallet changed */
diff --git a/src/random.cpp b/src/random.cpp
index 48d20d7d72..47d76d8700 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -5,19 +5,22 @@
#include <random.h>
+#include <compat/cpuid.h>
#include <crypto/sha512.h>
#include <support/cleanse.h>
#ifdef WIN32
#include <compat.h> // for Windows API
#include <wincrypt.h>
#endif
-#include <logging.h> // for LogPrint()
-#include <sync.h> // for WAIT_LOCK
+#include <logging.h> // for LogPrintf()
+#include <sync.h> // for Mutex
#include <util/time.h> // for GetTime()
#include <stdlib.h>
#include <thread>
+#include <randomenv.h>
+
#include <support/allocators/secure.h>
#ifndef WIN32
@@ -40,15 +43,6 @@
#include <sys/sysctl.h>
#endif
-
-#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
-#include <cpuid.h>
-#endif
-
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#include <openssl/conf.h>
-
[[noreturn]] static void RandFailure()
{
LogPrintf("Failed to read randomness, aborting\n");
@@ -75,7 +69,7 @@ static inline int64_t GetPerformanceCounter() noexcept
#endif
}
-#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
+#ifdef HAVE_GETCPUID
static bool g_rdrand_supported = false;
static bool g_rdseed_supported = false;
static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000;
@@ -86,15 +80,6 @@ static_assert(CPUID_F1_ECX_RDRAND == bit_RDRND, "Unexpected value for bit_RDRND"
#ifdef bit_RDSEED
static_assert(CPUID_F7_EBX_RDSEED == bit_RDSEED, "Unexpected value for bit_RDSEED");
#endif
-static void inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
-{
- // We can't use __get_cpuid as it doesn't support subleafs.
-#ifdef __GNUC__
- __cpuid_count(leaf, subleaf, a, b, c, d);
-#else
- __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
-#endif
-}
static void InitHardwareRand()
{
@@ -263,44 +248,6 @@ static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA51
memory_cleanse(buffer, sizeof(buffer));
}
-static void RandAddSeedPerfmon(CSHA512& hasher)
-{
-#ifdef WIN32
- // Don't need this on Linux, OpenSSL automatically uses /dev/urandom
- // Seed with the entire set of perfmon data
-
- // This can take up to 2 seconds, so only do it every 10 minutes
- static int64_t nLastPerfmon;
- if (GetTime() < nLastPerfmon + 10 * 60)
- return;
- nLastPerfmon = GetTime();
-
- std::vector<unsigned char> vData(250000, 0);
- long ret = 0;
- unsigned long nSize = 0;
- const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
- while (true) {
- nSize = vData.size();
- ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize);
- if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
- break;
- vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
- }
- RegCloseKey(HKEY_PERFORMANCE_DATA);
- if (ret == ERROR_SUCCESS) {
- hasher.Write(vData.data(), nSize);
- memory_cleanse(vData.data(), nSize);
- } else {
- // Performance data is only a best-effort attempt at improving the
- // situation when the OS randomness (and other sources) aren't
- // adequate. As a result, failure to read it is isn't considered critical,
- // so we don't call RandFailure().
- // TODO: Add logging when the logger is made functional before global
- // constructors have been invoked.
- }
-#endif
-}
-
#ifndef WIN32
/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most
* compatible way to get cryptographic randomness on UNIX-ish platforms.
@@ -396,8 +343,6 @@ void GetOSRand(unsigned char *ent32)
#endif
}
-void LockingCallbackOpenSSL(int mode, int i, const char* file, int line);
-
namespace {
class RNGState {
@@ -413,31 +358,15 @@ class RNGState {
unsigned char m_state[32] GUARDED_BY(m_mutex) = {0};
uint64_t m_counter GUARDED_BY(m_mutex) = 0;
bool m_strongly_seeded GUARDED_BY(m_mutex) = false;
- std::unique_ptr<Mutex[]> m_mutex_openssl;
public:
RNGState() noexcept
{
InitHardwareRand();
-
- // Init OpenSSL library multithreading support
- m_mutex_openssl.reset(new Mutex[CRYPTO_num_locks()]);
- CRYPTO_set_locking_callback(LockingCallbackOpenSSL);
-
- // OpenSSL can optionally load a config file which lists optional loadable modules and engines.
- // We don't use them so we don't require the config. However some of our libs may call functions
- // which attempt to load the config file, possibly resulting in an exit() or crash if it is missing
- // or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be
- // that the config appears to have been loaded and there are no modules/engines available.
- OPENSSL_no_config();
}
~RNGState()
{
- // Securely erase the memory used by the OpenSSL PRNG
- RAND_cleanup();
- // Shutdown OpenSSL library multithreading support
- CRYPTO_set_locking_callback(nullptr);
}
/** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher.
@@ -473,8 +402,6 @@ public:
memory_cleanse(buf, 64);
return ret;
}
-
- Mutex& GetOpenSSLMutex(int i) { return m_mutex_openssl[i]; }
};
RNGState& GetRNGState() noexcept
@@ -486,17 +413,6 @@ RNGState& GetRNGState() noexcept
}
}
-void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
-{
- RNGState& rng = GetRNGState();
-
- if (mode & CRYPTO_LOCK) {
- rng.GetOpenSSLMutex(i).lock();
- } else {
- rng.GetOpenSSLMutex(i).unlock();
- }
-}
-
/* A note on the use of noexcept in the seeding functions below:
*
* None of the RNG code should ever throw any exception, with the sole exception
@@ -544,10 +460,6 @@ static void SeedSlow(CSHA512& hasher) noexcept
GetOSRand(buffer);
hasher.Write(buffer, sizeof(buffer));
- // OpenSSL RNG (for now)
- RAND_bytes(buffer, sizeof(buffer));
- hasher.Write(buffer, sizeof(buffer));
-
// High-precision timestamp.
//
// Note that we also commit to a timestamp in the Fast seeder, so we indirectly commit to a
@@ -556,22 +468,16 @@ static void SeedSlow(CSHA512& hasher) noexcept
}
/** Extract entropy from rng, strengthen it, and feed it into hasher. */
-static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept
+static void SeedStrengthen(CSHA512& hasher, RNGState& rng, int microseconds) noexcept
{
- static std::atomic<int64_t> last_strengthen{0};
- int64_t last_time = last_strengthen.load();
- int64_t current_time = GetTimeMicros();
- if (current_time > last_time + 60000000) { // Only run once a minute
- // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
- unsigned char strengthen_seed[32];
- rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
- // Strengthen it for 10ms (100ms on first run), and feed it into hasher.
- Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher);
- last_strengthen = current_time;
- }
+ // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
+ unsigned char strengthen_seed[32];
+ rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
+ // Strengthen the seed, and feed it into hasher.
+ Strengthen(strengthen_seed, microseconds, hasher);
}
-static void SeedSleep(CSHA512& hasher, RNGState& rng)
+static void SeedPeriodic(CSHA512& hasher, RNGState& rng)
{
// Everything that the 'fast' seeder includes
SeedFast(hasher);
@@ -579,17 +485,13 @@ static void SeedSleep(CSHA512& hasher, RNGState& rng)
// High-precision timestamp
SeedTimestamp(hasher);
- // Sleep for 1ms
- MilliSleep(1);
-
- // High-precision timestamp after sleeping (as we commit to both the time before and after, this measures the delay)
- SeedTimestamp(hasher);
-
- // Windows performance monitor data (once every 10 minutes)
- RandAddSeedPerfmon(hasher);
+ // Dynamic environment data (performance monitoring, ...)
+ auto old_size = hasher.Size();
+ RandAddDynamicEnv(hasher);
+ LogPrintf("Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size);
- // Strengthen every minute
- SeedStrengthen(hasher, rng);
+ // Strengthen for 10 ms
+ SeedStrengthen(hasher, rng, 10000);
}
static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
@@ -600,17 +502,22 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
// Everything that the 'slow' seeder includes.
SeedSlow(hasher);
- // Windows performance monitor data.
- RandAddSeedPerfmon(hasher);
+ // Dynamic environment data (performance monitoring, ...)
+ auto old_size = hasher.Size();
+ RandAddDynamicEnv(hasher);
- // Strengthen
- SeedStrengthen(hasher, rng);
+ // Static environment data
+ RandAddStaticEnv(hasher);
+ LogPrintf("Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size);
+
+ // Strengthen for 100 ms
+ SeedStrengthen(hasher, rng, 100000);
}
enum class RNGLevel {
FAST, //!< Automatically called by GetRandBytes
SLOW, //!< Automatically called by GetStrongRandBytes
- SLEEP, //!< Called by RandAddSeedSleep()
+ PERIODIC, //!< Called by RandAddPeriodic()
};
static void ProcRand(unsigned char* out, int num, RNGLevel level)
@@ -628,8 +535,8 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
case RNGLevel::SLOW:
SeedSlow(hasher);
break;
- case RNGLevel::SLEEP:
- SeedSleep(hasher, rng);
+ case RNGLevel::PERIODIC:
+ SeedPeriodic(hasher, rng);
break;
}
@@ -640,19 +547,11 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
SeedStartup(startup_hasher, rng);
rng.MixExtract(out, num, std::move(startup_hasher), true);
}
-
- // For anything but the 'fast' level, feed the resulting RNG output (after an additional hashing step) back into OpenSSL.
- if (level != RNGLevel::FAST) {
- unsigned char buf[64];
- CSHA512().Write(out, num).Finalize(buf);
- RAND_add(buf, sizeof(buf), num);
- memory_cleanse(buf, 64);
- }
}
void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::FAST); }
void GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); }
-void RandAddSeedSleep() { ProcRand(nullptr, 0, RNGLevel::SLEEP); }
+void RandAddPeriodic() { ProcRand(nullptr, 0, RNGLevel::PERIODIC); }
bool g_mock_deterministic_tests{false};
@@ -716,7 +615,7 @@ bool Random_SanityCheck()
uint64_t start = GetPerformanceCounter();
/* This does not measure the quality of randomness, but it does test that
- * OSRandom() overwrites all 32 bytes of the output given a maximum
+ * GetOSRand() overwrites all 32 bytes of the output given a maximum
* number of tries.
*/
static const ssize_t MAX_TRIES = 1024;
diff --git a/src/random.h b/src/random.h
index 9d1f751773..2d8ab085e3 100644
--- a/src/random.h
+++ b/src/random.h
@@ -35,7 +35,6 @@
* that fast seeding includes, but additionally:
* - OS entropy (/dev/urandom, getrandom(), ...). The application will terminate if
* this entropy source fails.
- * - Bytes from OpenSSL's RNG (which itself may be seeded from various sources)
* - Another high-precision timestamp (indirectly committing to a benchmark of all the
* previous sources).
* These entropy sources are slower, but designed to make sure the RNG state contains
@@ -52,7 +51,6 @@
* sources used in the 'slow' seeder are included, but also:
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
* - (On Windows) Performance monitoring data from the OS.
- * - (On Windows) Through OpenSSL, the screen contents.
* - Strengthen the entropy for 100 ms using repeated SHA512.
*
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
@@ -85,11 +83,11 @@ uint256 GetRandHash() noexcept;
void GetStrongRandBytes(unsigned char* buf, int num) noexcept;
/**
- * Sleep for 1ms, gather entropy from various sources, and feed them to the PRNG state.
+ * Gather entropy from various expensive sources, and feed them to the PRNG state.
*
* Thread-safe.
*/
-void RandAddSeedSleep();
+void RandAddPeriodic();
/**
* Fast randomness source. This is seeded once with secure random data, but
diff --git a/src/randomenv.cpp b/src/randomenv.cpp
new file mode 100644
index 0000000000..ec42ddabc3
--- /dev/null
+++ b/src/randomenv.cpp
@@ -0,0 +1,507 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-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.
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <randomenv.h>
+
+#include <clientversion.h>
+#include <compat/cpuid.h>
+#include <crypto/sha512.h>
+#include <support/cleanse.h>
+#include <util/time.h> // for GetTime()
+#ifdef WIN32
+#include <compat.h> // for Windows API
+#endif
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <climits>
+#include <thread>
+#include <vector>
+
+#include <stdint.h>
+#include <string.h>
+#ifndef WIN32
+#include <sys/types.h> // must go before a number of other headers
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#endif
+#ifdef __MACH__
+#include <mach/clock.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif
+#if HAVE_DECL_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+#if HAVE_SYSCTL
+#include <sys/sysctl.h>
+#if HAVE_VM_VM_PARAM_H
+#include <vm/vm_param.h>
+#endif
+#if HAVE_SYS_RESOURCES_H
+#include <sys/resources.h>
+#endif
+#if HAVE_SYS_VMMETER_H
+#include <sys/vmmeter.h>
+#endif
+#endif
+#ifdef __linux__
+#include <sys/auxv.h>
+#endif
+
+//! Necessary on some platforms
+extern char** environ;
+
+namespace {
+
+void RandAddSeedPerfmon(CSHA512& hasher)
+{
+#ifdef WIN32
+ // Seed with the entire set of perfmon data
+
+ // This can take up to 2 seconds, so only do it every 10 minutes
+ static std::atomic<std::chrono::seconds> last_perfmon{std::chrono::seconds{0}};
+ auto last_time = last_perfmon.load();
+ auto current_time = GetTime<std::chrono::seconds>();
+ if (current_time < last_time + std::chrono::minutes{10}) return;
+ last_perfmon = current_time;
+
+ std::vector<unsigned char> vData(250000, 0);
+ long ret = 0;
+ unsigned long nSize = 0;
+ const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
+ while (true) {
+ nSize = vData.size();
+ ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize);
+ if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
+ break;
+ vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
+ }
+ RegCloseKey(HKEY_PERFORMANCE_DATA);
+ if (ret == ERROR_SUCCESS) {
+ hasher.Write(vData.data(), nSize);
+ memory_cleanse(vData.data(), nSize);
+ } else {
+ // Performance data is only a best-effort attempt at improving the
+ // situation when the OS randomness (and other sources) aren't
+ // adequate. As a result, failure to read it is isn't considered critical,
+ // so we don't call RandFailure().
+ // TODO: Add logging when the logger is made functional before global
+ // constructors have been invoked.
+ }
+#endif
+}
+
+/** Helper to easily feed data into a CSHA512.
+ *
+ * Note that this does not serialize the passed object (like stream.h's << operators do).
+ * Its raw memory representation is used directly.
+ */
+template<typename T>
+CSHA512& operator<<(CSHA512& hasher, const T& data) {
+ static_assert(!std::is_same<typename std::decay<T>::type, char*>::value, "Calling operator<<(CSHA512, char*) is probably not what you want");
+ static_assert(!std::is_same<typename std::decay<T>::type, unsigned char*>::value, "Calling operator<<(CSHA512, unsigned char*) is probably not what you want");
+ static_assert(!std::is_same<typename std::decay<T>::type, const char*>::value, "Calling operator<<(CSHA512, const char*) is probably not what you want");
+ static_assert(!std::is_same<typename std::decay<T>::type, const unsigned char*>::value, "Calling operator<<(CSHA512, const unsigned char*) is probably not what you want");
+ hasher.Write((const unsigned char*)&data, sizeof(data));
+ return hasher;
+}
+
+#ifndef WIN32
+void AddSockaddr(CSHA512& hasher, const struct sockaddr *addr)
+{
+ if (addr == nullptr) return;
+ switch (addr->sa_family) {
+ case AF_INET:
+ hasher.Write((const unsigned char*)addr, sizeof(sockaddr_in));
+ break;
+ case AF_INET6:
+ hasher.Write((const unsigned char*)addr, sizeof(sockaddr_in6));
+ break;
+ default:
+ hasher.Write((const unsigned char*)&addr->sa_family, sizeof(addr->sa_family));
+ }
+}
+
+void AddFile(CSHA512& hasher, const char *path)
+{
+ struct stat sb = {};
+ int f = open(path, O_RDONLY);
+ size_t total = 0;
+ if (f != -1) {
+ unsigned char fbuf[4096];
+ int n;
+ hasher.Write((const unsigned char*)&f, sizeof(f));
+ if (fstat(f, &sb) == 0) hasher << sb;
+ do {
+ n = read(f, fbuf, sizeof(fbuf));
+ if (n > 0) hasher.Write(fbuf, n);
+ total += n;
+ /* not bothering with EINTR handling. */
+ } while (n == sizeof(fbuf) && total < 1048576); // Read only the first 1 Mbyte
+ close(f);
+ }
+}
+
+void AddPath(CSHA512& hasher, const char *path)
+{
+ struct stat sb = {};
+ if (stat(path, &sb) == 0) {
+ hasher.Write((const unsigned char*)path, strlen(path) + 1);
+ hasher << sb;
+ }
+}
+#endif
+
+#if HAVE_SYSCTL
+template<int... S>
+void AddSysctl(CSHA512& hasher)
+{
+ int CTL[sizeof...(S)] = {S...};
+ unsigned char buffer[65536];
+ size_t siz = 65536;
+ int ret = sysctl(CTL, sizeof...(S), buffer, &siz, nullptr, 0);
+ if (ret == 0 || (ret == -1 && errno == ENOMEM)) {
+ hasher << sizeof(CTL);
+ hasher.Write((const unsigned char*)CTL, sizeof(CTL));
+ if (siz > sizeof(buffer)) siz = sizeof(buffer);
+ hasher << siz;
+ hasher.Write(buffer, siz);
+ }
+}
+#endif
+
+#ifdef HAVE_GETCPUID
+void inline AddCPUID(CSHA512& hasher, uint32_t leaf, uint32_t subleaf, uint32_t& ax, uint32_t& bx, uint32_t& cx, uint32_t& dx)
+{
+ GetCPUID(leaf, subleaf, ax, bx, cx, dx);
+ hasher << leaf << subleaf << ax << bx << cx << dx;
+}
+
+void AddAllCPUID(CSHA512& hasher)
+{
+ uint32_t ax, bx, cx, dx;
+ // Iterate over all standard leaves
+ AddCPUID(hasher, 0, 0, ax, bx, cx, dx); // Returns max leaf in ax
+ uint32_t max = ax;
+ for (uint32_t leaf = 1; leaf <= max; ++leaf) {
+ for (uint32_t subleaf = 0;; ++subleaf) {
+ AddCPUID(hasher, leaf, subleaf, ax, bx, cx, dx);
+ // Iterate over subleaves for leaf 4, 11, 13
+ if (leaf != 4 && leaf != 11 && leaf != 13) break;
+ if ((leaf == 4 || leaf == 13) && ax == 0) break;
+ if (leaf == 11 && (cx & 0xFF00) == 0) break;
+ }
+ }
+ // Iterate over all extended leaves
+ AddCPUID(hasher, 0x80000000, 0, ax, bx, cx, dx); // Returns max extended leaf in ax
+ uint32_t ext_max = ax;
+ for (uint32_t leaf = 0x80000001; leaf <= ext_max; ++leaf) {
+ AddCPUID(hasher, leaf, 0, ax, bx, cx, dx);
+ }
+}
+#endif
+} // namespace
+
+void RandAddDynamicEnv(CSHA512& hasher)
+{
+ RandAddSeedPerfmon(hasher);
+
+ // Various clocks
+#ifdef WIN32
+ FILETIME ftime;
+ GetSystemTimeAsFileTime(&ftime);
+ hasher << ftime;
+#else
+# ifndef __MACH__
+ // On non-MacOS systems, use various clock_gettime() calls.
+ struct timespec ts = {};
+# ifdef CLOCK_MONOTONIC
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ hasher << ts;
+# endif
+# ifdef CLOCK_REALTIME
+ clock_gettime(CLOCK_REALTIME, &ts);
+ hasher << ts;
+# endif
+# ifdef CLOCK_BOOTTIME
+ clock_gettime(CLOCK_BOOTTIME, &ts);
+ hasher << ts;
+# endif
+# else
+ // On MacOS use mach_absolute_time (number of CPU ticks since boot) as a replacement for CLOCK_MONOTONIC,
+ // and clock_get_time for CALENDAR_CLOCK as a replacement for CLOCK_REALTIME.
+ hasher << mach_absolute_time();
+ // From https://gist.github.com/jbenet/1087739
+ clock_serv_t cclock;
+ mach_timespec_t mts = {};
+ if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) == KERN_SUCCESS && clock_get_time(cclock, &mts) == KERN_SUCCESS) {
+ hasher << mts;
+ mach_port_deallocate(mach_task_self(), cclock);
+ }
+# endif
+ // gettimeofday is available on all UNIX systems, but only has microsecond precision.
+ struct timeval tv = {};
+ gettimeofday(&tv, nullptr);
+ hasher << tv;
+#endif
+ // Probably redundant, but also use all the clocks C++11 provides:
+ hasher << std::chrono::system_clock::now().time_since_epoch().count();
+ hasher << std::chrono::steady_clock::now().time_since_epoch().count();
+ hasher << std::chrono::high_resolution_clock::now().time_since_epoch().count();
+
+#ifndef WIN32
+ // Current resource usage.
+ struct rusage usage = {};
+ if (getrusage(RUSAGE_SELF, &usage) == 0) hasher << usage;
+#endif
+
+#ifdef __linux__
+ AddFile(hasher, "/proc/diskstats");
+ AddFile(hasher, "/proc/vmstat");
+ AddFile(hasher, "/proc/schedstat");
+ AddFile(hasher, "/proc/zoneinfo");
+ AddFile(hasher, "/proc/meminfo");
+ AddFile(hasher, "/proc/softirqs");
+ AddFile(hasher, "/proc/stat");
+ AddFile(hasher, "/proc/self/schedstat");
+ AddFile(hasher, "/proc/self/status");
+#endif
+
+#if HAVE_SYSCTL
+# ifdef CTL_KERN
+# if defined(KERN_PROC) && defined(KERN_PROC_ALL)
+ AddSysctl<CTL_KERN, KERN_PROC, KERN_PROC_ALL>(hasher);
+# endif
+# endif
+# ifdef CTL_HW
+# ifdef HW_DISKSTATS
+ AddSysctl<CTL_HW, HW_DISKSTATS>(hasher);
+# endif
+# endif
+# ifdef CTL_VM
+# ifdef VM_LOADAVG
+ AddSysctl<CTL_VM, VM_LOADAVG>(hasher);
+# endif
+# ifdef VM_TOTAL
+ AddSysctl<CTL_VM, VM_TOTAL>(hasher);
+# endif
+# ifdef VM_METER
+ AddSysctl<CTL_VM, VM_METER>(hasher);
+# endif
+# endif
+#endif
+
+ // Stack and heap location
+ void* addr = malloc(4097);
+ hasher << &addr << addr;
+ free(addr);
+}
+
+void RandAddStaticEnv(CSHA512& hasher)
+{
+ // Some compile-time static properties
+ hasher << (CHAR_MIN < 0) << sizeof(void*) << sizeof(long) << sizeof(int);
+#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
+ hasher << __GNUC__ << __GNUC_MINOR__ << __GNUC_PATCHLEVEL__;
+#endif
+#ifdef _MSC_VER
+ hasher << _MSC_VER;
+#endif
+ hasher << __cplusplus;
+#ifdef _XOPEN_VERSION
+ hasher << _XOPEN_VERSION;
+#endif
+#ifdef __VERSION__
+ const char* COMPILER_VERSION = __VERSION__;
+ hasher.Write((const unsigned char*)COMPILER_VERSION, strlen(COMPILER_VERSION) + 1);
+#endif
+
+ // Bitcoin client version
+ hasher << CLIENT_VERSION;
+
+#ifdef __linux__
+ // Information available through getauxval()
+# ifdef AT_HWCAP
+ hasher << getauxval(AT_HWCAP);
+# endif
+# ifdef AT_HWCAP2
+ hasher << getauxval(AT_HWCAP2);
+# endif
+# ifdef AT_RANDOM
+ const unsigned char* random_aux = (const unsigned char*)getauxval(AT_RANDOM);
+ if (random_aux) hasher.Write(random_aux, 16);
+# endif
+# ifdef AT_PLATFORM
+ const char* platform_str = (const char*)getauxval(AT_PLATFORM);
+ if (platform_str) hasher.Write((const unsigned char*)platform_str, strlen(platform_str) + 1);
+# endif
+# ifdef AT_EXECFN
+ const char* exec_str = (const char*)getauxval(AT_EXECFN);
+ if (exec_str) hasher.Write((const unsigned char*)exec_str, strlen(exec_str) + 1);
+# endif
+#endif // __linux__
+
+#ifdef HAVE_GETCPUID
+ AddAllCPUID(hasher);
+#endif
+
+ // Memory locations
+ hasher << &hasher << &RandAddStaticEnv << &malloc << &errno << &environ;
+
+ // Hostname
+ char hname[256];
+ if (gethostname(hname, 256) == 0) {
+ hasher.Write((const unsigned char*)hname, strnlen(hname, 256));
+ }
+
+#if HAVE_DECL_GETIFADDRS
+ // Network interfaces
+ struct ifaddrs *ifad = NULL;
+ getifaddrs(&ifad);
+ struct ifaddrs *ifit = ifad;
+ while (ifit != NULL) {
+ hasher.Write((const unsigned char*)&ifit, sizeof(ifit));
+ hasher.Write((const unsigned char*)ifit->ifa_name, strlen(ifit->ifa_name) + 1);
+ hasher.Write((const unsigned char*)&ifit->ifa_flags, sizeof(ifit->ifa_flags));
+ AddSockaddr(hasher, ifit->ifa_addr);
+ AddSockaddr(hasher, ifit->ifa_netmask);
+ AddSockaddr(hasher, ifit->ifa_dstaddr);
+ ifit = ifit->ifa_next;
+ }
+ freeifaddrs(ifad);
+#endif
+
+#ifndef WIN32
+ // UNIX kernel information
+ struct utsname name;
+ if (uname(&name) != -1) {
+ hasher.Write((const unsigned char*)&name.sysname, strlen(name.sysname) + 1);
+ hasher.Write((const unsigned char*)&name.nodename, strlen(name.nodename) + 1);
+ hasher.Write((const unsigned char*)&name.release, strlen(name.release) + 1);
+ hasher.Write((const unsigned char*)&name.version, strlen(name.version) + 1);
+ hasher.Write((const unsigned char*)&name.machine, strlen(name.machine) + 1);
+ }
+
+ /* Path and filesystem provided data */
+ AddPath(hasher, "/");
+ AddPath(hasher, ".");
+ AddPath(hasher, "/tmp");
+ AddPath(hasher, "/home");
+ AddPath(hasher, "/proc");
+#ifdef __linux__
+ AddFile(hasher, "/proc/cmdline");
+ AddFile(hasher, "/proc/cpuinfo");
+ AddFile(hasher, "/proc/version");
+#endif
+ AddFile(hasher, "/etc/passwd");
+ AddFile(hasher, "/etc/group");
+ AddFile(hasher, "/etc/hosts");
+ AddFile(hasher, "/etc/resolv.conf");
+ AddFile(hasher, "/etc/timezone");
+ AddFile(hasher, "/etc/localtime");
+#endif
+
+ // For MacOS/BSDs, gather data through sysctl instead of /proc. Not all of these
+ // will exist on every system.
+#if HAVE_SYSCTL
+# ifdef CTL_HW
+# ifdef HW_MACHINE
+ AddSysctl<CTL_HW, HW_MACHINE>(hasher);
+# endif
+# ifdef HW_MODEL
+ AddSysctl<CTL_HW, HW_MODEL>(hasher);
+# endif
+# ifdef HW_NCPU
+ AddSysctl<CTL_HW, HW_NCPU>(hasher);
+# endif
+# ifdef HW_PHYSMEM
+ AddSysctl<CTL_HW, HW_PHYSMEM>(hasher);
+# endif
+# ifdef HW_USERMEM
+ AddSysctl<CTL_HW, HW_USERMEM>(hasher);
+# endif
+# ifdef HW_MACHINE_ARCH
+ AddSysctl<CTL_HW, HW_MACHINE_ARCH>(hasher);
+# endif
+# ifdef HW_REALMEM
+ AddSysctl<CTL_HW, HW_REALMEM>(hasher);
+# endif
+# ifdef HW_CPU_FREQ
+ AddSysctl<CTL_HW, HW_CPU_FREQ>(hasher);
+# endif
+# ifdef HW_BUS_FREQ
+ AddSysctl<CTL_HW, HW_BUS_FREQ>(hasher);
+# endif
+# ifdef HW_CACHELINE
+ AddSysctl<CTL_HW, HW_CACHELINE>(hasher);
+# endif
+# endif
+# ifdef CTL_KERN
+# ifdef KERN_BOOTFILE
+ AddSysctl<CTL_KERN, KERN_BOOTFILE>(hasher);
+# endif
+# ifdef KERN_BOOTTIME
+ AddSysctl<CTL_KERN, KERN_BOOTTIME>(hasher);
+# endif
+# ifdef KERN_CLOCKRATE
+ AddSysctl<CTL_KERN, KERN_CLOCKRATE>(hasher);
+# endif
+# ifdef KERN_HOSTID
+ AddSysctl<CTL_KERN, KERN_HOSTID>(hasher);
+# endif
+# ifdef KERN_HOSTUUID
+ AddSysctl<CTL_KERN, KERN_HOSTUUID>(hasher);
+# endif
+# ifdef KERN_HOSTNAME
+ AddSysctl<CTL_KERN, KERN_HOSTNAME>(hasher);
+# endif
+# ifdef KERN_OSRELDATE
+ AddSysctl<CTL_KERN, KERN_OSRELDATE>(hasher);
+# endif
+# ifdef KERN_OSRELEASE
+ AddSysctl<CTL_KERN, KERN_OSRELEASE>(hasher);
+# endif
+# ifdef KERN_OSREV
+ AddSysctl<CTL_KERN, KERN_OSREV>(hasher);
+# endif
+# ifdef KERN_OSTYPE
+ AddSysctl<CTL_KERN, KERN_OSTYPE>(hasher);
+# endif
+# ifdef KERN_POSIX1
+ AddSysctl<CTL_KERN, KERN_OSREV>(hasher);
+# endif
+# ifdef KERN_VERSION
+ AddSysctl<CTL_KERN, KERN_VERSION>(hasher);
+# endif
+# endif
+#endif
+
+ // Env variables
+ if (environ) {
+ for (size_t i = 0; environ[i]; ++i) {
+ hasher.Write((const unsigned char*)environ[i], strlen(environ[i]));
+ }
+ }
+
+ // Process, thread, user, session, group, ... ids.
+#ifdef WIN32
+ hasher << GetCurrentProcessId() << GetCurrentThreadId();
+#else
+ hasher << getpid() << getppid() << getsid(0) << getpgid(0) << getuid() << geteuid() << getgid() << getegid();
+#endif
+ hasher << std::this_thread::get_id();
+}
diff --git a/src/randomenv.h b/src/randomenv.h
new file mode 100644
index 0000000000..46cea6f6f2
--- /dev/null
+++ b/src/randomenv.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-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_RANDOMENV_H
+#define BITCOIN_RANDOMENV_H
+
+#include <crypto/sha512.h>
+
+/** Gather non-cryptographic environment data that changes over time. */
+void RandAddDynamicEnv(CSHA512& hasher);
+
+/** Gather non-cryptographic environment data that does not change over time. */
+void RandAddStaticEnv(CSHA512& hasher);
+
+#endif
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 7ba66736a6..946152d9aa 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -15,6 +15,8 @@
#include <hash.h>
#include <index/blockfilterindex.h>
#include <node/coinstats.h>
+#include <node/context.h>
+#include <node/utxo_snapshot.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <policy/rbf.h>
@@ -38,8 +40,6 @@
#include <univalue.h>
-#include <boost/thread/thread.hpp> // boost::thread::interrupt
-
#include <condition_variable>
#include <memory>
#include <mutex>
@@ -54,11 +54,20 @@ static Mutex cs_blockchange;
static std::condition_variable cond_blockchange;
static CUpdatedBlock latestblock;
+CTxMemPool& EnsureMemPool()
+{
+ CHECK_NONFATAL(g_rpc_node);
+ if (!g_rpc_node->mempool) {
+ throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found");
+ }
+ return *g_rpc_node->mempool;
+}
+
/* Calculate the difficulty for a given block index.
*/
double GetDifficulty(const CBlockIndex* blockindex)
{
- assert(blockindex);
+ CHECK_NONFATAL(blockindex);
int nShift = (blockindex->nBits >> 24) & 0xff;
double dDiff =
@@ -957,7 +966,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
PruneBlockFilesManual(height);
const CBlockIndex* block = ::ChainActive().Tip();
- assert(block);
+ CHECK_NONFATAL(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
block = block->pprev;
}
@@ -1252,7 +1261,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
obj.pushKV("pruned", fPruneMode);
if (fPruneMode) {
const CBlockIndex* block = tip;
- assert(block);
+ CHECK_NONFATAL(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
block = block->pprev;
}
@@ -1598,7 +1607,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
}
}
- assert(pindex != nullptr);
+ CHECK_NONFATAL(pindex != nullptr);
if (request.params[0].isNull()) {
blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1));
@@ -1771,7 +1780,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
}
}
- assert(pindex != nullptr);
+ CHECK_NONFATAL(pindex != nullptr);
std::set<std::string> stats;
if (!request.params[1].isNull()) {
@@ -1871,7 +1880,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
}
CAmount txfee = tx_total_in - tx_total_out;
- assert(MoneyRange(txfee));
+ CHECK_NONFATAL(MoneyRange(txfee));
if (do_medianfee) {
fee_array.push_back(txfee);
}
@@ -1975,7 +1984,6 @@ bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>&
Coin coin;
if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false;
if (++count % 8192 == 0) {
- boost::this_thread::interruption_point();
if (should_abort) {
// allow to abort the scan via the abort reference
return false;
@@ -2008,7 +2016,7 @@ public:
explicit CoinsViewScanReserver() : m_could_reserve(false) {}
bool reserve() {
- assert (!m_could_reserve);
+ CHECK_NONFATAL(!m_could_reserve);
std::lock_guard<std::mutex> lock(g_utxosetscan);
if (g_scan_in_progress) {
return false;
@@ -2135,9 +2143,9 @@ UniValue scantxoutset(const JSONRPCRequest& request)
LOCK(cs_main);
::ChainstateActive().ForceFlushStateToDisk();
pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
- assert(pcursor);
+ CHECK_NONFATAL(pcursor);
tip = ::ChainActive().Tip();
- assert(tip);
+ CHECK_NONFATAL(tip);
}
bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
result.pushKV("success", res);
@@ -2245,6 +2253,113 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
return ret;
}
+/**
+ * Serialize the UTXO set to a file for loading elsewhere.
+ *
+ * @see SnapshotMetadata
+ */
+UniValue dumptxoutset(const JSONRPCRequest& request)
+{
+ RPCHelpMan{
+ "dumptxoutset",
+ "\nWrite the serialized UTXO set to disk.\n"
+ "Incidentally flushes the latest coinsdb (leveldb) to disk.\n",
+ {
+ {"path",
+ RPCArg::Type::STR,
+ RPCArg::Optional::NO,
+ /* default_val */ "",
+ "path to the output file. If relative, will be prefixed by datadir."},
+ },
+ RPCResult{
+ "{\n"
+ " \"coins_written\": n, (numeric) the number of coins written in the snapshot\n"
+ " \"base_hash\": \"...\", (string) the hash of the base of the snapshot\n"
+ " \"base_height\": n, (string) the height of the base of the snapshot\n"
+ " \"path\": \"...\" (string) the absolute path that the snapshot was written to\n"
+ "]\n"
+ },
+ RPCExamples{
+ HelpExampleCli("dumptxoutset", "utxo.dat")
+ }
+ }.Check(request);
+
+ fs::path path = fs::absolute(request.params[0].get_str(), GetDataDir());
+ // Write to a temporary path and then move into `path` on completion
+ // to avoid confusion due to an interruption.
+ fs::path temppath = fs::absolute(request.params[0].get_str() + ".incomplete", GetDataDir());
+
+ if (fs::exists(path)) {
+ throw JSONRPCError(
+ RPC_INVALID_PARAMETER,
+ path.string() + " already exists. If you are sure this is what you want, "
+ "move it out of the way first");
+ }
+
+ FILE* file{fsbridge::fopen(temppath, "wb")};
+ CAutoFile afile{file, SER_DISK, CLIENT_VERSION};
+ std::unique_ptr<CCoinsViewCursor> pcursor;
+ CCoinsStats stats;
+ CBlockIndex* tip;
+
+ {
+ // We need to lock cs_main to ensure that the coinsdb isn't written to
+ // between (i) flushing coins cache to disk (coinsdb), (ii) getting stats
+ // based upon the coinsdb, and (iii) constructing a cursor to the
+ // coinsdb for use below this block.
+ //
+ // Cursors returned by leveldb iterate over snapshots, so the contents
+ // of the pcursor will not be affected by simultaneous writes during
+ // use below this block.
+ //
+ // See discussion here:
+ // https://github.com/bitcoin/bitcoin/pull/15606#discussion_r274479369
+ //
+ LOCK(::cs_main);
+
+ ::ChainstateActive().ForceFlushStateToDisk();
+
+ if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats)) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
+ }
+
+ pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
+ tip = LookupBlockIndex(stats.hashBlock);
+ CHECK_NONFATAL(tip);
+ }
+
+ SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx};
+
+ afile << metadata;
+
+ COutPoint key;
+ Coin coin;
+ unsigned int iter{0};
+
+ while (pcursor->Valid()) {
+ if (iter % 5000 == 0 && !IsRPCRunning()) {
+ throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
+ }
+ ++iter;
+ if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
+ afile << key;
+ afile << coin;
+ }
+
+ pcursor->Next();
+ }
+
+ afile.fclose();
+ fs::rename(temppath, path);
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("coins_written", stats.coins_count);
+ result.pushKV("base_hash", tip->GetBlockHash().ToString());
+ result.pushKV("base_height", tip->nHeight);
+ result.pushKV("path", path.string());
+ return result;
+}
+
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -2281,6 +2396,7 @@ static const CRPCCommand commands[] =
{ "hidden", "waitforblock", &waitforblock, {"blockhash","timeout"} },
{ "hidden", "waitforblockheight", &waitforblockheight, {"height","timeout"} },
{ "hidden", "syncwithvalidationinterfacequeue", &syncwithvalidationinterfacequeue, {} },
+ { "hidden", "dumptxoutset", &dumptxoutset, {"path"} },
};
// clang-format on
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 8a1264f824..ccb3e39722 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -52,4 +52,6 @@ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES],
//! direct way to pass in state to RPC methods without globals.
extern NodeContext* g_rpc_node;
+CTxMemPool& EnsureMemPool();
+
#endif
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 00b8dd0255..85f9f1e8b7 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -375,9 +375,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
" }\n"
" ,...\n"
" ],\n"
- " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n"
- " \"flags\" : \"xx\" (string) key name is to be ignored, and value included in scriptSig\n"
- " },\n"
+ " \"coinbaseaux\" : { ... }, (json object) data that should be included in the coinbase's scriptSig content\n"
" \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)\n"
" \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n"
" \"target\" : \"xxxx\", (string) The hash target\n"
@@ -555,7 +553,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
// Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew;
}
- assert(pindexPrev);
+ CHECK_NONFATAL(pindexPrev);
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
const Consensus::Params& consensusParams = Params().GetConsensus();
@@ -597,7 +595,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
entry.pushKV("fee", pblocktemplate->vTxFees[index_in_template]);
int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template];
if (fPreSegWit) {
- assert(nTxSigOps % WITNESS_SCALE_FACTOR == 0);
+ CHECK_NONFATAL(nTxSigOps % WITNESS_SCALE_FACTOR == 0);
nTxSigOps /= WITNESS_SCALE_FACTOR;
}
entry.pushKV("sigops", nTxSigOps);
@@ -607,7 +605,6 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
}
UniValue aux(UniValue::VOBJ);
- aux.pushKV("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()));
arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
@@ -686,9 +683,9 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST;
int64_t nSizeLimit = MAX_BLOCK_SERIALIZED_SIZE;
if (fPreSegWit) {
- assert(nSigOpLimit % WITNESS_SCALE_FACTOR == 0);
+ CHECK_NONFATAL(nSigOpLimit % WITNESS_SCALE_FACTOR == 0);
nSigOpLimit /= WITNESS_SCALE_FACTOR;
- assert(nSizeLimit % WITNESS_SCALE_FACTOR == 0);
+ CHECK_NONFATAL(nSizeLimit % WITNESS_SCALE_FACTOR == 0);
nSizeLimit /= WITNESS_SCALE_FACTOR;
}
result.pushKV("sigoplimit", nSigOpLimit);
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index ef6537e4ec..ca779497b9 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -63,6 +63,9 @@ enum RPCErrorCode
RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //!< Invalid IP/Subnet
RPC_CLIENT_P2P_DISABLED = -31, //!< No valid connection manager instance found
+ //! Chain errors
+ RPC_CLIENT_MEMPOOL_DISABLED = -33, //!< No mempool instance found
+
//! Wallet errors
RPC_WALLET_ERROR = -4, //!< Unspecified problem with wallet (key not found etc.)
RPC_WALLET_INSUFFICIENT_FUNDS = -6, //!< Not enough funds in wallet or account
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 983f251d6b..6f24caee21 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -763,7 +763,9 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
// Parse the prevtxs array
ParsePrevouts(request.params[2], &keystore, coins);
- return SignTransaction(mtx, &keystore, coins, request.params[3]);
+ UniValue result(UniValue::VOBJ);
+ SignTransaction(mtx, &keystore, coins, request.params[3], result);
+ return result;
}
static UniValue sendrawtransaction(const JSONRPCRequest& request)
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index fe98fff4bb..40334883c5 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -268,7 +268,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
}
}
-UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType)
+void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result)
{
int nHashType = ParseSighashString(hashType);
@@ -319,12 +319,12 @@ UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keysto
}
bool fComplete = vErrors.empty();
- UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
result.pushKV("complete", fComplete);
if (!vErrors.empty()) {
+ if (result.exists("errors")) {
+ vErrors.push_backV(result["errors"].getValues());
+ }
result.pushKV("errors", vErrors);
}
-
- return result;
}
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index 1936998ff3..0b7712b83c 100644
--- a/src/rpc/rawtransaction_util.h
+++ b/src/rpc/rawtransaction_util.h
@@ -21,9 +21,9 @@ class SigningProvider;
* @param keystore Temporary keystore containing signing keys
* @param coins Map of unspent outputs
* @param hashType The signature hash type
- * @returns JSON object with details of signed transaction
+ * @param result JSON object where signed transaction results accumulate
*/
-UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType);
+void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result);
/**
* Parse a prevtxs UniValue array and get the map of coins from it
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 653b287e97..cfa3509c65 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -428,7 +428,7 @@ RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RP
std::set<std::string> named_args;
for (const auto& arg : m_args) {
// Should have unique named arguments
- assert(named_args.insert(arg.m_name).second);
+ CHECK_NONFATAL(named_args.insert(arg.m_name).second);
}
}
@@ -620,11 +620,11 @@ std::string RPCArg::ToStringObj(const bool oneline) const
case Type::OBJ:
case Type::OBJ_USER_KEYS:
// Currently unused, so avoid writing dead code
- assert(false);
+ CHECK_NONFATAL(false);
// no default case, so the compiler can warn about missing cases
}
- assert(false);
+ CHECK_NONFATAL(false);
}
std::string RPCArg::ToString(const bool oneline) const
@@ -661,7 +661,7 @@ std::string RPCArg::ToString(const bool oneline) const
// no default case, so the compiler can warn about missing cases
}
- assert(false);
+ CHECK_NONFATAL(false);
}
static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index fdc859b3a0..07a54335ac 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -41,8 +41,6 @@ void CScheduler::serviceQueue()
try {
if (!shouldStop() && taskQueue.empty()) {
reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
- // Use this chance to get more entropy
- RandAddSeedSleep();
}
while (!shouldStop() && taskQueue.empty()) {
// Wait until there is something to do.
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 13cdd6c61a..32b388b7fa 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -815,8 +815,8 @@ std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptCon
}
}
if (ctx == ParseScriptContext::P2SH) {
- if (script_size + 3 > 520) {
- error = strprintf("P2SH script is too large, %d bytes is larger than 520 bytes", script_size + 3);
+ if (script_size + 3 > MAX_SCRIPT_ELEMENT_SIZE) {
+ error = strprintf("P2SH script is too large, %d bytes is larger than %d bytes", script_size + 3, MAX_SCRIPT_ELEMENT_SIZE);
return nullptr;
}
}
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 20fae2eebf..ad833bc025 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -61,17 +61,17 @@ static inline void popstack(std::vector<valtype>& stack)
}
bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
- if (vchPubKey.size() < CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) {
+ if (vchPubKey.size() < CPubKey::COMPRESSED_SIZE) {
// Non-canonical public key: too short
return false;
}
if (vchPubKey[0] == 0x04) {
- if (vchPubKey.size() != CPubKey::PUBLIC_KEY_SIZE) {
+ if (vchPubKey.size() != CPubKey::SIZE) {
// Non-canonical public key: invalid length for uncompressed key
return false;
}
} else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) {
- if (vchPubKey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) {
+ if (vchPubKey.size() != CPubKey::COMPRESSED_SIZE) {
// Non-canonical public key: invalid length for compressed key
return false;
}
@@ -83,7 +83,7 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
}
bool static IsCompressedPubKey(const valtype &vchPubKey) {
- if (vchPubKey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) {
+ if (vchPubKey.size() != CPubKey::COMPRESSED_SIZE) {
// Non-canonical public key: invalid length for compressed key
return false;
}
diff --git a/src/script/sign.h b/src/script/sign.h
index 9d0a5b4d70..4c2403f83f 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -101,7 +101,7 @@ template<typename Stream>
void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, KeyOriginInfo>& hd_keypaths)
{
// Make sure that the key is the size of pubkey + 1
- if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
+ if (key.size() != CPubKey::SIZE + 1 && key.size() != CPubKey::COMPRESSED_SIZE + 1) {
throw std::ios_base::failure("Size of key was not the expected size for the type BIP32 keypath");
}
// Read in the pubkey from key
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index fc6898f444..144bdcff98 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -44,12 +44,12 @@ const char* GetTxnOutputType(txnouttype t)
static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
{
- if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) {
- pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::PUBLIC_KEY_SIZE + 1);
+ if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) {
+ pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1);
return CPubKey::ValidSize(pubkey);
}
- if (script.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) {
- pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1);
+ if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) {
+ pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1);
return CPubKey::ValidSize(pubkey);
}
return false;
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index 5c2050e4a2..85e3351e72 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -23,6 +23,10 @@
#endif
#include <algorithm>
+#ifdef ARENA_DEBUG
+#include <iomanip>
+#include <iostream>
+#endif
LockedPoolManager* LockedPoolManager::_instance = nullptr;
std::once_flag LockedPoolManager::init_flag;
@@ -137,7 +141,7 @@ Arena::Stats Arena::stats() const
}
#ifdef ARENA_DEBUG
-static void printchunk(char* base, size_t sz, bool used) {
+static void printchunk(void* base, size_t sz, bool used) {
std::cout <<
"0x" << std::hex << std::setw(16) << std::setfill('0') << base <<
" 0x" << std::hex << std::setw(16) << std::setfill('0') << sz <<
@@ -149,7 +153,7 @@ void Arena::walk() const
printchunk(chunk.first, chunk.second, true);
std::cout << std::endl;
for (const auto& chunk: chunks_free)
- printchunk(chunk.first, chunk.second, false);
+ printchunk(chunk.first, chunk.second->first, false);
std::cout << std::endl;
}
#endif
diff --git a/src/test/README.md b/src/test/README.md
index 96dcb072bc..731720f654 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -7,8 +7,8 @@ configure some other framework (we want as few impediments to creating
unit tests as possible).
The build system is set up to compile an executable called `test_bitcoin`
-that runs all of the unit tests. The main source file is called
-`setup_common.cpp`.
+that runs all of the unit tests. The main source file for the test library is found in
+`util/setup_common.cpp`.
### Compiling/running unit tests
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index da0abd495a..c034216bc1 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <string>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp
index e333763f27..d33d668a04 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -5,7 +5,7 @@
#include <util/memory.h>
#include <util/system.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <memory>
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index 378fe285d5..e20900ed13 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -4,7 +4,7 @@
#include <amount.h>
#include <policy/feerate.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
index 9ac87261b6..3723a48903 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -11,7 +11,7 @@
#include <uint256.h>
#include <arith_uint256.h>
#include <string>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
BOOST_FIXTURE_TEST_SUITE(arith_uint256_tests, BasicTestingSetup)
diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp
index b3bed2434c..bd6ece935b 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/strencodings.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index cb376cddb6..52301f799a 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -5,7 +5,7 @@
#include <test/data/base58_encode_decode.json.h>
#include <base58.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <univalue.h>
diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp
index 9ffffb0b7d..a5fed55504 100644
--- a/src/test/base64_tests.cpp
+++ b/src/test/base64_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/strencodings.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp
index 0ba492c24e..a2098f4f56 100644
--- a/src/test/bech32_tests.cpp
+++ b/src/test/bech32_tests.cpp
@@ -3,25 +3,13 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bech32.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
+#include <test/util/str.h>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup)
-static bool CaseInsensitiveEqual(const std::string &s1, const std::string &s2)
-{
- if (s1.size() != s2.size()) return false;
- for (size_t i = 0; i < s1.size(); ++i) {
- char c1 = s1[i];
- if (c1 >= 'A' && c1 <= 'Z') c1 -= ('A' - 'a');
- char c2 = s2[i];
- if (c2 >= 'A' && c2 <= 'Z') c2 -= ('A' - 'a');
- if (c1 != c2) return false;
- }
- return true;
-}
-
BOOST_AUTO_TEST_CASE(bip173_testvectors_valid)
{
static const std::string CASES[] = {
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index e46cf624cf..53df032252 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -9,7 +9,7 @@
#include <key_io.h>
#include <streams.h>
#include <util/strencodings.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <string>
#include <vector>
diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index ca75563ef0..29935c09f8 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -4,7 +4,7 @@
#include <chain.h>
#include <rpc/blockchain.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
/* Equality between doubles is imprecise. Comparison should be done
* with a small threshold of tolerance, rather than exact equality.
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index df589b63bf..8694891a51 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -8,7 +8,7 @@
#include <pow.h>
#include <streams.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
index acc6d6a21b..2b616f4c2b 100644
--- a/src/test/blockfilter_index_tests.cpp
+++ b/src/test/blockfilter_index_tests.cpp
@@ -9,8 +9,8 @@
#include <miner.h>
#include <pow.h>
#include <script/standard.h>
-#include <test/lib/blockfilter.h>
-#include <test/setup_common.h>
+#include <test/util/blockfilter.h>
+#include <test/util/setup_common.h>
#include <util/time.h>
#include <validation.h>
diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp
index df0a041e0e..e69503ef35 100644
--- a/src/test/blockfilter_tests.cpp
+++ b/src/test/blockfilter_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/data/blockfilters.json.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <blockfilter.h>
#include <core_io.h>
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 4421494007..4a7ad9b38b 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -15,7 +15,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <vector>
@@ -461,7 +461,7 @@ static std::vector<unsigned char> RandomData()
BOOST_AUTO_TEST_CASE(rolling_bloom)
{
- SeedInsecureRand(/* deterministic */ true);
+ SeedInsecureRand(SeedRand::ZEROS);
g_mock_deterministic_tests = true;
// last-100-entry, 1% false positive:
diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp
index 8fd4e5d5d6..d5e2344a8b 100644
--- a/src/test/bswap_tests.cpp
+++ b/src/test/bswap_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <compat/byteswap.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index d796444419..6745bb9015 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -5,9 +5,8 @@
#include <util/memory.h>
#include <util/system.h>
#include <util/time.h>
-#include <validation.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <checkqueue.h>
#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>
@@ -19,11 +18,10 @@
#include <unordered_set>
-// BasicTestingSetup not sufficient because nScriptCheckThreads is not set
-// otherwise.
BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup)
static const unsigned int QUEUE_BATCH_SIZE = 128;
+static const int SCRIPT_CHECK_THREADS = 3;
struct FakeCheck {
bool operator()()
@@ -149,7 +147,7 @@ static void Correct_Queue_range(std::vector<size_t> range)
{
auto small_queue = MakeUnique<Correct_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
- for (auto x = 0; x < nScriptCheckThreads; ++x) {
+ for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
tg.create_thread([&]{small_queue->Thread();});
}
// Make vChecks here to save on malloc (this test can be slow...)
@@ -214,7 +212,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure)
auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
- for (auto x = 0; x < nScriptCheckThreads; ++x) {
+ for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
tg.create_thread([&]{fail_queue->Thread();});
}
@@ -246,7 +244,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure)
{
auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
- for (auto x = 0; x < nScriptCheckThreads; ++x) {
+ for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
tg.create_thread([&]{fail_queue->Thread();});
}
@@ -274,7 +272,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
{
auto queue = MakeUnique<Unique_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
- for (auto x = 0; x < nScriptCheckThreads; ++x) {
+ for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
tg.create_thread([&]{queue->Thread();});
}
@@ -310,7 +308,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory)
{
auto queue = MakeUnique<Memory_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
- for (auto x = 0; x < nScriptCheckThreads; ++x) {
+ for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
tg.create_thread([&]{queue->Thread();});
}
for (size_t i = 0; i < 1000; ++i) {
@@ -342,7 +340,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
auto queue = MakeUnique<FrozenCleanup_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
bool fails = false;
- for (auto x = 0; x < nScriptCheckThreads; ++x) {
+ for (auto x = 0; x < SCRIPT_CHECK_THREADS; ++x) {
tg.create_thread([&]{queue->Thread();});
}
std::thread t0([&]() {
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 948591196c..436c1bffa0 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -7,7 +7,7 @@
#include <coins.h>
#include <script/standard.h>
#include <streams.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <uint256.h>
#include <undo.h>
#include <util/strencodings.h>
@@ -279,7 +279,7 @@ UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) {
// has the expected effect (the other duplicate is overwritten at all cache levels)
BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
{
- SeedInsecureRand(/* deterministic */ true);
+ SeedInsecureRand(SeedRand::ZEROS);
g_mock_deterministic_tests = true;
bool spent_a_duplicate_coinbase = false;
diff --git a/src/test/compilerbug_tests.cpp b/src/test/compilerbug_tests.cpp
index 74e1eac3ea..1a6fcda009 100644
--- a/src/test/compilerbug_tests.cpp
+++ b/src/test/compilerbug_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(compilerbug_tests, BasicTestingSetup)
diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp
index c6a08b293f..22eae91cf0 100644
--- a/src/test/compress_tests.cpp
+++ b/src/test/compress_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <compressor.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <script/standard.h>
#include <stdint.h>
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 4ac12bf969..591a317d17 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -15,7 +15,7 @@
#include <crypto/sha512.h>
#include <random.h>
#include <util/strencodings.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <vector>
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index a3017da3e7..119d4b3295 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -4,7 +4,7 @@
#include <boost/test/unit_test.hpp>
#include <cuckoocache.h>
#include <script/sigcache.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <random.h>
#include <thread>
@@ -29,7 +29,7 @@ BOOST_AUTO_TEST_SUITE(cuckoocache_tests);
*/
BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
{
- SeedInsecureRand(true);
+ SeedInsecureRand(SeedRand::ZEROS);
CuckooCache::cache<uint256, SignatureCacheHasher> cc{};
size_t megabytes = 4;
cc.setup_bytes(megabytes << 20);
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
template <typename Cache>
static double test_cache(size_t megabytes, double load)
{
- SeedInsecureRand(true);
+ SeedInsecureRand(SeedRand::ZEROS);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
@@ -118,7 +118,7 @@ template <typename Cache>
static void test_cache_erase(size_t megabytes)
{
double load = 1;
- SeedInsecureRand(true);
+ SeedInsecureRand(SeedRand::ZEROS);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
@@ -181,7 +181,7 @@ template <typename Cache>
static void test_cache_erase_parallel(size_t megabytes)
{
double load = 1;
- SeedInsecureRand(true);
+ SeedInsecureRand(SeedRand::ZEROS);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
@@ -285,7 +285,7 @@ static void test_cache_generations()
// iterations with non-deterministic values, so it isn't "overfit" to the
// specific entropy in FastRandomContext(true) and implementation of the
// cache.
- SeedInsecureRand(true);
+ SeedInsecureRand(SeedRand::ZEROS);
// block_activity models a chunk of network activity. n_insert elements are
// added to the cache. The first and last n/4 are stored for removal later
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 2ffe4dccdb..57d5b2bb5c 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -4,7 +4,7 @@
#include <dbwrapper.h>
#include <uint256.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/memory.h>
#include <memory>
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 1928324b27..632151793e 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -17,7 +17,7 @@
#include <util/time.h>
#include <validation.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <stdint.h>
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 55726a4a8f..bcce8854e3 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -6,7 +6,7 @@
#include <string>
#include <script/sign.h>
#include <script/standard.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <script/descriptor.h>
#include <util/strencodings.h>
diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp
index 740d805cce..9bb0b3ef02 100644
--- a/src/test/flatfile_tests.cpp
+++ b/src/test/flatfile_tests.cpp
@@ -5,7 +5,7 @@
#include <clientversion.h>
#include <flatfile.h>
#include <streams.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/system.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index b504a3cbb1..d02c3613ba 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#include <fs.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/system.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/fuzz/bech32.cpp b/src/test/fuzz/bech32.cpp
new file mode 100644
index 0000000000..8b91f9bc96
--- /dev/null
+++ b/src/test/fuzz/bech32.cpp
@@ -0,0 +1,43 @@
+// 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 <bech32.h>
+#include <test/fuzz/fuzz.h>
+#include <test/util/str.h>
+#include <util/strencodings.h>
+
+#include <cassert>
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ const std::string random_string(buffer.begin(), buffer.end());
+ const std::pair<std::string, std::vector<uint8_t>> r1 = bech32::Decode(random_string);
+ if (r1.first.empty()) {
+ assert(r1.second.empty());
+ } else {
+ const std::string& hrp = r1.first;
+ const std::vector<uint8_t>& data = r1.second;
+ const std::string reencoded = bech32::Encode(hrp, data);
+ assert(CaseInsensitiveEqual(random_string, reencoded));
+ }
+
+ std::vector<unsigned char> input;
+ ConvertBits<8, 5, true>([&](unsigned char c) { input.push_back(c); }, buffer.begin(), buffer.end());
+ const std::string encoded = bech32::Encode("bc", input);
+ assert(!encoded.empty());
+
+ const std::pair<std::string, std::vector<uint8_t>> r2 = bech32::Decode(encoded);
+ if (r2.first.empty()) {
+ assert(r2.second.empty());
+ } else {
+ const std::string& hrp = r2.first;
+ const std::vector<uint8_t>& data = r2.second;
+ assert(hrp == "bc");
+ assert(data == input);
+ }
+}
diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp
new file mode 100644
index 0000000000..1ce28f9a6d
--- /dev/null
+++ b/src/test/fuzz/psbt.cpp
@@ -0,0 +1,79 @@
+// 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 <test/fuzz/fuzz.h>
+
+#include <node/psbt.h>
+#include <optional.h>
+#include <psbt.h>
+#include <pubkey.h>
+#include <script/script.h>
+#include <streams.h>
+#include <util/memory.h>
+#include <version.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ static const auto verify_handle = MakeUnique<ECCVerifyHandle>();
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ PartiallySignedTransaction psbt_mut;
+ const std::string raw_psbt{buffer.begin(), buffer.end()};
+ std::string error;
+ if (!DecodeRawPSBT(psbt_mut, raw_psbt, error)) {
+ return;
+ }
+ const PartiallySignedTransaction psbt = psbt_mut;
+
+ const PSBTAnalysis analysis = AnalyzePSBT(psbt);
+ (void)PSBTRoleName(analysis.next);
+ for (const PSBTInputAnalysis& input_analysis : analysis.inputs) {
+ (void)PSBTRoleName(input_analysis.next);
+ }
+
+ (void)psbt.IsNull();
+ (void)psbt.IsSane();
+
+ Optional<CMutableTransaction> tx = psbt.tx;
+ if (tx) {
+ const CMutableTransaction& mtx = *tx;
+ const PartiallySignedTransaction psbt_from_tx{mtx};
+ }
+
+ for (const PSBTInput& input : psbt.inputs) {
+ (void)PSBTInputSigned(input);
+ (void)input.IsNull();
+ (void)input.IsSane();
+ }
+
+ for (const PSBTOutput& output : psbt.outputs) {
+ (void)output.IsNull();
+ }
+
+ for (size_t i = 0; i < psbt.tx->vin.size(); ++i) {
+ CTxOut tx_out;
+ if (psbt.GetInputUTXO(tx_out, i)) {
+ (void)tx_out.IsNull();
+ (void)tx_out.ToString();
+ }
+ }
+
+ psbt_mut = psbt;
+ (void)FinalizePSBT(psbt_mut);
+
+ psbt_mut = psbt;
+ CMutableTransaction result;
+ if (FinalizeAndExtractPSBT(psbt_mut, result)) {
+ const PartiallySignedTransaction psbt_from_tx{result};
+ }
+
+ psbt_mut = psbt;
+ (void)psbt_mut.Merge(psbt);
+}
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 77304fe918..5abd1087ec 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -4,7 +4,7 @@
#include <util/strencodings.h>
#include <util/system.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <string>
#include <utility>
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index d91fcb0034..b864e6e599 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -6,7 +6,7 @@
#include <crypto/siphash.h>
#include <hash.h>
#include <util/strencodings.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index e924f27d1b..b52513f4af 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -9,7 +9,7 @@
#include <key_io.h>
#include <script/script.h>
#include <util/strencodings.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/key_properties.cpp b/src/test/key_properties.cpp
index 95587130fc..0e45a2549d 100644
--- a/src/test/key_properties.cpp
+++ b/src/test/key_properties.cpp
@@ -4,7 +4,7 @@
#include <key.h>
#include <uint256.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <vector>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index 3e99dcaa40..85dc961bea 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -8,7 +8,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <string>
#include <vector>
diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp
index 00b36f51fb..ea18debbd3 100644
--- a/src/test/limitedmap_tests.cpp
+++ b/src/test/limitedmap_tests.cpp
@@ -4,7 +4,7 @@
#include <limitedmap.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/logging_tests.cpp b/src/test/logging_tests.cpp
new file mode 100644
index 0000000000..25655b8894
--- /dev/null
+++ b/src/test/logging_tests.cpp
@@ -0,0 +1,36 @@
+// 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 <logging.h>
+#include <logging/timer.h>
+#include <test/util/setup_common.h>
+
+#include <chrono>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(logging_timer)
+{
+
+ SetMockTime(1);
+ auto sec_timer = BCLog::Timer<std::chrono::seconds>("tests", "end_msg");
+ SetMockTime(2);
+ BOOST_CHECK_EQUAL(sec_timer.LogMsg("test secs"), "tests: test secs (1.00s)");
+
+ SetMockTime(1);
+ auto ms_timer = BCLog::Timer<std::chrono::milliseconds>("tests", "end_msg");
+ SetMockTime(2);
+ BOOST_CHECK_EQUAL(ms_timer.LogMsg("test ms"), "tests: test ms (1000.00ms)");
+
+ SetMockTime(1);
+ auto micro_timer = BCLog::Timer<std::chrono::microseconds>("tests", "end_msg");
+ SetMockTime(2);
+ BOOST_CHECK_EQUAL(micro_timer.LogMsg("test micros"), "tests: test micros (1000000.00μs)");
+
+ SetMockTime(0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index fe5d31b7d3..38fed51af2 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -7,7 +7,7 @@
#include <util/system.h>
#include <util/time.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <vector>
@@ -749,6 +749,43 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
pool.GetTransactionAncestry(ty6->GetHash(), ancestors, descendants);
BOOST_CHECK_EQUAL(ancestors, 9ULL);
BOOST_CHECK_EQUAL(descendants, 6ULL);
+
+ /* Ancestors represented more than once ("diamond") */
+ //
+ // [ta].0 <- [tb].0 -----<------- [td].0
+ // | |
+ // \---1 <- [tc].0 --<--/
+ //
+ CTransactionRef ta, tb, tc, td;
+ ta = make_tx(/* output_values */ {10 * COIN});
+ tb = make_tx(/* output_values */ {5 * COIN, 3 * COIN}, /* inputs */ {ta});
+ tc = make_tx(/* output_values */ {2 * COIN}, /* inputs */ {tb}, /* input_indices */ {1});
+ td = make_tx(/* output_values */ {6 * COIN}, /* inputs */ {tb, tc}, /* input_indices */ {0, 0});
+ pool.clear();
+ pool.addUnchecked(entry.Fee(10000LL).FromTx(ta));
+ pool.addUnchecked(entry.Fee(10000LL).FromTx(tb));
+ pool.addUnchecked(entry.Fee(10000LL).FromTx(tc));
+ pool.addUnchecked(entry.Fee(10000LL).FromTx(td));
+
+ // Ancestors / descendants should be:
+ // transaction ancestors descendants
+ // ============ =================== ===========
+ // ta 1 (ta 4 (ta,tb,tc,td)
+ // tb 2 (ta,tb) 4 (ta,tb,tc,td)
+ // tc 3 (ta,tb,tc) 4 (ta,tb,tc,td)
+ // td 4 (ta,tb,tc,td) 4 (ta,tb,tc,td)
+ pool.GetTransactionAncestry(ta->GetHash(), ancestors, descendants);
+ BOOST_CHECK_EQUAL(ancestors, 1ULL);
+ BOOST_CHECK_EQUAL(descendants, 4ULL);
+ pool.GetTransactionAncestry(tb->GetHash(), ancestors, descendants);
+ BOOST_CHECK_EQUAL(ancestors, 2ULL);
+ BOOST_CHECK_EQUAL(descendants, 4ULL);
+ pool.GetTransactionAncestry(tc->GetHash(), ancestors, descendants);
+ BOOST_CHECK_EQUAL(ancestors, 3ULL);
+ BOOST_CHECK_EQUAL(descendants, 4ULL);
+ pool.GetTransactionAncestry(td->GetHash(), ancestors, descendants);
+ BOOST_CHECK_EQUAL(ancestors, 4ULL);
+ BOOST_CHECK_EQUAL(descendants, 4ULL);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp
index dc38a1a818..03dce552fc 100644
--- a/src/test/merkle_tests.cpp
+++ b/src/test/merkle_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/merkle.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -345,8 +345,8 @@ BOOST_AUTO_TEST_CASE(merkle_test_BlockWitness)
hashes[0].SetNull();
hashes[1] = block.vtx[1]->GetHash();
- uint256 merkelRootofHashes = ComputeMerkleRoot(hashes);
+ uint256 merkleRootofHashes = ComputeMerkleRoot(hashes);
- BOOST_CHECK_EQUAL(merkelRootofHashes, blockWitness);
+ BOOST_CHECK_EQUAL(merkleRootofHashes, blockWitness);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp
index eac43471c7..9f8c4ba5c5 100644
--- a/src/test/merkleblock_tests.cpp
+++ b/src/test/merkleblock_tests.cpp
@@ -4,7 +4,7 @@
#include <merkleblock.h>
#include <uint256.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index c9661b730d..d79a598bf1 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -17,13 +17,23 @@
#include <util/time.h>
#include <validation.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <memory>
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup)
+namespace miner_tests {
+struct MinerTestingSetup : public TestingSetup {
+ void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
+ bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
+ {
+ return CheckSequenceLocks(*m_node.mempool, tx, flags);
+ }
+};
+} // namespace miner_tests
+
+BOOST_FIXTURE_TEST_SUITE(miner_tests, MinerTestingSetup)
// BOOST_CHECK_EXCEPTION predicates to check the specific validation error
class HasReason {
@@ -89,16 +99,10 @@ static CBlockIndex CreateBlockIndex(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
return index;
}
-static bool TestSequenceLocks(const CTransaction &tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- LOCK(::mempool.cs);
- return CheckSequenceLocks(::mempool, tx, flags);
-}
-
// Test suite for ancestor feerate transaction selection.
// Implemented as an additional function, rather than a separate test case,
// to allow reusing the blockchain created in CreateNewBlock_validity.
-static void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs)
+void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
{
// Test the ancestor feerate transaction selection.
TestMemPoolEntryHelper entry;
@@ -114,19 +118,19 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
tx.vout[0].nValue = 5000000000LL - 1000;
// This tx has a low fee: 1000 satoshis
uint256 hashParentTx = tx.GetHash(); // save this txid for later use
- mempool.addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
// This tx has a medium fee: 10000 satoshis
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vout[0].nValue = 5000000000LL - 10000;
uint256 hashMediumFeeTx = tx.GetHash();
- mempool.addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
// This tx has a high fee, but depends on the first transaction
tx.vin[0].prevout.hash = hashParentTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
uint256 hashHighFeeTx = tx.GetHash();
- mempool.addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
@@ -137,7 +141,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
tx.vin[0].prevout.hash = hashHighFeeTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
uint256 hashFreeTx = tx.GetHash();
- mempool.addUnchecked(entry.Fee(0).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(0).FromTx(tx));
size_t freeTxSize = ::GetSerializeSize(tx, PROTOCOL_VERSION);
// Calculate a fee on child transaction that will put the package just
@@ -147,7 +151,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
tx.vin[0].prevout.hash = hashFreeTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse;
uint256 hashLowFeeTx = tx.GetHash();
- mempool.addUnchecked(entry.Fee(feeToUse).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx));
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
// Verify that the free tx and the low fee tx didn't get selected
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
@@ -158,10 +162,10 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
// Test that packages above the min relay fee do get included, even if one
// of the transactions is below the min relay fee
// Remove the low fee transaction and replace with a higher fee transaction
- mempool.removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
+ m_node.mempool->removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
hashLowFeeTx = tx.GetHash();
- mempool.addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);
@@ -174,7 +178,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
tx.vout[0].nValue = 5000000000LL - 100000000;
tx.vout[1].nValue = 100000000; // 1BTC output
uint256 hashFreeTx2 = tx.GetHash();
- mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
// This tx can't be mined by itself
tx.vin[0].prevout.hash = hashFreeTx2;
@@ -182,7 +186,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
feeToUse = blockMinFeeRate.GetFee(freeTxSize);
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
uint256 hashLowFeeTx2 = tx.GetHash();
- mempool.addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
// Verify that this tx isn't selected.
@@ -195,7 +199,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
// as well.
tx.vin[0].prevout.n = 1;
tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
- mempool.addUnchecked(entry.Fee(10000).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx));
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
}
@@ -252,7 +256,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
}
LOCK(cs_main);
- LOCK(::mempool.cs);
+ LOCK(m_node.mempool->cs);
// Just to make sure we can still make simple blocks
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
@@ -276,12 +280,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
hash = tx.GetHash();
bool spendsCoinbase = i == 0; // only first tx spends coinbase
// If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
- mempool.clear();
+ m_node.mempool->clear();
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vout[0].nValue = BLOCKSUBSIDY;
@@ -291,11 +295,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
hash = tx.GetHash();
bool spendsCoinbase = i == 0; // only first tx spends coinbase
// If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- mempool.clear();
+ m_node.mempool->clear();
// block size > limit
tx.vin[0].scriptSig = CScript();
@@ -311,24 +315,24 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue -= LOWFEE;
hash = tx.GetHash();
bool spendsCoinbase = i == 0; // only first tx spends coinbase
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- mempool.clear();
+ m_node.mempool->clear();
- // orphan in mempool, template creation fails
+ // orphan in *m_node.mempool, template creation fails
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
- mempool.clear();
+ m_node.mempool->clear();
// child with higher feerate than parent
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin.resize(2);
tx.vin[1].scriptSig = CScript() << OP_1;
@@ -336,34 +340,34 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[1].prevout.n = 0;
tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- mempool.clear();
+ m_node.mempool->clear();
- // coinbase in mempool, template creation fails
+ // coinbase in *m_node.mempool, template creation fails
tx.vin.resize(1);
tx.vin[0].prevout.SetNull();
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
tx.vout[0].nValue = 0;
hash = tx.GetHash();
// give it a fee so it'll get mined
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
// Should throw bad-cb-multiple
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
- mempool.clear();
+ m_node.mempool->clear();
- // double spend txn pair in mempool, template creation fails
+ // double spend txn pair in *m_node.mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
tx.vout[0].scriptPubKey = CScript() << OP_1;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vout[0].scriptPubKey = CScript() << OP_2;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
- mempool.clear();
+ m_node.mempool->clear();
// subsidy changing
int nHeight = ::ChainActive().Height();
@@ -392,7 +396,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- // invalid p2sh txn in mempool, template creation fails
+ // invalid p2sh txn in *m_node.mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
@@ -400,15 +404,15 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
script = CScript() << OP_0;
tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
tx.vout[0].nValue -= LOWFEE;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
// Should throw block-validation-failed
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
- mempool.clear();
+ m_node.mempool->clear();
// Delete the dummy blocks again.
while (::ChainActive().Tip()->nHeight > nHeight) {
@@ -439,7 +443,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].scriptPubKey = CScript() << OP_1;
tx.nLockTime = 0;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
@@ -449,7 +453,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((::ChainActive().Tip()->GetMedianTimePast()+1-::ChainActive()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
@@ -465,7 +469,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
prevheights[0] = baseheight + 3;
tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
@@ -476,7 +480,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
@@ -513,7 +517,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
::ChainActive().Tip()->nHeight--;
SetMockTime(0);
- mempool.clear();
+ m_node.mempool->clear();
TestPackageSelection(chainparams, scriptPubKey, txFirst);
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 7c60abb93f..97a918da45 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -11,7 +11,7 @@
#include <script/signingprovider.h>
#include <tinyformat.h>
#include <uint256.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index f5f217b841..daf7fea6ad 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -5,7 +5,7 @@
#include <addrdb.h>
#include <addrman.h>
#include <clientversion.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <string>
#include <boost/test/unit_test.hpp>
#include <serialize.h>
@@ -301,5 +301,19 @@ BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
BOOST_CHECK_EQUAL(IsLocal(addr), false);
}
+BOOST_AUTO_TEST_CASE(PoissonNextSend)
+{
+ g_mock_deterministic_tests = true;
+
+ int64_t now = 5000;
+ int average_interval_seconds = 600;
+
+ auto poisson = ::PoissonNextSend(now, average_interval_seconds);
+ std::chrono::microseconds poisson_chrono = ::PoissonNextSend(std::chrono::microseconds{now}, std::chrono::seconds{average_interval_seconds});
+
+ BOOST_CHECK_EQUAL(poisson, poisson_chrono.count());
+
+ g_mock_deterministic_tests = false;
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index a3d0831624..78c11ff202 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -4,7 +4,7 @@
#include <netbase.h>
#include <net_permissions.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <string>
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
index c5513ae9fa..bf58bd63b9 100644
--- a/src/test/pmt_tests.cpp
+++ b/src/test/pmt_tests.cpp
@@ -9,7 +9,7 @@
#include <uint256.h>
#include <arith_uint256.h>
#include <version.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <vector>
@@ -30,7 +30,6 @@ BOOST_FIXTURE_TEST_SUITE(pmt_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(pmt_test1)
{
- SeedInsecureRand(false);
static const unsigned int nTxCounts[] = {1, 4, 7, 17, 56, 100, 127, 256, 312, 513, 1000, 4095};
for (int i = 0; i < 12; i++) {
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 5368f82ffe..025e2b78ca 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -8,7 +8,7 @@
#include <uint256.h>
#include <util/time.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 6c99021d97..0f9872f434 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -5,7 +5,7 @@
#include <chain.h>
#include <chainparams.h>
#include <pow.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index fc1f946bba..9782b78f2c 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -9,7 +9,7 @@
#include <serialize.h>
#include <streams.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/raii_event_tests.cpp b/src/test/raii_event_tests.cpp
index 41ca8029e5..04bf7c20c1 100644
--- a/src/test/raii_event_tests.cpp
+++ b/src/test/raii_event_tests.cpp
@@ -12,7 +12,7 @@
#include <support/events.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index e6fbe2355d..e0df41a971 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -4,7 +4,7 @@
#include <random.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp
index 69db9dcf4e..532fe143ae 100644
--- a/src/test/reverselock_tests.cpp
+++ b/src/test/reverselock_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <reverselock.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index faff1931cd..52dd22de7e 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -9,7 +9,7 @@
#include <core_io.h>
#include <interfaces/chain.h>
#include <node/context.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/time.h>
#include <boost/algorithm/string.hpp>
diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp
index 891aa8e5c3..4d50845256 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -4,7 +4,7 @@
#include <compat/sanity.h>
#include <key.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index 42242b962b..b292d5b0d0 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -5,7 +5,7 @@
#include <random.h>
#include <scheduler.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/thread.hpp>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index ec28d6a0ad..8c1e843b0b 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -11,7 +11,7 @@
#include <policy/settings.h>
#include <script/sign.h>
#include <script/signingprovider.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <vector>
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index 412a57dd9d..de990d9254 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -6,7 +6,7 @@
#include <script/script.h>
#include <script/signingprovider.h>
#include <script/standard.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index caa99805c3..26015ca4c2 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -12,8 +12,8 @@
#include <script/signingprovider.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/lib/transaction_utils.h>
-#include <test/setup_common.h>
+#include <test/util/transaction_utils.h>
+#include <test/util/setup_common.h>
#include <rpc/util.h>
#include <streams.h>
diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp
index e7916f5000..40a6f69668 100644
--- a/src/test/scriptnum_tests.cpp
+++ b/src/test/scriptnum_tests.cpp
@@ -4,7 +4,7 @@
#include <test/scriptnum10.h>
#include <script/script.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <limits.h>
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index b90be15fba..303bb9b88c 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -5,7 +5,7 @@
#include <serialize.h>
#include <streams.h>
#include <hash.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <stdint.h>
diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp
new file mode 100644
index 0000000000..b0ee76ea6b
--- /dev/null
+++ b/src/test/settings_tests.cpp
@@ -0,0 +1,163 @@
+// Copyright (c) 2011-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 <util/settings.h>
+
+#include <test/util.h>
+#include <test/util/setup_common.h>
+
+#include <boost/test/unit_test.hpp>
+#include <univalue.h>
+#include <util/strencodings.h>
+#include <vector>
+
+BOOST_FIXTURE_TEST_SUITE(settings_tests, BasicTestingSetup)
+
+//! Check settings struct contents against expected json strings.
+static void CheckValues(const util::Settings& settings, const std::string& single_val, const std::string& list_val)
+{
+ util::SettingsValue single_value = GetSetting(settings, "section", "name", false, false);
+ util::SettingsValue list_value(util::SettingsValue::VARR);
+ for (const auto& item : GetSettingsList(settings, "section", "name", false)) {
+ list_value.push_back(item);
+ }
+ BOOST_CHECK_EQUAL(single_value.write().c_str(), single_val);
+ BOOST_CHECK_EQUAL(list_value.write().c_str(), list_val);
+};
+
+// Simple settings merge test case.
+BOOST_AUTO_TEST_CASE(Simple)
+{
+ util::Settings settings;
+ settings.command_line_options["name"].push_back("val1");
+ settings.command_line_options["name"].push_back("val2");
+ settings.ro_config["section"]["name"].push_back(2);
+
+ // The last given arg takes precedence when specified via commandline.
+ CheckValues(settings, R"("val2")", R"(["val1","val2",2])");
+
+ util::Settings settings2;
+ settings2.ro_config["section"]["name"].push_back("val2");
+ settings2.ro_config["section"]["name"].push_back("val3");
+
+ // The first given arg takes precedence when specified via config file.
+ CheckValues(settings2, R"("val2")", R"(["val2","val3"])");
+}
+
+// Test different ways settings can be merged, and verify results. This test can
+// be used to confirm that updates to settings code don't change behavior
+// unintentionally.
+struct MergeTestingSetup : public BasicTestingSetup {
+ //! Max number of actions to sequence together. Can decrease this when
+ //! debugging to make test results easier to understand.
+ static constexpr int MAX_ACTIONS = 3;
+
+ enum Action { END, SET, NEGATE, SECTION_SET, SECTION_NEGATE };
+ using ActionList = Action[MAX_ACTIONS];
+
+ //! Enumerate all possible test configurations.
+ template <typename Fn>
+ void ForEachMergeSetup(Fn&& fn)
+ {
+ ActionList arg_actions = {};
+ // command_line_options do not have sections. Only iterate over SET and NEGATE
+ ForEachNoDup(arg_actions, SET, NEGATE, [&]{
+ ActionList conf_actions = {};
+ ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&]{
+ for (bool force_set : {false, true}) {
+ for (bool ignore_default_section_config : {false, true}) {
+ fn(arg_actions, conf_actions, force_set, ignore_default_section_config);
+ }
+ }
+ });
+ });
+ }
+};
+
+// Regression test covering different ways config settings can be merged. The
+// test parses and merges settings, representing the results as strings that get
+// compared against an expected hash. To debug, the result strings can be dumped
+// to a file (see comments below).
+BOOST_FIXTURE_TEST_CASE(Merge, MergeTestingSetup)
+{
+ CHash256 out_sha;
+ FILE* out_file = nullptr;
+ if (const char* out_path = getenv("SETTINGS_MERGE_TEST_OUT")) {
+ out_file = fsbridge::fopen(out_path, "w");
+ if (!out_file) throw std::system_error(errno, std::generic_category(), "fopen failed");
+ }
+
+ const std::string& network = CBaseChainParams::MAIN;
+ ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions, bool force_set,
+ bool ignore_default_section_config) {
+ std::string desc;
+ int value_suffix = 0;
+ util::Settings settings;
+
+ const std::string& name = ignore_default_section_config ? "wallet" : "server";
+ auto push_values = [&](Action action, const char* value_prefix, const std::string& name_prefix,
+ std::vector<util::SettingsValue>& dest) {
+ if (action == SET || action == SECTION_SET) {
+ for (int i = 0; i < 2; ++i) {
+ dest.push_back(value_prefix + std::to_string(++value_suffix));
+ desc += " " + name_prefix + name + "=" + dest.back().get_str();
+ }
+ } else if (action == NEGATE || action == SECTION_NEGATE) {
+ dest.push_back(false);
+ desc += " " + name_prefix + "no" + name;
+ }
+ };
+
+ if (force_set) {
+ settings.forced_settings[name] = "forced";
+ desc += " " + name + "=forced";
+ }
+ for (Action arg_action : arg_actions) {
+ push_values(arg_action, "a", "-", settings.command_line_options[name]);
+ }
+ for (Action conf_action : conf_actions) {
+ bool use_section = conf_action == SECTION_SET || conf_action == SECTION_NEGATE;
+ push_values(conf_action, "c", use_section ? network + "." : "",
+ settings.ro_config[use_section ? network : ""][name]);
+ }
+
+ desc += " || ";
+ desc += GetSetting(settings, network, name, ignore_default_section_config, /* get_chain_name= */ false).write();
+ desc += " |";
+ for (const auto& s : GetSettingsList(settings, network, name, ignore_default_section_config)) {
+ desc += " ";
+ desc += s.write();
+ }
+ desc += " |";
+ if (OnlyHasDefaultSectionSetting(settings, network, name)) desc += " ignored";
+ desc += "\n";
+
+ out_sha.Write((const unsigned char*)desc.data(), desc.size());
+ if (out_file) {
+ BOOST_REQUIRE(fwrite(desc.data(), 1, desc.size(), out_file) == desc.size());
+ }
+ });
+
+ if (out_file) {
+ if (fclose(out_file)) throw std::system_error(errno, std::generic_category(), "fclose failed");
+ out_file = nullptr;
+ }
+
+ unsigned char out_sha_bytes[CSHA256::OUTPUT_SIZE];
+ out_sha.Finalize(out_sha_bytes);
+ std::string out_sha_hex = HexStr(std::begin(out_sha_bytes), std::end(out_sha_bytes));
+
+ // If check below fails, should manually dump the results with:
+ //
+ // SETTINGS_MERGE_TEST_OUT=results.txt ./test_bitcoin --run_test=settings_tests/Merge
+ //
+ // And verify diff against previous results to make sure the changes are expected.
+ //
+ // Results file is formatted like:
+ //
+ // <input> || GetSetting() | GetSettingsList() | OnlyHasDefaultSectionSetting()
+ BOOST_CHECK_EQUAL(out_sha_hex, "79db02d74e3e193196541b67c068b40ebd0c124a24b3ecbe9cbf7e85b1c4ba7a");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index b18f9df72d..2c56bbdbb0 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -10,7 +10,7 @@
#include <script/script.h>
#include <serialize.h>
#include <streams.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <version.h>
@@ -119,8 +119,6 @@ BOOST_FIXTURE_TEST_SUITE(sighash_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(sighash_test)
{
- SeedInsecureRand(false);
-
#if defined(PRINT_SIGHASH_JSON)
std::cout << "[\n";
std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n";
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index a32f2cda92..6462fcefe3 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -9,7 +9,7 @@
#include <script/script.h>
#include <script/standard.h>
#include <uint256.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <vector>
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index 1cba3a1297..7ede79279f 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <vector>
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 6075fbfeca..177d8fda73 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <streams.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -338,7 +338,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file)
BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
{
// Make this test deterministic.
- SeedInsecureRand(true);
+ SeedInsecureRand(SeedRand::ZEROS);
for (int rep = 0; rep < 50; ++rep) {
FILE* file = fsbridge::fopen("streams_test_tmp", "w+b");
diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp
index c1399d2dbe..188e9986ff 100644
--- a/src/test/sync_tests.cpp
+++ b/src/test/sync_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <sync.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp
index 7b00222ab7..4721151197 100644
--- a/src/test/timedata_tests.cpp
+++ b/src/test/timedata_tests.cpp
@@ -5,7 +5,8 @@
#include <netaddress.h>
#include <noui.h>
-#include <test/setup_common.h>
+#include <test/util/logging.h>
+#include <test/util/setup_common.h>
#include <timedata.h>
#include <warnings.h>
@@ -59,9 +60,10 @@ BOOST_AUTO_TEST_CASE(addtimedata)
MultiAddTimeData(3, DEFAULT_MAX_TIME_ADJUSTMENT + 1);
// Filter size is 1 + 3 = 4: It is always initialized with a single element (offset 0)
- noui_suppress();
- MultiAddTimeData(1, DEFAULT_MAX_TIME_ADJUSTMENT + 1); //filter size 5
- noui_reconnect();
+ {
+ ASSERT_DEBUG_LOG("Please check that your computer's date and time are correct!");
+ MultiAddTimeData(1, DEFAULT_MAX_TIME_ADJUSTMENT + 1); //filter size 5
+ }
BOOST_CHECK(GetWarnings("gui").find("clock is wrong") != std::string::npos);
diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp
index d846062d9b..41aa17988c 100644
--- a/src/test/torcontrol_tests.cpp
+++ b/src/test/torcontrol_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index a8c8918733..eb0050a4a3 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -4,7 +4,7 @@
#include <test/data/tx_invalid.json.h>
#include <test/data/tx_valid.json.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <clientversion.h>
#include <checkqueue.h>
@@ -784,6 +784,19 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
reason.clear();
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "multi-op-return");
+
+ // Check large scriptSig (non-standard if size is >1650 bytes)
+ t.vout.resize(1);
+ t.vout[0].nValue = MAX_MONEY;
+ t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
+ // OP_PUSHDATA2 with len (3 bytes) + data (1647 bytes) = 1650 bytes
+ t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(1647, 0); // 1650
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+
+ t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(1648, 0); // 1651
+ reason.clear();
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
+ BOOST_CHECK_EQUAL(reason, "scriptsig-size");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index 0ac4b7ebc9..4b0214a15a 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -5,7 +5,7 @@
#include <chainparams.h>
#include <index/txindex.h>
#include <script/standard.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/time.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 391ebfadfb..cace75f093 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -6,7 +6,7 @@
#include <consensus/validation.h>
#include <primitives/transaction.h>
#include <script/script.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -34,17 +34,17 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
LOCK(cs_main);
- unsigned int initialPoolSize = mempool.size();
+ unsigned int initialPoolSize = m_node.mempool->size();
BOOST_CHECK_EQUAL(
false,
- AcceptToMemoryPool(mempool, state, MakeTransactionRef(coinbaseTx),
+ AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
nullptr /* plTxnReplaced */,
true /* bypass_limits */,
0 /* nAbsurdFee */));
// Check that the transaction hasn't been added to mempool.
- BOOST_CHECK_EQUAL(mempool.size(), initialPoolSize);
+ BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
// Check that the validation state reflects the unsuccessful attempt.
BOOST_CHECK(state.IsInvalid());
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 144230b114..67f45c4ed4 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -9,7 +9,7 @@
#include <script/standard.h>
#include <script/sign.h>
#include <script/signingprovider.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -17,16 +17,6 @@ bool CheckInputs(const CTransaction& tx, TxValidationState &state, const CCoinsV
BOOST_AUTO_TEST_SUITE(tx_validationcache_tests)
-static bool
-ToMemPool(const CMutableTransaction& tx)
-{
- LOCK(cs_main);
-
- TxValidationState state;
- return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx),
- nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
-}
-
BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
{
// Make sure skipping validation of transactions that were
@@ -35,6 +25,14 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ const auto ToMemPool = [this](const CMutableTransaction& tx) {
+ LOCK(cs_main);
+
+ TxValidationState state;
+ return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
+ nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
+ };
+
// Create a double-spend of mature coinbase txn:
std::vector<CMutableTransaction> spends;
spends.resize(2);
@@ -72,7 +70,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
LOCK(cs_main);
BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
}
- mempool.clear();
+ m_node.mempool->clear();
// Test 3: ... and should be rejected if spend2 is in the memory pool
BOOST_CHECK(ToMemPool(spends[1]));
@@ -81,9 +79,9 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
LOCK(cs_main);
BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
}
- mempool.clear();
+ m_node.mempool->clear();
- // Final sanity test: first spend in mempool, second in block, that's OK:
+ // Final sanity test: first spend in *m_node.mempool, second in block, that's OK:
std::vector<CMutableTransaction> oneSpend;
oneSpend.push_back(spends[0]);
BOOST_CHECK(ToMemPool(spends[1]));
@@ -94,7 +92,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
}
// spends[1] should have been removed from the mempool when the
// block with spends[0] is accepted:
- BOOST_CHECK_EQUAL(mempool.size(), 0U);
+ BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U);
}
// Run CheckInputs (using CoinsTip()) on the given transaction, for all script
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index 33a118c2bb..7293ecd325 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -6,7 +6,7 @@
#include <streams.h>
#include <uint256.h>
#include <version.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <sstream>
diff --git a/src/test/util/README.md b/src/test/util/README.md
new file mode 100644
index 0000000000..36ad645201
--- /dev/null
+++ b/src/test/util/README.md
@@ -0,0 +1,11 @@
+# Test library
+
+This contains files for the test library, which is used by the test binaries (unit tests, benchmarks, fuzzers, gui
+tests).
+
+Generally, the files in this folder should be well-separated modules. New code should be added to existing modules or
+(when in doubt) a new module should be created.
+
+The utilities in here are compiled into a library, which does not hold any state. However, the main file `setup_common`
+defines the common test setup for all test binaries. The test binaries will handle the global state when they
+instantiate the `BasicTestingSetup` (or one of its derived classes).
diff --git a/src/test/lib/blockfilter.cpp b/src/test/util/blockfilter.cpp
index ddcee85d7e..bccff5e5a6 100644
--- a/src/test/lib/blockfilter.cpp
+++ b/src/test/util/blockfilter.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <test/lib/blockfilter.h>
+#include <test/util/blockfilter.h>
#include <chainparams.h>
#include <validation.h>
diff --git a/src/test/lib/blockfilter.h b/src/test/util/blockfilter.h
index 392dacbe80..79d11dcad8 100644
--- a/src/test/lib/blockfilter.h
+++ b/src/test/util/blockfilter.h
@@ -2,12 +2,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_TEST_LIB_BLOCKFILTER_H
-#define BITCOIN_TEST_LIB_BLOCKFILTER_H
+#ifndef BITCOIN_TEST_UTIL_BLOCKFILTER_H
+#define BITCOIN_TEST_UTIL_BLOCKFILTER_H
#include <blockfilter.h>
class CBlockIndex;
bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex* block_index, BlockFilter& filter);
-#endif // BITCOIN_TEST_LIB_BLOCKFILTER_H
+#endif // BITCOIN_TEST_UTIL_BLOCKFILTER_H
diff --git a/src/test/util/logging.cpp b/src/test/util/logging.cpp
new file mode 100644
index 0000000000..fe2e69104b
--- /dev/null
+++ b/src/test/util/logging.cpp
@@ -0,0 +1,32 @@
+// 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 <test/util/logging.h>
+
+#include <logging.h>
+#include <noui.h>
+#include <tinyformat.h>
+#include <util/memory.h>
+
+#include <stdexcept>
+
+DebugLogHelper::DebugLogHelper(std::string message)
+ : m_message{std::move(message)}
+{
+ m_print_connection = LogInstance().PushBackCallback(
+ [this](const std::string& s) {
+ if (m_found) return;
+ m_found = s.find(m_message) != std::string::npos;
+ });
+ noui_test_redirect();
+}
+
+void DebugLogHelper::check_found()
+{
+ noui_reconnect();
+ LogInstance().DeleteCallback(m_print_connection);
+ if (!m_found) {
+ throw std::runtime_error(strprintf("'%s' not found in debug log\n", m_message));
+ }
+}
diff --git a/src/test/util/logging.h b/src/test/util/logging.h
new file mode 100644
index 0000000000..45ec44173c
--- /dev/null
+++ b/src/test/util/logging.h
@@ -0,0 +1,29 @@
+// 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_LOGGING_H
+#define BITCOIN_TEST_UTIL_LOGGING_H
+
+#include <util/macros.h>
+
+#include <functional>
+#include <list>
+#include <string>
+
+class DebugLogHelper
+{
+ const std::string m_message;
+ bool m_found{false};
+ std::list<std::function<void(const std::string&)>>::iterator m_print_connection;
+
+ void check_found();
+
+public:
+ DebugLogHelper(std::string message);
+ ~DebugLogHelper() { check_found(); }
+};
+
+#define ASSERT_DEBUG_LOG(message) DebugLogHelper PASTE2(debugloghelper, __COUNTER__)(message)
+
+#endif // BITCOIN_TEST_UTIL_LOGGING_H
diff --git a/src/test/setup_common.cpp b/src/test/util/setup_common.cpp
index 73517f3f9b..86c355fdcd 100644
--- a/src/test/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <banman.h>
#include <chainparams.h>
@@ -34,6 +34,27 @@
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
FastRandomContext g_insecure_rand_ctx;
+/** Random context to get unique temp data dirs. Separate from g_insecure_rand_ctx, which can be seeded from a const env var */
+static FastRandomContext g_insecure_rand_ctx_temp_path;
+
+/** Return the unsigned from the environment var if available, otherwise 0 */
+static uint256 GetUintFromEnv(const std::string& env_name)
+{
+ const char* num = std::getenv(env_name.c_str());
+ if (!num) return {};
+ return uint256S(num);
+}
+
+void Seed(FastRandomContext& ctx)
+{
+ // Should be enough to get the seed once for the process
+ static uint256 seed{};
+ static const std::string RANDOM_CTX_SEED{"RANDOM_CTX_SEED"};
+ if (seed.IsNull()) seed = GetUintFromEnv(RANDOM_CTX_SEED);
+ if (seed.IsNull()) seed = GetRandHash();
+ LogPrintf("%s: Setting random seed for current tests to %s=%s\n", __func__, RANDOM_CTX_SEED, seed.GetHex());
+ ctx = FastRandomContext(seed);
+}
std::ostream& operator<<(std::ostream& os, const uint256& num)
{
@@ -42,12 +63,13 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
}
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
- : m_path_root(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
+ : m_path_root{fs::temp_directory_path() / "test_common_" PACKAGE_NAME / std::to_string(g_insecure_rand_ctx_temp_path.rand32())}
{
fs::create_directories(m_path_root);
gArgs.ForceSetArg("-datadir", m_path_root.string());
ClearDatadirCache();
SelectParams(chainName);
+ SeedInsecureRand();
gArgs.ForceSetArg("-printtoconsole", "0");
InitLogging();
LogInstance().StartLogging();
@@ -85,7 +107,6 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
- mempool.setSanityCheck(1.0);
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
g_chainstate = MakeUnique<CChainState>();
::ChainstateActive().InitCoinsDB(
@@ -102,10 +123,15 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state)));
}
- nScriptCheckThreads = 3;
- for (int i = 0; i < nScriptCheckThreads - 1; i++)
+ // Start script-checking threads. Set g_parallel_script_checks to true so they are used.
+ constexpr int script_check_threads = 2;
+ for (int i = 0; i < script_check_threads; ++i) {
threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
+ }
+ g_parallel_script_checks = true;
+ m_node.mempool = &::mempool;
+ m_node.mempool->setSanityCheck(1.0);
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
}
@@ -119,6 +145,7 @@ TestingSetup::~TestingSetup()
g_rpc_node = nullptr;
m_node.connman.reset();
m_node.banman.reset();
+ m_node.mempool = nullptr;
UnloadBlockIndex();
g_chainstate.reset();
pblocktree.reset();
@@ -129,6 +156,7 @@ TestChain100Setup::TestChain100Setup()
// CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
// TODO: fix the code to support SegWit blocks.
gArgs.ForceSetArg("-segwitheight", "432");
+ // Need to recreate chainparams
SelectParams(CBaseChainParams::REGTEST);
// Generate a 100-block chain:
@@ -142,12 +170,9 @@ TestChain100Setup::TestChain100Setup()
}
}
-//
// Create a new block with just given transactions, coinbase paying to
// scriptPubKey, and try to add it to the current chain.
-//
-CBlock
-TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
+CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
@@ -175,6 +200,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
TestChain100Setup::~TestChain100Setup()
{
+ gArgs.ForceSetArg("-segwitheight", "0");
}
diff --git a/src/test/setup_common.h b/src/test/util/setup_common.h
index 465baf90c3..1e2e059a56 100644
--- a/src/test/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_TEST_SETUP_COMMON_H
-#define BITCOIN_TEST_SETUP_COMMON_H
+#ifndef BITCOIN_TEST_UTIL_SETUP_COMMON_H
+#define BITCOIN_TEST_UTIL_SETUP_COMMON_H
#include <chainparamsbase.h>
#include <fs.h>
@@ -39,9 +39,21 @@ extern FastRandomContext g_insecure_rand_ctx;
*/
extern bool g_mock_deterministic_tests;
-static inline void SeedInsecureRand(bool deterministic = false)
+enum class SeedRand {
+ ZEROS, //!< Seed with a compile time constant of zeros
+ SEED, //!< Call the Seed() helper
+};
+
+/** Seed the given random ctx or use the seed passed in via an environment var */
+void Seed(FastRandomContext& ctx);
+
+static inline void SeedInsecureRand(SeedRand seed = SeedRand::SEED)
{
- g_insecure_rand_ctx = FastRandomContext(deterministic);
+ if (seed == SeedRand::ZEROS) {
+ g_insecure_rand_ctx = FastRandomContext(/* deterministic */ true);
+ } else {
+ Seed(g_insecure_rand_ctx);
+ }
}
static inline uint32_t InsecureRand32() { return g_insecure_rand_ctx.rand32(); }
diff --git a/src/test/util/str.cpp b/src/test/util/str.cpp
new file mode 100644
index 0000000000..c517fe44d9
--- /dev/null
+++ b/src/test/util/str.cpp
@@ -0,0 +1,21 @@
+// 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 <test/util/str.h>
+
+#include <cstdint>
+#include <string>
+
+bool CaseInsensitiveEqual(const std::string& s1, const std::string& s2)
+{
+ if (s1.size() != s2.size()) return false;
+ for (size_t i = 0; i < s1.size(); ++i) {
+ char c1 = s1[i];
+ if (c1 >= 'A' && c1 <= 'Z') c1 -= ('A' - 'a');
+ char c2 = s2[i];
+ if (c2 >= 'A' && c2 <= 'Z') c2 -= ('A' - 'a');
+ if (c1 != c2) return false;
+ }
+ return true;
+}
diff --git a/src/test/util/str.h b/src/test/util/str.h
new file mode 100644
index 0000000000..63629501e8
--- /dev/null
+++ b/src/test/util/str.h
@@ -0,0 +1,12 @@
+// 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_STR_H
+#define BITCOIN_TEST_UTIL_STR_H
+
+#include <string>
+
+bool CaseInsensitiveEqual(const std::string& s1, const std::string& s2);
+
+#endif // BITCOIN_TEST_UTIL_STR_H
diff --git a/src/test/lib/transaction_utils.cpp b/src/test/util/transaction_utils.cpp
index 2619fb9006..90b78effb0 100644
--- a/src/test/lib/transaction_utils.cpp
+++ b/src/test/util/transaction_utils.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <test/lib/transaction_utils.h>
+#include <test/util/transaction_utils.h>
CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey, int nValue)
{
diff --git a/src/test/lib/transaction_utils.h b/src/test/util/transaction_utils.h
index 6f297ac34f..57604646e7 100644
--- a/src/test/lib/transaction_utils.h
+++ b/src/test/util/transaction_utils.h
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_TEST_LIB_TRANSACTION_UTILS_H
-#define BITCOIN_TEST_LIB_TRANSACTION_UTILS_H
+#ifndef BITCOIN_TEST_UTIL_TRANSACTION_UTILS_H
+#define BITCOIN_TEST_UTIL_TRANSACTION_UTILS_H
#include <primitives/transaction.h>
@@ -16,4 +16,4 @@ CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey, int n
// 1 output with empty scriptPubKey, full value of referenced transaction]
CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CScriptWitness& scriptWitness, const CTransaction& txCredit);
-#endif // BITCOIN_TEST_LIB_TRANSACTION_UTILS_H
+#endif // BITCOIN_TEST_UTIL_TRANSACTION_UTILS_H
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 569ce53092..b9fcd97a8f 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -6,7 +6,7 @@
#include <clientversion.h>
#include <sync.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <test/util.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
@@ -17,6 +17,7 @@
#include <stdint.h>
#include <thread>
+#include <univalue.h>
#include <utility>
#include <vector>
#ifndef WIN32
@@ -166,14 +167,12 @@ BOOST_AUTO_TEST_CASE(util_FormatISO8601Date)
struct TestArgsManager : public ArgsManager
{
TestArgsManager() { m_network_only_args.clear(); }
- std::map<std::string, std::vector<std::string> >& GetOverrideArgs() { return m_override_args; }
- std::map<std::string, std::vector<std::string> >& GetConfigArgs() { return m_config_args; }
void ReadConfigString(const std::string str_config)
{
std::istringstream streamConfig(str_config);
{
LOCK(cs_args);
- m_config_args.clear();
+ m_settings.ro_config.clear();
m_config_sections.clear();
}
std::string error;
@@ -193,6 +192,7 @@ struct TestArgsManager : public ArgsManager
using ArgsManager::ReadConfigStream;
using ArgsManager::cs_args;
using ArgsManager::m_network;
+ using ArgsManager::m_settings;
};
BOOST_AUTO_TEST_CASE(util_ParseParameters)
@@ -206,31 +206,86 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters)
const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"};
std::string error;
+ LOCK(testArgs.cs_args);
testArgs.SetupArgs({a, b, ccc, d});
BOOST_CHECK(testArgs.ParseParameters(0, (char**)argv_test, error));
- BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty());
+ BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty());
BOOST_CHECK(testArgs.ParseParameters(1, (char**)argv_test, error));
- BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty());
+ BOOST_CHECK(testArgs.m_settings.command_line_options.empty() && testArgs.m_settings.ro_config.empty());
BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
// expectation: -ignored is ignored (program name argument),
// -a, -b and -ccc end up in map, -d ignored because it is after
// a non-option argument (non-GNU option parsing)
- BOOST_CHECK(testArgs.GetOverrideArgs().size() == 3 && testArgs.GetConfigArgs().empty());
+ BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 3 && testArgs.m_settings.ro_config.empty());
BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc")
&& !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d"));
- BOOST_CHECK(testArgs.GetOverrideArgs().count("-a") && testArgs.GetOverrideArgs().count("-b") && testArgs.GetOverrideArgs().count("-ccc")
- && !testArgs.GetOverrideArgs().count("f") && !testArgs.GetOverrideArgs().count("-d"));
-
- BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].size() == 1);
- BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].front() == "");
- BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].size() == 2);
- BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].front() == "argument");
- BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].back() == "multiple");
+ BOOST_CHECK(testArgs.m_settings.command_line_options.count("a") && testArgs.m_settings.command_line_options.count("b") && testArgs.m_settings.command_line_options.count("ccc")
+ && !testArgs.m_settings.command_line_options.count("f") && !testArgs.m_settings.command_line_options.count("d"));
+
+ BOOST_CHECK(testArgs.m_settings.command_line_options["a"].size() == 1);
+ BOOST_CHECK(testArgs.m_settings.command_line_options["a"].front().get_str() == "");
+ BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].size() == 2);
+ BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].front().get_str() == "argument");
+ BOOST_CHECK(testArgs.m_settings.command_line_options["ccc"].back().get_str() == "multiple");
BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2);
}
+static void TestParse(const std::string& str, bool expected_bool, int64_t expected_int)
+{
+ TestArgsManager test;
+ test.SetupArgs({{"-value", ArgsManager::ALLOW_ANY}});
+ std::string arg = "-value=" + str;
+ const char* argv[] = {"ignored", arg.c_str()};
+ std::string error;
+ BOOST_CHECK(test.ParseParameters(2, (char**)argv, error));
+ BOOST_CHECK_EQUAL(test.GetBoolArg("-value", false), expected_bool);
+ BOOST_CHECK_EQUAL(test.GetBoolArg("-value", true), expected_bool);
+ BOOST_CHECK_EQUAL(test.GetArg("-value", 99998), expected_int);
+ BOOST_CHECK_EQUAL(test.GetArg("-value", 99999), expected_int);
+}
+
+// Test bool and int parsing.
+BOOST_AUTO_TEST_CASE(util_ArgParsing)
+{
+ // Some of these cases could be ambiguous or surprising to users, and might
+ // be worth triggering errors or warnings in the future. But for now basic
+ // test coverage is useful to avoid breaking backwards compatibility
+ // unintentionally.
+ TestParse("", true, 0);
+ TestParse(" ", false, 0);
+ TestParse("0", false, 0);
+ TestParse("0 ", false, 0);
+ TestParse(" 0", false, 0);
+ TestParse("+0", false, 0);
+ TestParse("-0", false, 0);
+ TestParse("5", true, 5);
+ TestParse("5 ", true, 5);
+ TestParse(" 5", true, 5);
+ TestParse("+5", true, 5);
+ TestParse("-5", true, -5);
+ TestParse("0 5", false, 0);
+ TestParse("5 0", true, 5);
+ TestParse("050", true, 50);
+ TestParse("0.", false, 0);
+ TestParse("5.", true, 5);
+ TestParse("0.0", false, 0);
+ TestParse("0.5", false, 0);
+ TestParse("5.0", true, 5);
+ TestParse("5.5", true, 5);
+ TestParse("x", false, 0);
+ TestParse("x0", false, 0);
+ TestParse("x5", false, 0);
+ TestParse("0x", false, 0);
+ TestParse("5x", true, 5);
+ TestParse("0x5", false, 0);
+ TestParse("false", false, 0);
+ TestParse("true", false, 0);
+ TestParse("yes", false, 0);
+ TestParse("no", false, 0);
+}
+
BOOST_AUTO_TEST_CASE(util_GetBoolArg)
{
TestArgsManager testArgs;
@@ -244,6 +299,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg)
const char *argv_test[] = {
"ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"};
std::string error;
+ LOCK(testArgs.cs_args);
testArgs.SetupArgs({a, b, c, d, e, f});
BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
@@ -252,8 +308,8 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg)
BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt);
// Nothing else should be in the map
- BOOST_CHECK(testArgs.GetOverrideArgs().size() == 6 &&
- testArgs.GetConfigArgs().empty());
+ BOOST_CHECK(testArgs.m_settings.command_line_options.size() == 6 &&
+ testArgs.m_settings.ro_config.empty());
// The -no prefix should get stripped on the way in.
BOOST_CHECK(!testArgs.IsArgSet("-nob"));
@@ -349,6 +405,7 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
"iii=2\n";
TestArgsManager test_args;
+ LOCK(test_args.cs_args);
const auto a = std::make_pair("-a", ArgsManager::ALLOW_BOOL);
const auto b = std::make_pair("-b", ArgsManager::ALLOW_BOOL);
const auto ccc = std::make_pair("-ccc", ArgsManager::ALLOW_STRING);
@@ -365,22 +422,25 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
// expectation: a, b, ccc, d, fff, ggg, h, i end up in map
// so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii
- BOOST_CHECK(test_args.GetOverrideArgs().empty());
- BOOST_CHECK(test_args.GetConfigArgs().size() == 13);
-
- BOOST_CHECK(test_args.GetConfigArgs().count("-a")
- && test_args.GetConfigArgs().count("-b")
- && test_args.GetConfigArgs().count("-ccc")
- && test_args.GetConfigArgs().count("-d")
- && test_args.GetConfigArgs().count("-fff")
- && test_args.GetConfigArgs().count("-ggg")
- && test_args.GetConfigArgs().count("-h")
- && test_args.GetConfigArgs().count("-i")
+ BOOST_CHECK(test_args.m_settings.command_line_options.empty());
+ BOOST_CHECK(test_args.m_settings.ro_config.size() == 3);
+ BOOST_CHECK(test_args.m_settings.ro_config[""].size() == 8);
+ BOOST_CHECK(test_args.m_settings.ro_config["sec1"].size() == 3);
+ BOOST_CHECK(test_args.m_settings.ro_config["sec2"].size() == 2);
+
+ BOOST_CHECK(test_args.m_settings.ro_config[""].count("a")
+ && test_args.m_settings.ro_config[""].count("b")
+ && test_args.m_settings.ro_config[""].count("ccc")
+ && test_args.m_settings.ro_config[""].count("d")
+ && test_args.m_settings.ro_config[""].count("fff")
+ && test_args.m_settings.ro_config[""].count("ggg")
+ && test_args.m_settings.ro_config[""].count("h")
+ && test_args.m_settings.ro_config[""].count("i")
);
- BOOST_CHECK(test_args.GetConfigArgs().count("-sec1.ccc")
- && test_args.GetConfigArgs().count("-sec1.h")
- && test_args.GetConfigArgs().count("-sec2.ccc")
- && test_args.GetConfigArgs().count("-sec2.iii")
+ BOOST_CHECK(test_args.m_settings.ro_config["sec1"].count("ccc")
+ && test_args.m_settings.ro_config["sec1"].count("h")
+ && test_args.m_settings.ro_config["sec2"].count("ccc")
+ && test_args.m_settings.ro_config["sec2"].count("iii")
);
BOOST_CHECK(test_args.IsArgSet("-a")
@@ -519,24 +579,25 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream)
BOOST_AUTO_TEST_CASE(util_GetArg)
{
TestArgsManager testArgs;
- testArgs.GetOverrideArgs().clear();
- testArgs.GetOverrideArgs()["strtest1"] = {"string..."};
+ LOCK(testArgs.cs_args);
+ testArgs.m_settings.command_line_options.clear();
+ testArgs.m_settings.command_line_options["strtest1"] = {"string..."};
// strtest2 undefined on purpose
- testArgs.GetOverrideArgs()["inttest1"] = {"12345"};
- testArgs.GetOverrideArgs()["inttest2"] = {"81985529216486895"};
+ testArgs.m_settings.command_line_options["inttest1"] = {"12345"};
+ testArgs.m_settings.command_line_options["inttest2"] = {"81985529216486895"};
// inttest3 undefined on purpose
- testArgs.GetOverrideArgs()["booltest1"] = {""};
+ testArgs.m_settings.command_line_options["booltest1"] = {""};
// booltest2 undefined on purpose
- testArgs.GetOverrideArgs()["booltest3"] = {"0"};
- testArgs.GetOverrideArgs()["booltest4"] = {"1"};
+ testArgs.m_settings.command_line_options["booltest3"] = {"0"};
+ testArgs.m_settings.command_line_options["booltest4"] = {"1"};
// priorities
- testArgs.GetOverrideArgs()["pritest1"] = {"a", "b"};
- testArgs.GetConfigArgs()["pritest2"] = {"a", "b"};
- testArgs.GetOverrideArgs()["pritest3"] = {"a"};
- testArgs.GetConfigArgs()["pritest3"] = {"b"};
- testArgs.GetOverrideArgs()["pritest4"] = {"a","b"};
- testArgs.GetConfigArgs()["pritest4"] = {"c","d"};
+ testArgs.m_settings.command_line_options["pritest1"] = {"a", "b"};
+ testArgs.m_settings.ro_config[""]["pritest2"] = {"a", "b"};
+ testArgs.m_settings.command_line_options["pritest3"] = {"a"};
+ testArgs.m_settings.ro_config[""]["pritest3"] = {"b"};
+ testArgs.m_settings.command_line_options["pritest4"] = {"a","b"};
+ testArgs.m_settings.ro_config[""]["pritest4"] = {"c","d"};
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string...");
BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default");
@@ -890,6 +951,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
desc += " ";
desc += argstr + 1;
conf += argstr + 1;
+ conf += "\n";
}
std::istringstream conf_stream(conf);
BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error));
@@ -928,7 +990,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
// Results file is formatted like:
//
// <input> || <output>
- BOOST_CHECK_EQUAL(out_sha_hex, "94b4ad55c8ac639a56b93e36f7e32e4c611fd7d7dd7b2be6a71707b1eadcaec7");
+ BOOST_CHECK_EQUAL(out_sha_hex, "f0b3a3c29869edc765d579c928f7f1690a71fbb673b49ccf39cbc4de18156a0d");
}
BOOST_AUTO_TEST_CASE(util_FormatMoney)
@@ -1051,7 +1113,7 @@ BOOST_AUTO_TEST_CASE(util_IsHexNumber)
BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
{
- SeedInsecureRand(true);
+ SeedInsecureRand(SeedRand::ZEROS);
for (int mod=2;mod<11;mod++)
{
int mask = 1;
diff --git a/src/test/util_threadnames_tests.cpp b/src/test/util_threadnames_tests.cpp
index 71c0168ca3..33f840d9c5 100644
--- a/src/test/util_threadnames_tests.cpp
+++ b/src/test/util_threadnames_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/threadnames.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <thread>
#include <vector>
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index ae998e92a5..1fa48b325c 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -11,7 +11,7 @@
#include <pow.h>
#include <random.h>
#include <script/standard.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/time.h>
#include <validation.h>
#include <validationinterface.h>
@@ -40,9 +40,10 @@ struct TestSubscriber : public CValidationInterface {
m_expected_tip = block->GetHash();
}
- void BlockDisconnected(const std::shared_ptr<const CBlock>& block) override
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override
{
BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash());
+ BOOST_CHECK_EQUAL(m_expected_tip, pindex->GetBlockHash());
m_expected_tip = block->hashPrevBlock;
}
@@ -278,7 +279,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
std::list<CTransactionRef> plTxnReplaced;
for (const auto& tx : txs) {
BOOST_REQUIRE(AcceptToMemoryPool(
- ::mempool,
+ *m_node.mempool,
state,
tx,
&plTxnReplaced,
@@ -289,8 +290,8 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// Check that all txs are in the pool
{
- LOCK(::mempool.cs);
- BOOST_CHECK_EQUAL(::mempool.mapTx.size(), txs.size());
+ LOCK(m_node.mempool->cs);
+ BOOST_CHECK_EQUAL(m_node.mempool->mapTx.size(), txs.size());
}
// Run a thread that simulates an RPC caller that is polling while
@@ -300,8 +301,8 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// the transactions invalidated by the reorg, or none of them, and
// not some intermediate amount.
while (true) {
- LOCK(::mempool.cs);
- if (::mempool.mapTx.size() == 0) {
+ LOCK(m_node.mempool->cs);
+ if (m_node.mempool->mapTx.size() == 0) {
// We are done with the reorg
break;
}
@@ -310,7 +311,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// be atomic. So the caller assumes that the returned mempool
// is consistent. That is, it has all txs that were there
// before the reorg.
- assert(::mempool.mapTx.size() == txs.size());
+ assert(m_node.mempool->mapTx.size() == txs.size());
continue;
}
LOCK(cs_main);
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index 101025d31e..3b961db52d 100644
--- a/src/test/validation_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -6,7 +6,7 @@
#include <net.h>
#include <validation.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <boost/signals2/signal.hpp>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 0ca3a17974..7b59d539a6 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -4,7 +4,7 @@
#include <chain.h>
#include <versionbits.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <chainparams.h>
#include <validation.h>
#include <consensus/params.h>
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 08f935c24f..3c967a04c0 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -17,6 +17,7 @@
#include <util/system.h>
#include <util/moneystr.h>
#include <util/time.h>
+#include <validationinterface.h>
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
int64_t _nTime, unsigned int _entryHeight,
@@ -403,7 +404,12 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
{
- NotifyEntryRemoved(it->GetSharedTx(), reason);
+ CTransactionRef ptx = it->GetSharedTx();
+ NotifyEntryRemoved(ptx, reason);
+ if (reason != MemPoolRemovalReason::BLOCK && reason != MemPoolRemovalReason::CONFLICT) {
+ GetMainSignals().TransactionRemovedFromMempool(ptx);
+ }
+
const uint256 hash = it->GetTx().GetHash();
for (const CTxIn& txin : it->GetTx().vin)
mapNextTx.erase(txin.prevout);
diff --git a/src/txmempool.h b/src/txmempool.h
index 9ccede9d4d..4a7640b78a 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -50,8 +50,6 @@ struct LockPoints
LockPoints() : height(0), time(0), maxInputBlock(nullptr) { }
};
-class CTxMemPool;
-
/** \class CTxMemPoolEntry
*
* CTxMemPoolEntry stores data about the corresponding transaction, as well
diff --git a/src/util/fees.cpp b/src/util/fees.cpp
index cf16d5e44f..41149888d7 100644
--- a/src/util/fees.cpp
+++ b/src/util/fees.cpp
@@ -3,8 +3,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <util/fees.h>
+
#include <policy/fees.h>
+#include <map>
#include <string>
std::string StringForFeeReason(FeeReason reason) {
diff --git a/src/util/settings.cpp b/src/util/settings.cpp
new file mode 100644
index 0000000000..af75fef310
--- /dev/null
+++ b/src/util/settings.cpp
@@ -0,0 +1,169 @@
+// 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 <util/settings.h>
+
+#include <univalue.h>
+
+namespace util {
+namespace {
+
+enum class Source {
+ FORCED,
+ COMMAND_LINE,
+ CONFIG_FILE_NETWORK_SECTION,
+ CONFIG_FILE_DEFAULT_SECTION
+};
+
+//! Merge settings from multiple sources in precedence order:
+//! Forced config > command line > config file network-specific section > config file default section
+//!
+//! This function is provided with a callback function fn that contains
+//! specific logic for how to merge the sources.
+template <typename Fn>
+static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn)
+{
+ // Merge in the forced settings
+ if (auto* value = FindKey(settings.forced_settings, name)) {
+ fn(SettingsSpan(*value), Source::FORCED);
+ }
+ // Merge in the command-line options
+ if (auto* values = FindKey(settings.command_line_options, name)) {
+ fn(SettingsSpan(*values), Source::COMMAND_LINE);
+ }
+ // Merge in the network-specific section of the config file
+ if (!section.empty()) {
+ if (auto* map = FindKey(settings.ro_config, section)) {
+ if (auto* values = FindKey(*map, name)) {
+ fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION);
+ }
+ }
+ }
+ // Merge in the default section of the config file
+ if (auto* map = FindKey(settings.ro_config, "")) {
+ if (auto* values = FindKey(*map, name)) {
+ fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION);
+ }
+ }
+}
+} // namespace
+
+SettingsValue GetSetting(const Settings& settings,
+ const std::string& section,
+ const std::string& name,
+ bool ignore_default_section_config,
+ bool get_chain_name)
+{
+ SettingsValue result;
+ MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
+ // Weird behavior preserved for backwards compatibility: Apply negated
+ // setting even if non-negated setting would be ignored. A negated
+ // value in the default section is applied to network specific options,
+ // even though normal non-negated values there would be ignored.
+ const bool never_ignore_negated_setting = span.last_negated();
+
+ // Weird behavior preserved for backwards compatibility: Take first
+ // assigned value instead of last. In general, later settings take
+ // precedence over early settings, but for backwards compatibility in
+ // the config file the precedence is reversed for all settings except
+ // chain name settings.
+ const bool reverse_precedence = (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && !get_chain_name;
+
+ // Weird behavior preserved for backwards compatibility: Negated
+ // -regtest and -testnet arguments which you would expect to override
+ // values set in the configuration file are currently accepted but
+ // silently ignored. It would be better to apply these just like other
+ // negated values, or at least warn they are ignored.
+ const bool skip_negated_command_line = get_chain_name;
+
+ // Ignore settings in default config section if requested.
+ if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION && !never_ignore_negated_setting) return;
+
+ // Skip negated command line settings.
+ if (skip_negated_command_line && span.last_negated()) return;
+
+ // Stick with highest priority value, keeping result if already set.
+ if (!result.isNull()) return;
+
+ if (!span.empty()) {
+ result = reverse_precedence ? span.begin()[0] : span.end()[-1];
+ } else if (span.last_negated()) {
+ result = false;
+ }
+ });
+ return result;
+}
+
+std::vector<SettingsValue> GetSettingsList(const Settings& settings,
+ const std::string& section,
+ const std::string& name,
+ bool ignore_default_section_config)
+{
+ std::vector<SettingsValue> result;
+ bool result_complete = false;
+ bool prev_negated_empty = false;
+ MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
+ // Weird behavior preserved for backwards compatibility: Apply config
+ // file settings even if negated on command line. Negating a setting on
+ // command line will ignore earlier settings on the command line and
+ // ignore settings in the config file, unless the negated command line
+ // value is followed by non-negated value, in which case config file
+ // settings will be brought back from the dead (but earlier command
+ // line settings will still be ignored).
+ const bool add_zombie_config_values = (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && !prev_negated_empty;
+
+ // Ignore settings in default config section if requested.
+ if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION) return;
+
+ // Add new settings to the result if isn't already complete, or if the
+ // values are zombies.
+ if (!result_complete || add_zombie_config_values) {
+ for (const auto& value : span) {
+ if (value.isArray()) {
+ result.insert(result.end(), value.getValues().begin(), value.getValues().end());
+ } else {
+ result.push_back(value);
+ }
+ }
+ }
+
+ // If a setting was negated, or if a setting was forced, set
+ // result_complete to true to ignore any later lower priority settings.
+ result_complete |= span.negated() > 0 || source == Source::FORCED;
+
+ // Update the negated and empty state used for the zombie values check.
+ prev_negated_empty |= span.last_negated() && result.empty();
+ });
+ return result;
+}
+
+bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name)
+{
+ bool has_default_section_setting = false;
+ bool has_other_setting = false;
+ MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {
+ if (span.empty()) return;
+ else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true;
+ else has_other_setting = true;
+ });
+ // If a value is set in the default section and not explicitly overwritten by the
+ // user on the command line or in a different section, then we want to enable
+ // warnings about the value being ignored.
+ return has_default_section_setting && !has_other_setting;
+}
+
+SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {}
+const SettingsValue* SettingsSpan::begin() const { return data + negated(); }
+const SettingsValue* SettingsSpan::end() const { return data + size; }
+bool SettingsSpan::empty() const { return size == 0 || last_negated(); }
+bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); }
+size_t SettingsSpan::negated() const
+{
+ for (size_t i = size; i > 0; --i) {
+ if (data[i - 1].isFalse()) return i; // Return number of negated values (position of last false value)
+ }
+ return 0;
+}
+
+} // namespace util
diff --git a/src/util/settings.h b/src/util/settings.h
new file mode 100644
index 0000000000..17832e4d2c
--- /dev/null
+++ b/src/util/settings.h
@@ -0,0 +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.
+
+#ifndef BITCOIN_UTIL_SETTINGS_H
+#define BITCOIN_UTIL_SETTINGS_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+class UniValue;
+
+namespace util {
+
+//! Settings value type (string/integer/boolean/null variant).
+//!
+//! @note UniValue is used here for convenience and because it can be easily
+//! serialized in a readable format. But any other variant type that can
+//! be assigned strings, int64_t, and bool values and has get_str(),
+//! get_int64(), get_bool(), isNum(), isBool(), isFalse(), isTrue() and
+//! isNull() methods can be substituted if there's a need to move away
+//! from UniValue. (An implementation with boost::variant was posted at
+//! https://github.com/bitcoin/bitcoin/pull/15934/files#r337691812)
+using SettingsValue = UniValue;
+
+//! Stored bitcoin settings. This struct combines settings from the command line
+//! and a read-only configuration file.
+struct Settings {
+ //! Map of setting name to forced setting value.
+ std::map<std::string, SettingsValue> forced_settings;
+ //! Map of setting name to list of command line values.
+ std::map<std::string, std::vector<SettingsValue>> command_line_options;
+ //! Map of config section name and setting name to list of config file values.
+ std::map<std::string, std::map<std::string, std::vector<SettingsValue>>> ro_config;
+};
+
+//! Get settings value from combined sources: forced settings, command line
+//! arguments and the read-only config file.
+//!
+//! @param ignore_default_section_config - ignore values in the default section
+//! of the config file (part before any
+//! [section] keywords)
+//! @param get_chain_name - enable special backwards compatible behavior
+//! for GetChainName
+SettingsValue GetSetting(const Settings& settings, const std::string& section, const std::string& name, bool ignore_default_section_config, bool get_chain_name);
+
+//! Get combined setting value similar to GetSetting(), except if setting was
+//! specified multiple times, return a list of all the values specified.
+std::vector<SettingsValue> GetSettingsList(const Settings& settings, const std::string& section, const std::string& name, bool ignore_default_section_config);
+
+//! Return true if a setting is set in the default config file section, and not
+//! overridden by a higher priority command-line or network section value.
+//!
+//! This is used to provide user warnings about values that might be getting
+//! ignored unintentionally.
+bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name);
+
+//! Accessor for list of settings that skips negated values when iterated over.
+//! The last boolean `false` value in the list and all earlier values are
+//! considered negated.
+struct SettingsSpan {
+ explicit SettingsSpan() = default;
+ explicit SettingsSpan(const SettingsValue& value) noexcept : SettingsSpan(&value, 1) {}
+ explicit SettingsSpan(const SettingsValue* data, size_t size) noexcept : data(data), size(size) {}
+ explicit SettingsSpan(const std::vector<SettingsValue>& vec) noexcept;
+ const SettingsValue* begin() const; //<! Pointer to first non-negated value.
+ const SettingsValue* end() const; //<! Pointer to end of values.
+ bool empty() const; //<! True if there are any non-negated values.
+ bool last_negated() const; //<! True if the last value is negated.
+ size_t negated() const; //<! Number of negated values.
+
+ const SettingsValue* data = nullptr;
+ size_t size = 0;
+};
+
+//! Map lookup helper.
+template <typename Map, typename Key>
+auto FindKey(Map&& map, Key&& key) -> decltype(&map.at(key))
+{
+ auto it = map.find(key);
+ return it == map.end() ? nullptr : &it->second;
+}
+
+} // namespace util
+
+#endif // BITCOIN_UTIL_SETTINGS_H
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 7da408eda5..2a2ae6fdf5 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -63,6 +63,7 @@
#endif
#include <thread>
+#include <univalue.h>
// Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime();
@@ -161,11 +162,14 @@ static bool InterpretBool(const std::string& strValue)
return (atoi(strValue) != 0);
}
+static std::string SettingName(const std::string& arg)
+{
+ return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg;
+}
+
/** Internal helper functions for ArgsManager */
class ArgsManagerHelper {
public:
- typedef std::map<std::string, std::vector<std::string>> MapArgs;
-
/** Determine whether to use config settings in the default section,
* See also comments around ArgsManager::ArgsManager() below. */
static inline bool UseDefaultSection(const ArgsManager& am, const std::string& arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args)
@@ -173,91 +177,10 @@ public:
return (am.m_network == CBaseChainParams::MAIN || am.m_network_only_args.count(arg) == 0);
}
- /** Convert regular argument into the network-specific setting */
- static inline std::string NetworkArg(const ArgsManager& am, const std::string& arg)
- {
- assert(arg.length() > 1 && arg[0] == '-');
- return "-" + am.m_network + "." + arg.substr(1);
- }
-
- /** Find arguments in a map and add them to a vector */
- static inline void AddArgs(std::vector<std::string>& res, const MapArgs& map_args, const std::string& arg)
- {
- auto it = map_args.find(arg);
- if (it != map_args.end()) {
- res.insert(res.end(), it->second.begin(), it->second.end());
- }
- }
-
- /** Return true/false if an argument is set in a map, and also
- * return the first (or last) of the possibly multiple values it has
- */
- static inline std::pair<bool,std::string> GetArgHelper(const MapArgs& map_args, const std::string& arg, bool getLast = false)
- {
- auto it = map_args.find(arg);
-
- if (it == map_args.end() || it->second.empty()) {
- return std::make_pair(false, std::string());
- }
-
- if (getLast) {
- return std::make_pair(true, it->second.back());
- } else {
- return std::make_pair(true, it->second.front());
- }
- }
-
- /* Get the string value of an argument, returning a pair of a boolean
- * indicating the argument was found, and the value for the argument
- * if it was found (or the empty string if not found).
- */
- static inline std::pair<bool,std::string> GetArg(const ArgsManager &am, const std::string& arg)
+ static util::SettingsValue Get(const ArgsManager& am, const std::string& arg)
{
LOCK(am.cs_args);
- std::pair<bool,std::string> found_result(false, std::string());
-
- // We pass "true" to GetArgHelper in order to return the last
- // argument value seen from the command line (so "bitcoind -foo=bar
- // -foo=baz" gives GetArg(am,"foo")=={true,"baz"}
- found_result = GetArgHelper(am.m_override_args, arg, true);
- if (found_result.first) {
- return found_result;
- }
-
- // But in contrast we return the first argument seen in a config file,
- // so "foo=bar \n foo=baz" in the config file gives
- // GetArg(am,"foo")={true,"bar"}
- if (!am.m_network.empty()) {
- found_result = GetArgHelper(am.m_config_args, NetworkArg(am, arg));
- if (found_result.first) {
- return found_result;
- }
- }
-
- if (UseDefaultSection(am, arg)) {
- found_result = GetArgHelper(am.m_config_args, arg);
- if (found_result.first) {
- return found_result;
- }
- }
-
- return found_result;
- }
-
- /* Special test for -testnet and -regtest args, because we
- * don't want to be confused by craziness like "[regtest] testnet=1"
- */
- static inline bool GetNetBoolArg(const ArgsManager &am, const std::string& net_arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args)
- {
- std::pair<bool,std::string> found_result(false,std::string());
- found_result = GetArgHelper(am.m_override_args, net_arg, true);
- if (!found_result.first) {
- found_result = GetArgHelper(am.m_config_args, net_arg, true);
- if (!found_result.first) {
- return false; // not set
- }
- }
- return InterpretBool(found_result.second); // is set, so evaluate
+ return GetSetting(am.m_settings, am.m_network, SettingName(arg), !UseDefaultSection(am, arg), /* get_chain_name= */ false);
}
};
@@ -268,13 +191,12 @@ public:
* checks whether there was a double-negative (-nofoo=0 -> -foo=1).
*
* If there was not a double negative, it removes the "no" from the key
- * and clears the args vector to indicate a negated option.
+ * and returns false.
*
- * If there was a double negative, it removes "no" from the key, sets the
- * value to "1" and pushes the key and the updated value to the args vector.
+ * If there was a double negative, it removes "no" from the key, and
+ * returns true.
*
- * If there was no "no", it leaves key and value untouched and pushes them
- * to the args vector.
+ * If there was no "no", it returns the string value untouched.
*
* Where an option was negated can be later checked using the
* IsArgNegated() method. One use case for this is to have a way to disable
@@ -282,34 +204,39 @@ public:
* that debug log output is not sent to any file at all).
*/
-NODISCARD static bool InterpretOption(std::string key, std::string val, unsigned int flags,
- std::map<std::string, std::vector<std::string>>& args,
- std::string& error)
+static util::SettingsValue InterpretOption(std::string& section, std::string& key, const std::string& value)
{
- assert(key[0] == '-');
-
+ // Split section name from key name for keys like "testnet.foo" or "regtest.bar"
size_t option_index = key.find('.');
- if (option_index == std::string::npos) {
- option_index = 1;
- } else {
- ++option_index;
+ if (option_index != std::string::npos) {
+ section = key.substr(0, option_index);
+ key.erase(0, option_index + 1);
}
- if (key.substr(option_index, 2) == "no") {
- key.erase(option_index, 2);
- if (flags & ArgsManager::ALLOW_BOOL) {
- if (InterpretBool(val)) {
- args[key].clear();
- return true;
- }
- // Double negatives like -nofoo=0 are supported (but discouraged)
- LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val);
- val = "1";
- } else {
- error = strprintf("Negating of %s is meaningless and therefore forbidden", key);
- return false;
+ if (key.substr(0, 2) == "no") {
+ key.erase(0, 2);
+ // Double negatives like -nofoo=0 are supported (but discouraged)
+ if (!InterpretBool(value)) {
+ LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key, value);
+ return true;
}
+ return false;
+ }
+ return value;
+}
+
+/**
+ * Check settings value validity according to flags.
+ *
+ * TODO: Add more meaningful error checks here in the future
+ * See "here's how the flags are meant to behave" in
+ * https://github.com/bitcoin/bitcoin/pull/16097#issuecomment-514627823
+ */
+static bool CheckValid(const std::string& key, const util::SettingsValue& val, unsigned int flags, std::string& error)
+{
+ if (val.isBool() && !(flags & ArgsManager::ALLOW_BOOL)) {
+ error = strprintf("Negating of -%s is meaningless and therefore forbidden", key);
+ return false;
}
- args[key].push_back(val);
return true;
}
@@ -331,22 +258,9 @@ const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {};
for (const auto& arg : m_network_only_args) {
- std::pair<bool, std::string> found_result;
-
- // if this option is overridden it's fine
- found_result = ArgsManagerHelper::GetArgHelper(m_override_args, arg);
- if (found_result.first) continue;
-
- // if there's a network-specific value for this option, it's fine
- found_result = ArgsManagerHelper::GetArgHelper(m_config_args, ArgsManagerHelper::NetworkArg(*this, arg));
- if (found_result.first) continue;
-
- // if there isn't a default value for this option, it's fine
- found_result = ArgsManagerHelper::GetArgHelper(m_config_args, arg);
- if (!found_result.first) continue;
-
- // otherwise, issue a warning
- unsuitables.insert(arg);
+ if (OnlyHasDefaultSectionSetting(m_settings, m_network, SettingName(arg))) {
+ unsuitables.insert(arg);
+ }
}
return unsuitables;
}
@@ -375,7 +289,7 @@ void ArgsManager::SelectConfigNetwork(const std::string& network)
bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
{
LOCK(cs_args);
- m_override_args.clear();
+ m_settings.command_line_options.clear();
for (int i = 1; i < argc; i++) {
std::string key(argv[i]);
@@ -408,49 +322,44 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
if (key.length() > 1 && key[1] == '-')
key.erase(0, 1);
+ // Transform -foo to foo
+ key.erase(0, 1);
+ std::string section;
+ util::SettingsValue value = InterpretOption(section, key, val);
const unsigned int flags = FlagsOfKnownArg(key);
if (flags) {
- if (!InterpretOption(key, val, flags, m_override_args, error)) {
+ if (!CheckValid(key, value, flags, error)) {
return false;
}
+ // Weird behavior preserved for backwards compatibility: command
+ // line options with section prefixes are allowed but ignored. It
+ // would be better if these options triggered the Invalid parameter
+ // error below.
+ if (section.empty()) {
+ m_settings.command_line_options[key].push_back(value);
+ }
} else {
- error = strprintf("Invalid parameter %s", key);
+ error = strprintf("Invalid parameter -%s", key);
return false;
}
}
// we do not allow -includeconf from command line, so we clear it here
- auto it = m_override_args.find("-includeconf");
- if (it != m_override_args.end()) {
- if (it->second.size() > 0) {
- for (const auto& ic : it->second) {
- error += "-includeconf cannot be used from commandline; -includeconf=" + ic + "\n";
- }
- return false;
+ bool success = true;
+ if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
+ for (const auto& include : util::SettingsSpan(*includes)) {
+ error += "-includeconf cannot be used from commandline; -includeconf=" + include.get_str() + "\n";
+ success = false;
}
}
- return true;
+ return success;
}
unsigned int ArgsManager::FlagsOfKnownArg(const std::string& key) const
{
- assert(key[0] == '-');
-
- size_t option_index = key.find('.');
- if (option_index == std::string::npos) {
- option_index = 1;
- } else {
- ++option_index;
- }
- if (key.substr(option_index, 2) == "no") {
- option_index += 2;
- }
-
- const std::string base_arg_name = '-' + key.substr(option_index);
-
LOCK(cs_args);
for (const auto& arg_map : m_available_args) {
- const auto search = arg_map.second.find(base_arg_name);
+ const auto search = arg_map.second.find('-' + key);
if (search != arg_map.second.end()) {
return search->second.m_flags;
}
@@ -460,69 +369,42 @@ unsigned int ArgsManager::FlagsOfKnownArg(const std::string& key) const
std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
{
- std::vector<std::string> result = {};
- if (IsArgNegated(strArg)) return result; // special case
-
LOCK(cs_args);
-
- ArgsManagerHelper::AddArgs(result, m_override_args, strArg);
- if (!m_network.empty()) {
- ArgsManagerHelper::AddArgs(result, m_config_args, ArgsManagerHelper::NetworkArg(*this, strArg));
+ bool ignore_default_section_config = !ArgsManagerHelper::UseDefaultSection(*this, strArg);
+ std::vector<std::string> result;
+ for (const util::SettingsValue& value :
+ util::GetSettingsList(m_settings, m_network, SettingName(strArg), ignore_default_section_config)) {
+ result.push_back(value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str());
}
-
- if (ArgsManagerHelper::UseDefaultSection(*this, strArg)) {
- ArgsManagerHelper::AddArgs(result, m_config_args, strArg);
- }
-
return result;
}
bool ArgsManager::IsArgSet(const std::string& strArg) const
{
- if (IsArgNegated(strArg)) return true; // special case
- return ArgsManagerHelper::GetArg(*this, strArg).first;
+ return !ArgsManagerHelper::Get(*this, strArg).isNull();
}
bool ArgsManager::IsArgNegated(const std::string& strArg) const
{
- LOCK(cs_args);
-
- const auto& ov = m_override_args.find(strArg);
- if (ov != m_override_args.end()) return ov->second.empty();
-
- if (!m_network.empty()) {
- const auto& cfs = m_config_args.find(ArgsManagerHelper::NetworkArg(*this, strArg));
- if (cfs != m_config_args.end()) return cfs->second.empty();
- }
-
- const auto& cf = m_config_args.find(strArg);
- if (cf != m_config_args.end()) return cf->second.empty();
-
- return false;
+ return ArgsManagerHelper::Get(*this, strArg).isFalse();
}
std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
{
- if (IsArgNegated(strArg)) return "0";
- std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg);
- if (found_res.first) return found_res.second;
- return strDefault;
+ const util::SettingsValue value = ArgsManagerHelper::Get(*this, strArg);
+ return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str();
}
int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const
{
- if (IsArgNegated(strArg)) return 0;
- std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg);
- if (found_res.first) return atoi64(found_res.second);
- return nDefault;
+ const util::SettingsValue value = ArgsManagerHelper::Get(*this, strArg);
+ return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.get_int64() : atoi64(value.get_str());
}
bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
{
- if (IsArgNegated(strArg)) return false;
- std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg);
- if (found_res.first) return InterpretBool(found_res.second);
- return fDefault;
+ const util::SettingsValue value = ArgsManagerHelper::Get(*this, strArg);
+ return value.isNull() ? fDefault : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
}
bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
@@ -544,7 +426,7 @@ bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
{
LOCK(cs_args);
- m_override_args[strArg] = {strValue};
+ m_settings.forced_settings[SettingName(strArg)] = strValue;
}
void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
@@ -860,12 +742,15 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
return false;
}
for (const std::pair<std::string, std::string>& option : options) {
- const std::string strKey = std::string("-") + option.first;
- const unsigned int flags = FlagsOfKnownArg(strKey);
+ std::string section;
+ std::string key = option.first;
+ util::SettingsValue value = InterpretOption(section, key, option.second);
+ const unsigned int flags = FlagsOfKnownArg(key);
if (flags) {
- if (!InterpretOption(strKey, option.second, flags, m_config_args, error)) {
+ if (!CheckValid(key, value, flags, error)) {
return false;
}
+ m_settings.ro_config[section][key].push_back(value);
} else {
if (ignore_invalid_keys) {
LogPrintf("Ignoring unknown configuration value %s\n", option.first);
@@ -882,7 +767,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
{
{
LOCK(cs_args);
- m_config_args.clear();
+ m_settings.ro_config.clear();
m_config_sections.clear();
}
@@ -894,58 +779,64 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
if (!ReadConfigStream(stream, confPath, error, ignore_invalid_keys)) {
return false;
}
- // if there is an -includeconf in the override args, but it is empty, that means the user
- // passed '-noincludeconf' on the command line, in which case we should not include anything
- bool emptyIncludeConf;
+ // `-includeconf` cannot be included in the command line arguments except
+ // as `-noincludeconf` (which indicates that no conf file should be used).
+ bool use_conf_file{true};
{
LOCK(cs_args);
- emptyIncludeConf = m_override_args.count("-includeconf") == 0;
+ if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
+ // ParseParameters() fails if a non-negated -includeconf is passed on the command-line
+ assert(util::SettingsSpan(*includes).last_negated());
+ use_conf_file = false;
+ }
}
- if (emptyIncludeConf) {
+ if (use_conf_file) {
std::string chain_id = GetChainName();
- std::vector<std::string> includeconf(GetArgs("-includeconf"));
- {
- // We haven't set m_network yet (that happens in SelectParams()), so manually check
- // for network.includeconf args.
- std::vector<std::string> includeconf_net(GetArgs(std::string("-") + chain_id + ".includeconf"));
- includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end());
- }
+ std::vector<std::string> conf_file_names;
- // Remove -includeconf from configuration, so we can warn about recursion
- // later
- {
+ auto add_includes = [&](const std::string& network, size_t skip = 0) {
+ size_t num_values = 0;
LOCK(cs_args);
- m_config_args.erase("-includeconf");
- m_config_args.erase(std::string("-") + chain_id + ".includeconf");
- }
-
- for (const std::string& to_include : includeconf) {
- fsbridge::ifstream include_config(GetConfigFile(to_include));
- if (include_config.good()) {
- if (!ReadConfigStream(include_config, to_include, error, ignore_invalid_keys)) {
+ if (auto* section = util::FindKey(m_settings.ro_config, network)) {
+ if (auto* values = util::FindKey(*section, "includeconf")) {
+ for (size_t i = std::max(skip, util::SettingsSpan(*values).negated()); i < values->size(); ++i) {
+ conf_file_names.push_back((*values)[i].get_str());
+ }
+ num_values = values->size();
+ }
+ }
+ return num_values;
+ };
+
+ // We haven't set m_network yet (that happens in SelectParams()), so manually check
+ // for network.includeconf args.
+ const size_t chain_includes = add_includes(chain_id);
+ const size_t default_includes = add_includes({});
+
+ for (const std::string& conf_file_name : conf_file_names) {
+ fsbridge::ifstream conf_file_stream(GetConfigFile(conf_file_name));
+ if (conf_file_stream.good()) {
+ if (!ReadConfigStream(conf_file_stream, conf_file_name, error, ignore_invalid_keys)) {
return false;
}
- LogPrintf("Included configuration file %s\n", to_include);
+ LogPrintf("Included configuration file %s\n", conf_file_name);
} else {
- error = "Failed to include configuration file " + to_include;
+ error = "Failed to include configuration file " + conf_file_name;
return false;
}
}
// Warn about recursive -includeconf
- includeconf = GetArgs("-includeconf");
- {
- std::vector<std::string> includeconf_net(GetArgs(std::string("-") + chain_id + ".includeconf"));
- includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end());
- std::string chain_id_final = GetChainName();
- if (chain_id_final != chain_id) {
- // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
- includeconf_net = GetArgs(std::string("-") + chain_id_final + ".includeconf");
- includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end());
- }
+ conf_file_names.clear();
+ add_includes(chain_id, /* skip= */ chain_includes);
+ add_includes({}, /* skip= */ default_includes);
+ std::string chain_id_final = GetChainName();
+ if (chain_id_final != chain_id) {
+ // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
+ add_includes(chain_id_final);
}
- for (const std::string& to_include : includeconf) {
- tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include);
+ for (const std::string& conf_file_name : conf_file_names) {
+ tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", conf_file_name);
}
}
}
@@ -961,9 +852,16 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
std::string ArgsManager::GetChainName() const
{
- LOCK(cs_args);
- const bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest");
- const bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet");
+ auto get_net = [&](const std::string& arg) {
+ LOCK(cs_args);
+ util::SettingsValue value = GetSetting(m_settings, /* section= */ "", SettingName(arg),
+ /* ignore_default_section_config= */ false,
+ /* get_chain_name= */ true);
+ return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
+ };
+
+ const bool fRegTest = get_net("-regtest");
+ const bool fTestNet = get_net("-testnet");
const bool is_chain_arg_set = IsArgSet("-chain");
if ((int)is_chain_arg_set + (int)fRegTest + (int)fTestNet > 1) {
diff --git a/src/util/system.h b/src/util/system.h
index 7452f186e6..e0b6371dc9 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -22,6 +22,7 @@
#include <sync.h>
#include <tinyformat.h>
#include <util/memory.h>
+#include <util/settings.h>
#include <util/threadnames.h>
#include <util/time.h>
@@ -157,8 +158,7 @@ protected:
};
mutable CCriticalSection cs_args;
- std::map<std::string, std::vector<std::string>> m_override_args GUARDED_BY(cs_args);
- std::map<std::string, std::vector<std::string>> m_config_args GUARDED_BY(cs_args);
+ util::Settings m_settings GUARDED_BY(cs_args);
std::string m_network GUARDED_BY(cs_args);
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
diff --git a/src/validation.cpp b/src/validation.cpp
index 11072b6038..8f5d333170 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -18,6 +18,8 @@
#include <flatfile.h>
#include <hash.h>
#include <index/txindex.h>
+#include <logging.h>
+#include <logging/timer.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/settings.h>
@@ -107,7 +109,7 @@ CBlockIndex *pindexBestHeader = nullptr;
Mutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
uint256 g_best_block;
-int nScriptCheckThreads = 0;
+bool g_parallel_script_checks{false};
std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false);
bool fHavePruned = false;
@@ -127,9 +129,6 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CBlockPolicyEstimator feeEstimator;
CTxMemPool mempool(&feeEstimator);
-/** Constant stuff for coinbase transactions we create: */
-CScript COINBASE_FLAGS;
-
// Internal stuff
namespace {
CBlockIndex* pindexBestInvalid = nullptr;
@@ -2069,7 +2068,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
CBlockUndo blockundo;
- CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : nullptr);
+ CCheckQueueControl<CScriptCheck> control(fScriptChecks && g_parallel_script_checks ? &scriptcheckqueue : nullptr);
std::vector<int> prevheights;
CAmount nFees = 0;
@@ -2130,7 +2129,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
TxValidationState tx_state;
- if (fScriptChecks && !CheckInputs(tx, tx_state, view, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) {
+ if (fScriptChecks && !CheckInputs(tx, tx_state, view, flags, fCacheResults, fCacheResults, txdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
// Any transaction validation failure in ConnectBlock is a block consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
@@ -2199,6 +2198,10 @@ bool CChainState::FlushStateToDisk(
static int64_t nLastFlush = 0;
std::set<int> setFilesToPrune;
bool full_flush_completed = false;
+
+ const size_t coins_count = CoinsTip().GetCacheSize();
+ const size_t coins_mem_usage = CoinsTip().DynamicMemoryUsage();
+
try {
{
bool fFlushForPrune = false;
@@ -2206,8 +2209,12 @@ bool CChainState::FlushStateToDisk(
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
if (nManualPruneHeight > 0) {
+ LOG_TIME_MILLIS("find files to prune (manual)", BCLog::BENCH);
+
FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight);
} else {
+ LOG_TIME_MILLIS("find files to prune", BCLog::BENCH);
+
FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight());
fCheckForPruning = false;
}
@@ -2246,10 +2253,17 @@ bool CChainState::FlushStateToDisk(
if (!CheckDiskSpace(GetBlocksDir())) {
return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
}
- // First make sure all block and undo data is flushed to disk.
- FlushBlockFile();
+ {
+ LOG_TIME_MILLIS("write block and undo data to disk", BCLog::BENCH);
+
+ // First make sure all block and undo data is flushed to disk.
+ FlushBlockFile();
+ }
+
// Then update all block file information (which may refer to block and undo files).
{
+ LOG_TIME_MILLIS("write block index to disk", BCLog::BENCH);
+
std::vector<std::pair<int, const CBlockFileInfo*> > vFiles;
vFiles.reserve(setDirtyFileInfo.size());
for (std::set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) {
@@ -2267,12 +2281,18 @@ bool CChainState::FlushStateToDisk(
}
}
// Finally remove any pruned files
- if (fFlushForPrune)
+ if (fFlushForPrune) {
+ LOG_TIME_MILLIS("unlink pruned files", BCLog::BENCH);
+
UnlinkPrunedFiles(setFilesToPrune);
+ }
nLastWrite = nNow;
}
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
if (fDoFullFlush && !CoinsTip().GetBestBlock().IsNull()) {
+ LOG_TIME_SECONDS(strprintf("write coins cache to disk (%d coins, %.2fkB)",
+ coins_count, coins_mem_usage / 1000));
+
// Typical Coin structures on disk are around 48 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is already
@@ -2435,7 +2455,7 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams&
UpdateTip(pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
- GetMainSignals().BlockDisconnected(pblock);
+ GetMainSignals().BlockDisconnected(pblock, pindexDelete);
return true;
}
@@ -5066,7 +5086,7 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
fTxTotal = pindex->nChainTx + (nNow - pindex->GetBlockTime()) * data.dTxRate;
}
- return pindex->nChainTx / fTxTotal;
+ return std::min<double>(pindex->nChainTx / fTxTotal, 1.0);
}
class CMainCleanup
diff --git a/src/validation.h b/src/validation.h
index 7f9582adfd..d87113c41f 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -21,6 +21,7 @@
#include <txmempool.h> // For CTxMemPool::cs
#include <txdb.h>
#include <versionbits.h>
+#include <serialize.h>
#include <algorithm>
#include <atomic>
@@ -76,8 +77,8 @@ static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
-/** Maximum number of script-checking threads allowed */
-static const int MAX_SCRIPTCHECK_THREADS = 16;
+/** Maximum number of dedicated script-checking threads allowed */
+static const int MAX_SCRIPTCHECK_THREADS = 15;
/** -par default (number of script-checking threads, 0 = auto) */
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
/** Number of blocks that can be requested at any given time from a single peer. */
@@ -136,7 +137,6 @@ struct BlockHasher
size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
};
-extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
@@ -146,7 +146,10 @@ extern std::condition_variable g_best_block_cv;
extern uint256 g_best_block;
extern std::atomic_bool fImporting;
extern std::atomic_bool fReindex;
-extern int nScriptCheckThreads;
+/** Whether there are dedicated script-checking threads running.
+ * False indicates all script checking is done on the main threadMessageHandler thread.
+ */
+extern bool g_parallel_script_checks;
extern bool fRequireStandard;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index a46b4003f1..6c0f4d5edd 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -7,9 +7,9 @@
#include <primitives/block.h>
#include <scheduler.h>
-#include <txmempool.h>
#include <future>
+#include <unordered_map>
#include <utility>
#include <boost/signals2/signal.hpp>
@@ -29,7 +29,7 @@ struct MainSignalsInstance {
boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected;
- boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
+ boost::signals2::signal<void (const std::shared_ptr<const CBlock>&, const CBlockIndex* pindex)> BlockDisconnected;
boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
boost::signals2::signal<void (const CBlock&, const BlockValidationState&)> BlockChecked;
@@ -46,11 +46,6 @@ struct MainSignalsInstance {
static CMainSignals g_signals;
-// This map has to a separate global instead of a member of MainSignalsInstance,
-// because RegisterWithMempoolSignals is currently called before RegisterBackgroundSignalScheduler,
-// so MainSignalsInstance hasn't been created yet.
-static std::unordered_map<CTxMemPool*, boost::signals2::scoped_connection> g_connNotifyEntryRemoved;
-
void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) {
assert(!m_internals);
m_internals.reset(new MainSignalsInstance(&scheduler));
@@ -71,17 +66,6 @@ size_t CMainSignals::CallbacksPending() {
return m_internals->m_schedulerClient.CallbacksPending();
}
-void CMainSignals::RegisterWithMempoolSignals(CTxMemPool& pool) {
- g_connNotifyEntryRemoved.emplace(std::piecewise_construct,
- std::forward_as_tuple(&pool),
- std::forward_as_tuple(pool.NotifyEntryRemoved.connect(std::bind(&CMainSignals::MempoolEntryRemoved, this, std::placeholders::_1, std::placeholders::_2)))
- );
-}
-
-void CMainSignals::UnregisterWithMempoolSignals(CTxMemPool& pool) {
- g_connNotifyEntryRemoved.erase(&pool);
-}
-
CMainSignals& GetMainSignals()
{
return g_signals;
@@ -92,7 +76,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
conns.UpdatedBlockTip = g_signals.m_internals->UpdatedBlockTip.connect(std::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
conns.TransactionAddedToMempool = g_signals.m_internals->TransactionAddedToMempool.connect(std::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, std::placeholders::_1));
conns.BlockConnected = g_signals.m_internals->BlockConnected.connect(std::bind(&CValidationInterface::BlockConnected, pwalletIn, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
- conns.BlockDisconnected = g_signals.m_internals->BlockDisconnected.connect(std::bind(&CValidationInterface::BlockDisconnected, pwalletIn, std::placeholders::_1));
+ conns.BlockDisconnected = g_signals.m_internals->BlockDisconnected.connect(std::bind(&CValidationInterface::BlockDisconnected, pwalletIn, std::placeholders::_1, std::placeholders::_2));
conns.TransactionRemovedFromMempool = g_signals.m_internals->TransactionRemovedFromMempool.connect(std::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, std::placeholders::_1));
conns.ChainStateFlushed = g_signals.m_internals->ChainStateFlushed.connect(std::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, std::placeholders::_1));
conns.BlockChecked = g_signals.m_internals->BlockChecked.connect(std::bind(&CValidationInterface::BlockChecked, pwalletIn, std::placeholders::_1, std::placeholders::_2));
@@ -126,13 +110,6 @@ void SyncWithValidationInterfaceQueue() {
promise.get_future().wait();
}
-void CMainSignals::MempoolEntryRemoved(CTransactionRef ptx, MemPoolRemovalReason reason) {
- if (reason != MemPoolRemovalReason::BLOCK && reason != MemPoolRemovalReason::CONFLICT) {
- m_internals->m_schedulerClient.AddToProcessQueue([ptx, this] {
- m_internals->TransactionRemovedFromMempool(ptx);
- });
- }
-}
void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
// Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which
@@ -150,15 +127,22 @@ void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
});
}
+void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
+ m_internals->m_schedulerClient.AddToProcessQueue([ptx, this] {
+ m_internals->TransactionRemovedFromMempool(ptx);
+ });
+}
+
void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>>& pvtxConflicted) {
m_internals->m_schedulerClient.AddToProcessQueue([pblock, pindex, pvtxConflicted, this] {
m_internals->BlockConnected(pblock, pindex, *pvtxConflicted);
});
}
-void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) {
- m_internals->m_schedulerClient.AddToProcessQueue([pblock, this] {
- m_internals->BlockDisconnected(pblock);
+void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
+{
+ m_internals->m_schedulerClient.AddToProcessQueue([pblock, pindex, this] {
+ m_internals->BlockDisconnected(pblock, pindex);
});
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index dc8425869b..63aad8661f 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -21,8 +21,6 @@ class CConnman;
class CValidationInterface;
class uint256;
class CScheduler;
-class CTxMemPool;
-enum class MemPoolRemovalReason;
// These functions dispatch to one or all registered wallets
@@ -114,7 +112,7 @@ protected:
*
* Called on a background thread.
*/
- virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {}
+ virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) {}
/**
* Notifies listeners of the new active block chain on-disk.
*
@@ -158,8 +156,6 @@ private:
friend void ::UnregisterAllValidationInterfaces();
friend void ::CallFunctionInValidationInterfaceQueue(std::function<void ()> func);
- void MempoolEntryRemoved(CTransactionRef tx, MemPoolRemovalReason reason);
-
public:
/** Register a CScheduler to give callbacks which should run in the background (may only be called once) */
void RegisterBackgroundSignalScheduler(CScheduler& scheduler);
@@ -170,15 +166,12 @@ public:
size_t CallbacksPending();
- /** Register with mempool to call TransactionRemovedFromMempool callbacks */
- void RegisterWithMempoolSignals(CTxMemPool& pool);
- /** Unregister with mempool */
- void UnregisterWithMempoolSignals(CTxMemPool& pool);
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
void TransactionAddedToMempool(const CTransactionRef &);
+ void TransactionRemovedFromMempool(const CTransactionRef &);
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &);
- void BlockDisconnected(const std::shared_ptr<const CBlock> &);
+ void BlockDisconnected(const std::shared_ptr<const CBlock> &, const CBlockIndex* pindex);
void ChainStateFlushed(const CBlockLocator &);
void BlockChecked(const CBlock&, const BlockValidationState&);
void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&);
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index e48eee6c2c..79825847c8 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -166,10 +166,9 @@ BerkeleyEnvironment::~BerkeleyEnvironment()
bool BerkeleyEnvironment::Open(bool retry)
{
- if (fDbEnvInit)
+ if (fDbEnvInit) {
return true;
-
- boost::this_thread::interruption_point();
+ }
fs::path pathIn = strPath;
TryCreateDirectories(pathIn);
@@ -238,14 +237,12 @@ bool BerkeleyEnvironment::Open(bool retry)
return true;
}
-//! Construct an in-memory mock Berkeley environment for testing and as a place-holder for g_dbenvs emplace
+//! Construct an in-memory mock Berkeley environment for testing
BerkeleyEnvironment::BerkeleyEnvironment()
{
Reset();
- boost::this_thread::interruption_point();
-
- LogPrint(BCLog::DB, "BerkeleyEnvironment::MakeMock\n");
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
dbenv->set_cachesize(1, 0, 1);
dbenv->set_lg_bsize(10485760 * 4);
@@ -263,8 +260,9 @@ BerkeleyEnvironment::BerkeleyEnvironment()
DB_THREAD |
DB_PRIVATE,
S_IRUSR | S_IWUSR);
- if (ret > 0)
+ if (ret > 0) {
throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
+ }
fDbEnvInit = true;
fMockDb = true;
@@ -767,7 +765,7 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
{
int64_t nStart = GetTimeMillis();
// Flush log data to the actual data file on all files that are not in use
- LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
if (!fDbEnvInit)
return;
{
@@ -776,21 +774,21 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
while (mi != mapFileUseCount.end()) {
std::string strFile = (*mi).first;
int nRefCount = (*mi).second;
- LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
if (nRefCount == 0) {
// Move log data to the dat file
CloseDb(strFile);
- LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
dbenv->txn_checkpoint(0, 0, 0);
- LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
if (!fMockDb)
dbenv->lsn_reset(strFile.c_str(), 0);
- LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
mapFileUseCount.erase(mi++);
} else
mi++;
}
- LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
+ LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
if (fShutdown) {
char** listp;
if (mapFileUseCount.empty()) {
@@ -830,7 +828,7 @@ bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase& database)
std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
if (mi != env->mapFileUseCount.end())
{
- LogPrint(BCLog::DB, "Flushing %s\n", strFile);
+ LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
int64_t nStart = GetTimeMillis();
// Flush wallet file so it's self contained
@@ -838,7 +836,7 @@ bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase& database)
env->CheckpointLSN(strFile);
env->mapFileUseCount.erase(mi++);
- LogPrint(BCLog::DB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
+ LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
ret = true;
}
}
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 0a4bb3f396..8f0b495ac4 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -16,7 +16,7 @@
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
-static feebumper::Result PreconditionChecks(interfaces::Chain::Lock& locked_chain, const CWallet& wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
+static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
if (wallet.HasWalletSpend(wtx.GetHash())) {
errors.push_back("Transaction has descendants in the wallet");
@@ -30,7 +30,7 @@ static feebumper::Result PreconditionChecks(interfaces::Chain::Lock& locked_chai
}
}
- if (wtx.GetDepthInMainChain(locked_chain) != 0) {
+ if (wtx.GetDepthInMainChain() != 0) {
errors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
return feebumper::Result::WALLET_ERROR;
}
@@ -146,7 +146,7 @@ bool TransactionCanBeBumped(const CWallet& wallet, const uint256& txid)
if (wtx == nullptr) return false;
std::vector<std::string> errors_dummy;
- feebumper::Result res = PreconditionChecks(*locked_chain, wallet, *wtx, errors_dummy);
+ feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy);
return res == feebumper::Result::OK;
}
@@ -165,7 +165,7 @@ Result CreateTotalBumpTransaction(const CWallet* wallet, const uint256& txid, co
}
const CWalletTx& wtx = it->second;
- Result result = PreconditionChecks(*locked_chain, *wallet, wtx, errors);
+ Result result = PreconditionChecks(*wallet, wtx, errors);
if (result != Result::OK) {
return result;
}
@@ -291,7 +291,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
}
const CWalletTx& wtx = it->second;
- Result result = PreconditionChecks(*locked_chain, wallet, wtx, errors);
+ Result result = PreconditionChecks(wallet, wtx, errors);
if (result != Result::OK) {
return result;
}
@@ -382,7 +382,7 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti
CWalletTx& oldWtx = it->second;
// make sure the transaction still has no descendants and hasn't been mined in the meantime
- Result result = PreconditionChecks(*locked_chain, wallet, oldWtx, errors);
+ Result result = PreconditionChecks(wallet, oldWtx, errors);
if (result != Result::OK) {
return result;
}
diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp
index aa13cacca4..96c1ad8d3f 100644
--- a/src/wallet/psbtwallet.cpp
+++ b/src/wallet/psbtwallet.cpp
@@ -39,12 +39,35 @@ TransactionError FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& ps
return TransactionError::SIGHASH_MISMATCH;
}
- complete &= SignPSBTInput(HidingSigningProvider(pwallet->GetSigningProvider(), !sign, !bip32derivs), psbtx, i, sighash_type);
+ // Get the scriptPubKey to know which SigningProvider to use
+ CScript script;
+ if (!input.witness_utxo.IsNull()) {
+ script = input.witness_utxo.scriptPubKey;
+ } else if (input.non_witness_utxo) {
+ script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey;
+ } else {
+ // There's no UTXO so we can just skip this now
+ complete = false;
+ continue;
+ }
+ SignatureData sigdata;
+ input.FillSignatureData(sigdata);
+ const SigningProvider* provider = pwallet->GetSigningProvider(script, sigdata);
+ if (!provider) {
+ complete = false;
+ continue;
+ }
+
+ complete &= SignPSBTInput(HidingSigningProvider(provider, !sign, !bip32derivs), psbtx, i, sighash_type);
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
- UpdatePSBTOutput(HidingSigningProvider(pwallet->GetSigningProvider(), true, !bip32derivs), psbtx, i);
+ const CTxOut& out = psbtx.tx->vout.at(i);
+ const SigningProvider* provider = pwallet->GetSigningProvider(out.scriptPubKey);
+ if (provider) {
+ UpdatePSBTOutput(HidingSigningProvider(provider, true, !bip32derivs), psbtx, i);
+ }
}
return TransactionError::OK;
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index f7353ebbbb..d95481500d 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -87,15 +87,6 @@ static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver,
}
}
-static LegacyScriptPubKeyMan& GetLegacyScriptPubKeyMan(CWallet& wallet)
-{
- LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
- if (!spk_man) {
- throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
- }
- return *spk_man;
-}
-
UniValue importprivkey(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -134,7 +125,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
}
- LegacyScriptPubKeyMan& spk_man = GetLegacyScriptPubKeyMan(*wallet);
+ EnsureLegacyScriptPubKeyMan(*wallet);
WalletRescanReserver reserver(pwallet);
bool fRescan = true;
@@ -168,7 +159,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
CPubKey pubkey = key.GetPubKey();
- assert(key.VerifyPubKey(pubkey));
+ CHECK_NONFATAL(key.VerifyPubKey(pubkey));
CKeyID vchAddress = pubkey.GetID();
{
pwallet->MarkDirty();
@@ -262,7 +253,7 @@ UniValue importaddress(const JSONRPCRequest& request)
},
}.Check(request);
- LegacyScriptPubKeyMan& spk_man = GetLegacyScriptPubKeyMan(*pwallet);
+ EnsureLegacyScriptPubKeyMan(*pwallet);
std::string strLabel;
if (!request.params[1].isNull())
@@ -325,7 +316,7 @@ UniValue importaddress(const JSONRPCRequest& request)
{
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- pwallet->ReacceptWalletTransactions(*locked_chain);
+ pwallet->ReacceptWalletTransactions();
}
}
@@ -363,28 +354,26 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
//Search partial merkle tree in proof for our transaction and index in valid block
std::vector<uint256> vMatch;
std::vector<unsigned int> vIndex;
- unsigned int txnIndex = 0;
- if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) {
-
- auto locked_chain = pwallet->chain().lock();
- if (locked_chain->getBlockHeight(merkleBlock.header.GetHash()) == nullopt) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
- }
-
- std::vector<uint256>::const_iterator it;
- if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx))==vMatch.end()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
- }
+ if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
+ }
- txnIndex = vIndex[it - vMatch.begin()];
+ auto locked_chain = pwallet->chain().lock();
+ Optional<int> height = locked_chain->getBlockHeight(merkleBlock.header.GetHash());
+ if (height == nullopt) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
- else {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
+
+ std::vector<uint256>::const_iterator it;
+ if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx)) == vMatch.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
}
- wtx.SetConf(CWalletTx::Status::CONFIRMED, merkleBlock.header.GetHash(), txnIndex);
+ unsigned int txnIndex = vIndex[it - vMatch.begin()];
+
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, *height, merkleBlock.header.GetHash(), txnIndex);
+ wtx.m_confirm = confirm;
- auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (pwallet->IsMine(*wtx.tx)) {
@@ -465,7 +454,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
},
}.Check(request);
- LegacyScriptPubKeyMan& spk_man = GetLegacyScriptPubKeyMan(*wallet);
+ EnsureLegacyScriptPubKeyMan(*wallet);
std::string strLabel;
if (!request.params[1].isNull())
@@ -516,7 +505,7 @@ UniValue importpubkey(const JSONRPCRequest& request)
{
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- pwallet->ReacceptWalletTransactions(*locked_chain);
+ pwallet->ReacceptWalletTransactions();
}
}
@@ -549,7 +538,7 @@ UniValue importwallet(const JSONRPCRequest& request)
},
}.Check(request);
- LegacyScriptPubKeyMan& spk_man = GetLegacyScriptPubKeyMan(*wallet);
+ EnsureLegacyScriptPubKeyMan(*wallet);
if (pwallet->chain().havePruned()) {
// Exit early and print an error.
@@ -639,7 +628,7 @@ UniValue importwallet(const JSONRPCRequest& request)
std::string label = std::get<3>(key_tuple);
CPubKey pubkey = key.GetPubKey();
- assert(key.VerifyPubKey(pubkey));
+ CHECK_NONFATAL(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID();
pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(PKHash(keyid)));
@@ -708,7 +697,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
},
}.Check(request);
- LegacyScriptPubKeyMan& spk_man = GetLegacyScriptPubKeyMan(*wallet);
+ LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -759,7 +748,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
},
}.Check(request);
- LegacyScriptPubKeyMan& spk_man = GetLegacyScriptPubKeyMan(*wallet);
+ LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -906,7 +895,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
case TX_SCRIPTHASH: {
if (script_ctx == ScriptContext::P2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside another P2SH");
if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside a P2WSH");
- assert(script_ctx == ScriptContext::TOP);
+ CHECK_NONFATAL(script_ctx == ScriptContext::TOP);
CScriptID id = CScriptID(uint160(solverdata[0]));
auto subscript = std::move(import_data.redeemscript); // Remove redeemscript from import_data to check for superfluous script later.
if (!subscript) return "missing redeemscript";
@@ -1346,7 +1335,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
- LegacyScriptPubKeyMan& spk_man = GetLegacyScriptPubKeyMan(*wallet);
+ EnsureLegacyScriptPubKeyMan(*wallet);
const UniValue& requests = mainRequest.params[0];
@@ -1415,7 +1404,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
{
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- pwallet->ReacceptWalletTransactions(*locked_chain);
+ pwallet->ReacceptWalletTransactions();
}
if (pwallet->IsAbortingRescan()) {
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 8d8e63066c..3563b05c71 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -124,19 +124,29 @@ void EnsureWalletIsUnlocked(const CWallet* pwallet)
}
}
+LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet)
+{
+ LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
+ if (!spk_man) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
+ }
+ return *spk_man;
+}
+
static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx, UniValue& entry)
{
- int confirms = wtx.GetDepthInMainChain(locked_chain);
+ int confirms = wtx.GetDepthInMainChain();
entry.pushKV("confirmations", confirms);
if (wtx.IsCoinBase())
entry.pushKV("generated", true);
if (confirms > 0)
{
entry.pushKV("blockhash", wtx.m_confirm.hashBlock.GetHex());
+ entry.pushKV("blockheight", wtx.m_confirm.block_height);
entry.pushKV("blockindex", wtx.m_confirm.nIndex);
int64_t block_time;
bool found_block = chain.findBlock(wtx.m_confirm.hashBlock, nullptr /* block */, &block_time);
- assert(found_block);
+ CHECK_NONFATAL(found_block);
entry.pushKV("blocktime", block_time);
} else {
entry.pushKV("trusted", wtx.IsTrusted(locked_chain));
@@ -550,7 +560,11 @@ static UniValue signmessage(const JSONRPCRequest& request)
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
- const SigningProvider* provider = pwallet->GetSigningProvider();
+ CScript script_pub_key = GetScriptForDestination(*pkhash);
+ const SigningProvider* provider = pwallet->GetSigningProvider(script_pub_key);
+ if (!provider) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
+ }
CKey key;
CKeyID keyID(*pkhash);
@@ -631,7 +645,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
for (const CTxOut& txout : wtx.tx->vout)
if (txout.scriptPubKey == scriptPubKey)
- if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth)
+ if (wtx.GetDepthInMainChain() >= nMinDepth)
nAmount += txout.nValue;
}
@@ -697,7 +711,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) && pwallet->IsMine(address) && setAddress.count(address)) {
- if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth)
+ if (wtx.GetDepthInMainChain() >= nMinDepth)
nAmount += txout.nValue;
}
}
@@ -966,10 +980,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
},
}.Check(request);
- LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan();
- if (!spk_man) {
- throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
- }
+ LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -987,7 +998,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
if (IsHex(keys_or_addrs[i].get_str()) && (keys_or_addrs[i].get_str().length() == 66 || keys_or_addrs[i].get_str().length() == 130)) {
pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str()));
} else {
- pubkeys.push_back(AddrToPubKey(spk_man, keys_or_addrs[i].get_str()));
+ pubkeys.push_back(AddrToPubKey(&spk_man, keys_or_addrs[i].get_str()));
}
}
@@ -1000,7 +1011,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
// Construct using pay-to-script-hash:
CScript inner;
- CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, *spk_man, inner);
+ CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, spk_man, inner);
pwallet->SetAddressBook(dest, label, "send");
UniValue result(UniValue::VOBJ);
@@ -1057,7 +1068,7 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co
continue;
}
- int nDepth = wtx.GetDepthInMainChain(locked_chain);
+ int nDepth = wtx.GetDepthInMainChain();
if (nDepth < nMinDepth)
continue;
@@ -1314,8 +1325,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con
}
// Received
- if (listReceived.size() > 0 && wtx.GetDepthInMainChain(locked_chain) >= nMinDepth)
- {
+ if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) {
for (const COutputEntry& r : listReceived)
{
std::string label;
@@ -1332,9 +1342,9 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con
MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase())
{
- if (wtx.GetDepthInMainChain(locked_chain) < 1)
+ if (wtx.GetDepthInMainChain() < 1)
entry.pushKV("category", "orphan");
- else if (wtx.IsImmatureCoinBase(locked_chain))
+ else if (wtx.IsImmatureCoinBase())
entry.pushKV("category", "immature");
else
entry.pushKV("category", "generate");
@@ -1362,6 +1372,7 @@ static const std::string TransactionDescriptionString()
" \"generated\": xxx, (bool) Only present if transaction only input is a coinbase one.\n"
" \"trusted\": xxx, (bool) Only present if we consider transaction to be trusted and so safe to spend from.\n"
" \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction.\n"
+ " \"blockheight\": n, (numeric) The block height containing the transaction.\n"
" \"blockindex\": n, (numeric) The index of the transaction in the block that includes it.\n"
" \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n"
" \"txid\": \"transactionid\", (string) The transaction id.\n"
@@ -1598,7 +1609,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
CWalletTx tx = pairWtx.second;
- if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) {
+ if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) {
ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
}
}
@@ -1715,7 +1726,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
}
const CWalletTx& wtx = it->second;
- CAmount nCredit = wtx.GetCredit(*locked_chain, filter);
+ CAmount nCredit = wtx.GetCredit(filter);
CAmount nDebit = wtx.GetDebit(filter);
CAmount nNet = nCredit - nDebit;
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
@@ -1779,7 +1790,7 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
if (!pwallet->mapWallet.count(hash)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
}
- if (!pwallet->AbandonTransaction(*locked_chain, hash)) {
+ if (!pwallet->AbandonTransaction(hash)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment");
}
@@ -2210,7 +2221,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
}
- if (pwallet->IsSpent(*locked_chain, outpt.hash, outpt.n)) {
+ if (pwallet->IsSpent(outpt.hash, outpt.n)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
}
@@ -2933,34 +2944,36 @@ static UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("label", i->second.name);
}
- const SigningProvider* provider = pwallet->GetSigningProvider();
- if (scriptPubKey.IsPayToScriptHash()) {
- const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address));
- CScript redeemScript;
- if (provider->GetCScript(hash, redeemScript)) {
- entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
- // Now check if the redeemScript is actually a P2WSH script
- CTxDestination witness_destination;
- if (redeemScript.IsPayToWitnessScriptHash()) {
- bool extracted = ExtractDestination(redeemScript, witness_destination);
- assert(extracted);
- // Also return the witness script
- const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination);
- CScriptID id;
- CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
- CScript witnessScript;
- if (provider->GetCScript(id, witnessScript)) {
- entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ const SigningProvider* provider = pwallet->GetSigningProvider(scriptPubKey);
+ if (provider) {
+ if (scriptPubKey.IsPayToScriptHash()) {
+ const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address));
+ CScript redeemScript;
+ if (provider->GetCScript(hash, redeemScript)) {
+ entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
+ // Now check if the redeemScript is actually a P2WSH script
+ CTxDestination witness_destination;
+ if (redeemScript.IsPayToWitnessScriptHash()) {
+ bool extracted = ExtractDestination(redeemScript, witness_destination);
+ CHECK_NONFATAL(extracted);
+ // Also return the witness script
+ const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination);
+ CScriptID id;
+ CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
+ CScript witnessScript;
+ if (provider->GetCScript(id, witnessScript)) {
+ entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ }
}
}
- }
- } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
- const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address);
- CScriptID id;
- CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
- CScript witnessScript;
- if (provider->GetCScript(id, witnessScript)) {
- entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
+ const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address);
+ CScriptID id;
+ CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
+ CScript witnessScript;
+ if (provider->GetCScript(id, witnessScript)) {
+ entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ }
}
}
}
@@ -2971,8 +2984,11 @@ static UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
if (out.fSolvable) {
- auto descriptor = InferDescriptor(scriptPubKey, *pwallet->GetLegacyScriptPubKeyMan());
- entry.pushKV("desc", descriptor->ToString());
+ const SigningProvider* provider = pwallet->GetSigningProvider(scriptPubKey);
+ if (provider) {
+ auto descriptor = InferDescriptor(scriptPubKey, *provider);
+ entry.pushKV("desc", descriptor->ToString());
+ }
}
if (avoid_reuse) entry.pushKV("reused", reused);
entry.pushKV("safe", out.fSafe);
@@ -3281,7 +3297,23 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
// Parse the prevtxs array
ParsePrevouts(request.params[1], nullptr, coins);
- return SignTransaction(mtx, &*pwallet->GetLegacyScriptPubKeyMan(), coins, request.params[2]);
+ std::set<const SigningProvider*> providers;
+ for (const std::pair<COutPoint, Coin> coin_pair : coins) {
+ const SigningProvider* provider = pwallet->GetSigningProvider(coin_pair.second.out.scriptPubKey);
+ if (provider) {
+ providers.insert(std::move(provider));
+ }
+ }
+ if (providers.size() == 0) {
+ // When there are no available providers, use DUMMY_SIGNING_PROVIDER so we can check if the tx is complete
+ providers.insert(&DUMMY_SIGNING_PROVIDER);
+ }
+
+ UniValue result(UniValue::VOBJ);
+ for (const SigningProvider* provider : providers) {
+ SignTransaction(mtx, provider, coins, request.params[2], result);
+ }
+ return result;
}
static UniValue bumpfee(const JSONRPCRequest& request)
@@ -3646,9 +3678,10 @@ static UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& de
{
UniValue ret(UniValue::VOBJ);
UniValue detail = DescribeAddress(dest);
+ CScript script = GetScriptForDestination(dest);
const SigningProvider* provider = nullptr;
if (pwallet) {
- provider = pwallet->GetSigningProvider();
+ provider = pwallet->GetSigningProvider(script);
}
ret.pushKVs(detail);
ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(provider), dest));
@@ -3740,11 +3773,11 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
CScript scriptPubKey = GetScriptForDestination(dest);
ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
- const SigningProvider* provider = pwallet->GetSigningProvider();
+ const SigningProvider* provider = pwallet->GetSigningProvider(scriptPubKey);
isminetype mine = pwallet->IsMine(dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
- bool solvable = IsSolvable(*provider, scriptPubKey);
+ bool solvable = provider && IsSolvable(*provider, scriptPubKey);
ret.pushKV("solvable", solvable);
if (solvable) {
ret.pushKV("desc", InferDescriptor(scriptPubKey, *provider)->ToString());
@@ -3757,17 +3790,9 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
}
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
- ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan();
+ ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey);
if (spk_man) {
- CKeyID key_id = GetKeyForDestination(*provider, dest);
- const CKeyMetadata* meta = nullptr;
- if (!key_id.IsNull()) {
- meta = spk_man->GetMetadata(key_id);
- }
- if (!meta) {
- meta = spk_man->GetMetadata(CScriptID(scriptPubKey));
- }
- if (meta) {
+ if (const CKeyMetadata* meta = spk_man->GetMetadata(dest)) {
ret.pushKV("timestamp", meta->nCreateTime);
if (meta->has_key_origin) {
ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path));
@@ -3831,7 +3856,7 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
// address strings, but build a separate set as a precaution just in
// case it does.
bool unique = addresses.emplace(address).second;
- assert(unique);
+ CHECK_NONFATAL(unique);
// UniValue::pushKV checks if the key exists in O(N)
// and since duplicate addresses are unexpected (checked with
// std::set in O(log(N))), UniValue::__pushKV is used instead,
@@ -3933,10 +3958,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
},
}.Check(request);
- LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan();
- if (!spk_man) {
- throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
- }
+ LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
if (pwallet->chain().isInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
@@ -3963,22 +3985,22 @@ UniValue sethdseed(const JSONRPCRequest& request)
CPubKey master_pub_key;
if (request.params[1].isNull()) {
- master_pub_key = spk_man->GenerateNewSeed();
+ master_pub_key = spk_man.GenerateNewSeed();
} else {
CKey key = DecodeSecret(request.params[1].get_str());
if (!key.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
}
- if (HaveKey(*spk_man, key)) {
+ if (HaveKey(spk_man, key)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
}
- master_pub_key = spk_man->DeriveNewSeed(key);
+ master_pub_key = spk_man.DeriveNewSeed(key);
}
- spk_man->SetHDSeed(master_pub_key);
- if (flush_key_pool) spk_man->NewKeyPool();
+ spk_man.SetHDSeed(master_pub_key);
+ if (flush_key_pool) spk_man.NewKeyPool();
return NullUniValue;
}
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index 31d3f7a5f9..becca455f6 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -12,6 +12,7 @@
class CRPCTable;
class CWallet;
class JSONRPCRequest;
+class LegacyScriptPubKeyMan;
class UniValue;
struct PartiallySignedTransaction;
class CTransaction;
@@ -40,6 +41,7 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
std::string HelpRequiringPassphrase(const CWallet*);
void EnsureWalletIsUnlocked(const CWallet*);
bool EnsureWalletIsAvailable(const CWallet*, bool avoidException);
+LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet);
UniValue getaddressinfo(const JSONRPCRequest& request);
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request);
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index bb13db11ba..3eaaf3786c 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -14,7 +14,7 @@
bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error)
{
error.clear();
- TopUpKeyPool();
+ TopUp();
// Generate a new key that is added to wallet
CPubKey new_key;
@@ -264,10 +264,8 @@ bool LegacyScriptPubKeyMan::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool)
{
- {
- if (!ReserveKeyFromKeyPool(index, keypool, internal)) {
- return false;
- }
+ if (!ReserveKeyFromKeyPool(index, keypool, internal)) {
+ return false;
}
return true;
}
@@ -282,11 +280,6 @@ void LegacyScriptPubKeyMan::ReturnDestination(int64_t index, bool internal, cons
ReturnKey(index, internal, pubkey);
}
-bool LegacyScriptPubKeyMan::TopUp(unsigned int size)
-{
- return TopUpKeyPool(size);
-}
-
void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
{
AssertLockHeld(cs_wallet);
@@ -297,7 +290,7 @@ void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)
WalletLogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
MarkReserveKeysAsUsed(mi->second);
- if (!TopUpKeyPool()) {
+ if (!TopUp()) {
WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
}
}
@@ -401,7 +394,7 @@ bool LegacyScriptPubKeyMan::Upgrade(int prev_version, std::string& error)
}
// Regenerate the keypool if upgraded to HD
if (hd_upgrade) {
- if (!TopUpKeyPool()) {
+ if (!TopUp()) {
error = _("Unable to generate keys").translated;
return false;
}
@@ -476,18 +469,24 @@ int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const
return nTimeFirstKey;
}
-const CKeyMetadata* LegacyScriptPubKeyMan::GetMetadata(uint160 id) const
+const CKeyMetadata* LegacyScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const
{
AssertLockHeld(cs_wallet);
- auto it = mapKeyMetadata.find(CKeyID(id));
- if (it != mapKeyMetadata.end()) {
- return &it->second;
- } else {
- auto it2 = m_script_metadata.find(CScriptID(id));
- if (it2 != m_script_metadata.end()) {
- return &it2->second;
+
+ CKeyID key_id = GetKeyForDestination(*this, dest);
+ if (!key_id.IsNull()) {
+ auto it = mapKeyMetadata.find(key_id);
+ if (it != mapKeyMetadata.end()) {
+ return &it->second;
}
}
+
+ CScript scriptPubKey = GetScriptForDestination(dest);
+ auto it = m_script_metadata.find(CScriptID(scriptPubKey));
+ if (it != m_script_metadata.end()) {
+ return &it->second;
+ }
+
return nullptr;
}
@@ -1023,7 +1022,7 @@ bool LegacyScriptPubKeyMan::NewKeyPool()
m_pool_key_to_index.clear();
- if (!TopUpKeyPool()) {
+ if (!TopUp()) {
return false;
}
WalletLogPrintf("LegacyScriptPubKeyMan::NewKeyPool rewrote keypool\n");
@@ -1031,7 +1030,7 @@ bool LegacyScriptPubKeyMan::NewKeyPool()
return true;
}
-bool LegacyScriptPubKeyMan::TopUpKeyPool(unsigned int kpSize)
+bool LegacyScriptPubKeyMan::TopUp(unsigned int kpSize)
{
if (!CanGenerateKeys()) {
return false;
@@ -1148,7 +1147,7 @@ bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& key
{
LOCK(cs_wallet);
- TopUpKeyPool();
+ TopUp();
bool fReturningInternal = fRequestedInternal;
fReturningInternal &= (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) || m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 0dbf98ee94..4f17156792 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -186,7 +186,8 @@ public:
virtual int64_t GetTimeFirstKey() const { return 0; }
- virtual const CKeyMetadata* GetMetadata(uint160 id) const { return nullptr; }
+ //! Return address metadata
+ virtual const CKeyMetadata* GetMetadata(const CTxDestination& dest) const { return nullptr; }
};
class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider
@@ -302,7 +303,7 @@ public:
int64_t GetTimeFirstKey() const override;
- const CKeyMetadata* GetMetadata(uint160 id) const override;
+ const CKeyMetadata* GetMetadata(const CTxDestination& dest) const override;
bool CanGetAddresses(bool internal = false) override;
@@ -355,7 +356,6 @@ public:
//! Load a keypool entry
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool TopUpKeyPool(unsigned int kpSize = 0);
bool NewKeyPool();
void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 397e6ea9d3..7e9f88f6b7 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -9,7 +9,7 @@
#include <amount.h>
#include <primitives/transaction.h>
#include <random.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <wallet/test/wallet_test_fixture.h>
#include <boost/test/unit_test.hpp>
@@ -55,7 +55,7 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set)
set.emplace(MakeTransactionRef(tx), nInput);
}
-static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
+static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
{
balance += nValue;
static int nextLockTime = 0;
@@ -63,21 +63,31 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
+ if (spendable) {
+ CTxDestination dest;
+ std::string error;
+ assert(wallet.GetNewDestination(OutputType::BECH32, "", dest, error));
+ tx.vout[nInput].scriptPubKey = GetScriptForDestination(dest);
+ }
if (fIsFromMe) {
// IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(),
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
tx.vin.resize(1);
}
- std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
+ std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx)));
if (fIsFromMe)
{
wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
}
COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
vCoins.push_back(output);
- testWallet.AddToWallet(*wtx.get());
+ wallet.AddToWallet(*wtx.get());
wtxn.emplace_back(std::move(wtx));
}
+static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
+{
+ add_coin(testWallet, nValue, nAge, fIsFromMe, nInput, spendable);
+}
static void empty_wallet(void)
{
@@ -252,17 +262,33 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
- // Make sure that we aren't using BnB when there are preset inputs
+ // Test fees subtracted from output:
+ empty_wallet();
+ add_coin(1 * CENT);
+ vCoins.at(0).nInputBytes = 40;
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
+ coin_selection_params_bnb.m_subtract_fee_outputs = true;
+ BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
+ BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
+
+ // Make sure that can use BnB when there are preset inputs
empty_wallet();
- add_coin(5 * CENT);
- add_coin(3 * CENT);
- add_coin(2 * CENT);
- CCoinControl coin_control;
- coin_control.fAllowOtherInputs = true;
- coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
- BOOST_CHECK(testWallet.SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb, bnb_used));
- BOOST_CHECK(!bnb_used);
- BOOST_CHECK(!coin_selection_params_bnb.use_bnb);
+ {
+ std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock());
+ bool firstRun;
+ wallet->LoadWallet(firstRun);
+ LOCK(wallet->cs_wallet);
+ add_coin(*wallet, 5 * CENT, 6 * 24, false, 0, true);
+ add_coin(*wallet, 3 * CENT, 6 * 24, false, 0, true);
+ add_coin(*wallet, 2 * CENT, 6 * 24, false, 0, true);
+ CCoinControl coin_control;
+ coin_control.fAllowOtherInputs = true;
+ coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
+ coin_selection_params_bnb.effective_fee = CFeeRate(0);
+ BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb, bnb_used));
+ BOOST_CHECK(bnb_used);
+ BOOST_CHECK(coin_selection_params_bnb.use_bnb);
+ }
}
BOOST_AUTO_TEST_CASE(knapsack_solver_test)
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index c961456572..f4a4c9fa7c 100644
--- a/src/wallet/test/db_tests.cpp
+++ b/src/wallet/test/db_tests.cpp
@@ -7,7 +7,7 @@
#include <boost/test/unit_test.hpp>
#include <fs.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <wallet/db.h>
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
index eb4e72c88b..6ba7d66b7c 100644
--- a/src/wallet/test/init_test_fixture.h
+++ b/src/wallet/test/init_test_fixture.h
@@ -7,7 +7,7 @@
#include <interfaces/chain.h>
#include <node/context.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
struct InitWalletDirTestingSetup: public BasicTestingSetup {
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index 279542ffad..c228e06009 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -5,7 +5,8 @@
#include <boost/test/unit_test.hpp>
#include <noui.h>
-#include <test/setup_common.h>
+#include <test/util/logging.h>
+#include <test/util/setup_common.h>
#include <util/system.h>
#include <wallet/test/init_test_fixture.h>
@@ -34,28 +35,31 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
{
SetWalletDir(m_walletdir_path_cases["nonexistent"]);
- noui_suppress();
- bool result = m_chain_client->verify();
- noui_reconnect();
- BOOST_CHECK(result == false);
+ {
+ ASSERT_DEBUG_LOG("does not exist");
+ bool result = m_chain_client->verify();
+ BOOST_CHECK(result == false);
+ }
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory)
{
SetWalletDir(m_walletdir_path_cases["file"]);
- noui_suppress();
- bool result = m_chain_client->verify();
- noui_reconnect();
- BOOST_CHECK(result == false);
+ {
+ ASSERT_DEBUG_LOG("is not a directory");
+ bool result = m_chain_client->verify();
+ BOOST_CHECK(result == false);
+ }
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
{
SetWalletDir(m_walletdir_path_cases["relative"]);
- noui_suppress();
- bool result = m_chain_client->verify();
- noui_reconnect();
- BOOST_CHECK(result == false);
+ {
+ ASSERT_DEBUG_LOG("is a relative path");
+ bool result = m_chain_client->verify();
+ BOOST_CHECK(result == false);
+ }
}
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp
index 24636fd599..76c3639d16 100644
--- a/src/wallet/test/ismine_tests.cpp
+++ b/src/wallet/test/ismine_tests.cpp
@@ -6,7 +6,7 @@
#include <node/context.h>
#include <script/script.h>
#include <script/standard.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <wallet/ismine.h>
#include <wallet/wallet.h>
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index 27a64ff12f..d930ca6bea 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -9,7 +9,7 @@
#include <wallet/wallet.h>
#include <boost/test/unit_test.hpp>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <wallet/test/wallet_test_fixture.h>
BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup)
diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp
index 2f41813234..97f8c94fa6 100644
--- a/src/wallet/test/wallet_crypto_tests.cpp
+++ b/src/wallet/test/wallet_crypto_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <wallet/crypter.h>
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index def6f1934e..4e4129fb2c 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_WALLET_TEST_WALLET_TEST_FIXTURE_H
#define BITCOIN_WALLET_TEST_WALLET_TEST_FIXTURE_H
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <interfaces/chain.h>
#include <interfaces/wallet.h>
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 72e1b4e83b..2f21b2439b 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -12,7 +12,7 @@
#include <node/context.h>
#include <policy/policy.h>
#include <rpc/server.h>
-#include <test/setup_common.h>
+#include <test/util/setup_common.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/test/wallet_test_fixture.h>
@@ -50,6 +50,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions accommodates a null start block.
{
CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ {
+ LOCK(wallet.cs_wallet);
+ wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ }
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -65,6 +69,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// and new block files.
{
CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ {
+ LOCK(wallet.cs_wallet);
+ wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ }
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -84,6 +92,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// file.
{
CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ {
+ LOCK(wallet.cs_wallet);
+ wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ }
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -102,6 +114,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions scans no blocks.
{
CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ {
+ LOCK(wallet.cs_wallet);
+ wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ }
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -258,18 +274,20 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
LockAssertion lock(::cs_main);
LOCK(wallet.cs_wallet);
AssertLockHeld(spk_man->cs_wallet);
+ wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
- wtx.SetConf(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 0);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 0);
+ wtx.m_confirm = confirm;
// Call GetImmatureCredit() once before adding the key to the wallet to
// cache the current immature credit amount, which is 0.
- BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 0);
+ BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 0);
// Invalidate the cached vanue, add the key, and make sure a new immature
// credit amount is calculated.
wtx.MarkDirty();
BOOST_CHECK(spk_man->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()));
- BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 50*COIN);
+ BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50*COIN);
}
static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
@@ -300,7 +318,8 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
wallet.AddToWallet(wtx);
}
if (block) {
- wtx.SetConf(CWalletTx::Status::CONFIRMED, block->GetBlockHash(), 0);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, block->nHeight, block->GetBlockHash(), 0);
+ wtx.m_confirm = confirm;
}
wallet.AddToWallet(wtx);
return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart;
@@ -338,9 +357,10 @@ BOOST_AUTO_TEST_CASE(LoadReceiveRequests)
{
CTxDestination dest = PKHash();
LOCK(m_wallet.cs_wallet);
- m_wallet.AddDestData(dest, "misc", "val_misc");
- m_wallet.AddDestData(dest, "rr0", "val_rr0");
- m_wallet.AddDestData(dest, "rr1", "val_rr1");
+ WalletBatch batch{m_wallet.GetDatabase()};
+ m_wallet.AddDestData(batch, dest, "misc", "val_misc");
+ m_wallet.AddDestData(batch, dest, "rr0", "val_rr0");
+ m_wallet.AddDestData(batch, dest, "rr1", "val_rr1");
auto values = m_wallet.GetDestValues("rr");
BOOST_CHECK_EQUAL(values.size(), 2U);
@@ -435,6 +455,10 @@ public:
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock());
+ {
+ LOCK(wallet->cs_wallet);
+ wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
+ }
bool firstRun;
wallet->LoadWallet(firstRun);
AddKey(*wallet, coinbaseKey);
@@ -473,9 +497,11 @@ public:
LOCK(cs_main);
LOCK(wallet->cs_wallet);
+ wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, ::ChainActive().Tip()->GetBlockHash());
auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
- it->second.SetConf(CWalletTx::Status::CONFIRMED, ::ChainActive().Tip()->GetBlockHash(), 1);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, ::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash(), 1);
+ it->second.m_confirm = confirm;
return it->second;
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 6dc5d403ca..fcb7bfecb8 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -452,7 +452,7 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
* Outpoint is spent if any non-conflicted transaction
* spends it:
*/
-bool CWallet::IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash, unsigned int n) const
+bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
{
const COutPoint outpoint(hash, n);
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
@@ -463,7 +463,7 @@ bool CWallet::IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash
const uint256& wtxid = it->second;
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) {
- int depth = mit->second.GetDepthInMainChain(locked_chain);
+ int depth = mit->second.GetDepthInMainChain();
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned()))
return true; // Spent
}
@@ -572,7 +572,9 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// if we are using HD, replace the HD seed with a new one
if (auto spk_man = m_spk_man.get()) {
if (spk_man->IsHDEnabled()) {
- spk_man->SetupGeneration(true);
+ if (!spk_man->SetupGeneration(true)) {
+ return false;
+ }
}
}
Lock();
@@ -699,19 +701,19 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
return success;
}
-void CWallet::SetUsedDestinationState(const uint256& hash, unsigned int n, bool used)
+void CWallet::SetUsedDestinationState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used)
{
+ AssertLockHeld(cs_wallet);
const CWalletTx* srctx = GetWalletTx(hash);
if (!srctx) return;
CTxDestination dst;
if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) {
if (IsMine(dst)) {
- LOCK(cs_wallet);
if (used && !GetDestData(dst, "used", nullptr)) {
- AddDestData(dst, "used", "p"); // p for "present", opposite of absent (null)
+ AddDestData(batch, dst, "used", "p"); // p for "present", opposite of absent (null)
} else if (!used && GetDestData(dst, "used", nullptr)) {
- EraseDestData(dst, "used");
+ EraseDestData(batch, dst, "used");
}
}
}
@@ -742,7 +744,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
// Mark used destinations
for (const CTxIn& txin : wtxIn.tx->vin) {
const COutPoint& op = txin.prevout;
- SetUsedDestinationState(op.hash, op.n, true);
+ SetUsedDestinationState(batch, op.hash, op.n, true);
}
}
@@ -766,10 +768,12 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
wtx.m_confirm.status = wtxIn.m_confirm.status;
wtx.m_confirm.nIndex = wtxIn.m_confirm.nIndex;
wtx.m_confirm.hashBlock = wtxIn.m_confirm.hashBlock;
+ wtx.m_confirm.block_height = wtxIn.m_confirm.block_height;
fUpdated = true;
} else {
assert(wtx.m_confirm.nIndex == wtxIn.m_confirm.nIndex);
assert(wtx.m_confirm.hashBlock == wtxIn.m_confirm.hashBlock);
+ assert(wtx.m_confirm.block_height == wtxIn.m_confirm.block_height);
}
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
{
@@ -820,12 +824,22 @@ void CWallet::LoadToWallet(CWalletTx& wtxIn)
{
// If wallet doesn't have a chain (e.g wallet-tool), lock can't be taken.
auto locked_chain = LockChain();
- // If tx hasn't been reorged out of chain while wallet being shutdown
- // change tx status to UNCONFIRMED and reset hashBlock/nIndex.
- if (!wtxIn.m_confirm.hashBlock.IsNull()) {
- if (locked_chain && !locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock)) {
+ if (locked_chain) {
+ Optional<int> block_height = locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock);
+ if (block_height) {
+ // Update cached block height variable since it not stored in the
+ // serialized transaction.
+ wtxIn.m_confirm.block_height = *block_height;
+ } else if (wtxIn.isConflicted() || wtxIn.isConfirmed()) {
+ // If tx block (or conflicting block) was reorged out of chain
+ // while the wallet was shutdown, change tx status to UNCONFIRMED
+ // and reset block height, hash, and index. ABANDONED tx don't have
+ // associated blocks and don't need to be updated. The case where a
+ // transaction was reorged out while online and then reconfirmed
+ // while offline is covered by the rescan logic.
wtxIn.setUnconfirmed();
wtxIn.m_confirm.hashBlock = uint256();
+ wtxIn.m_confirm.block_height = 0;
wtxIn.m_confirm.nIndex = 0;
}
}
@@ -842,25 +856,25 @@ void CWallet::LoadToWallet(CWalletTx& wtxIn)
if (it != mapWallet.end()) {
CWalletTx& prevtx = it->second;
if (prevtx.isConflicted()) {
- MarkConflicted(prevtx.m_confirm.hashBlock, wtx.GetHash());
+ MarkConflicted(prevtx.m_confirm.hashBlock, prevtx.m_confirm.block_height, wtx.GetHash());
}
}
}
}
-bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool fUpdate)
+bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool fUpdate)
{
const CTransaction& tx = *ptx;
{
AssertLockHeld(cs_wallet);
- if (!block_hash.IsNull()) {
+ if (!confirm.hashBlock.IsNull()) {
for (const CTxIn& txin : tx.vin) {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);
while (range.first != range.second) {
if (range.first->second != tx.GetHash()) {
- WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), block_hash.ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n);
- MarkConflicted(block_hash, range.first->second);
+ WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), confirm.hashBlock.ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n);
+ MarkConflicted(confirm.hashBlock, confirm.block_height, range.first->second);
}
range.first++;
}
@@ -888,7 +902,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::St
// Block disconnection override an abandoned tx as unconfirmed
// which means user may have to call abandontransaction again
- wtx.SetConf(status, block_hash, posInBlock);
+ wtx.m_confirm = confirm;
return AddToWallet(wtx, false);
}
@@ -901,7 +915,7 @@ bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
auto locked_chain = chain().lock();
LOCK(cs_wallet);
const CWalletTx* wtx = GetWalletTx(hashTx);
- return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain(*locked_chain) == 0 && !wtx->InMempool();
+ return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool();
}
void CWallet::MarkInputsDirty(const CTransactionRef& tx)
@@ -914,9 +928,9 @@ void CWallet::MarkInputsDirty(const CTransactionRef& tx)
}
}
-bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const uint256& hashTx)
+bool CWallet::AbandonTransaction(const uint256& hashTx)
{
- auto locked_chain_recursive = chain().lock(); // Temporary. Removed in upcoming lock cleanup
+ auto locked_chain = chain().lock(); // Temporary. Removed in upcoming lock cleanup
LOCK(cs_wallet);
WalletBatch batch(*database, "r+");
@@ -928,7 +942,7 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
auto it = mapWallet.find(hashTx);
assert(it != mapWallet.end());
CWalletTx& origtx = it->second;
- if (origtx.GetDepthInMainChain(locked_chain) != 0 || origtx.InMempool()) {
+ if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) {
return false;
}
@@ -941,14 +955,13 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
auto it = mapWallet.find(now);
assert(it != mapWallet.end());
CWalletTx& wtx = it->second;
- int currentconfirm = wtx.GetDepthInMainChain(locked_chain);
+ int currentconfirm = wtx.GetDepthInMainChain();
// If the orig tx was not in block, none of its spends can be
assert(currentconfirm <= 0);
// if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
if (currentconfirm == 0 && !wtx.isAbandoned()) {
// If the orig tx was not in block/mempool, none of its spends can be in mempool
assert(!wtx.InMempool());
- wtx.m_confirm.nIndex = 0;
wtx.setAbandoned();
wtx.MarkDirty();
batch.WriteTx(wtx);
@@ -970,12 +983,12 @@ bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const ui
return true;
}
-void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
+void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx)
{
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- int conflictconfirms = -locked_chain->getBlockDepth(hashBlock);
+ int conflictconfirms = (m_last_block_processed_height - conflicting_height + 1) * -1;
// If number of conflict confirms cannot be determined, this means
// that the block is still unknown or not yet part of the main chain,
// for example when loading the wallet during a reindex. Do nothing in that
@@ -998,12 +1011,13 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
auto it = mapWallet.find(now);
assert(it != mapWallet.end());
CWalletTx& wtx = it->second;
- int currentconfirm = wtx.GetDepthInMainChain(*locked_chain);
+ int currentconfirm = wtx.GetDepthInMainChain();
if (conflictconfirms < currentconfirm) {
// Block is 'more conflicted' than current confirm; update.
// Mark transaction as conflicted with this block.
wtx.m_confirm.nIndex = 0;
wtx.m_confirm.hashBlock = hashBlock;
+ wtx.m_confirm.block_height = conflicting_height;
wtx.setConflicted();
wtx.MarkDirty();
batch.WriteTx(wtx);
@@ -1022,9 +1036,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
}
}
-void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool update_tx)
+void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool update_tx)
{
- if (!AddToWalletIfInvolvingMe(ptx, status, block_hash, posInBlock, update_tx))
+ if (!AddToWalletIfInvolvingMe(ptx, confirm, update_tx))
return; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
@@ -1036,7 +1050,8 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Status stat
void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- SyncTransaction(ptx, CWalletTx::Status::UNCONFIRMED, {} /* block hash */, 0 /* position in block */);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
+ SyncTransaction(ptx, confirm);
auto it = mapWallet.find(ptx->GetHash());
if (it != mapWallet.end()) {
@@ -1052,23 +1067,26 @@ void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
}
}
-void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) {
+void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted, int height)
+{
const uint256& block_hash = block.GetHash();
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- for (size_t i = 0; i < block.vtx.size(); i++) {
- SyncTransaction(block.vtx[i], CWalletTx::Status::CONFIRMED, block_hash, i);
- TransactionRemovedFromMempool(block.vtx[i]);
+ m_last_block_processed_height = height;
+ m_last_block_processed = block_hash;
+ for (size_t index = 0; index < block.vtx.size(); index++) {
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, block_hash, index);
+ SyncTransaction(block.vtx[index], confirm);
+ TransactionRemovedFromMempool(block.vtx[index]);
}
for (const CTransactionRef& ptx : vtxConflicted) {
TransactionRemovedFromMempool(ptx);
}
-
- m_last_block_processed = block_hash;
}
-void CWallet::BlockDisconnected(const CBlock& block) {
+void CWallet::BlockDisconnected(const CBlock& block, int height)
+{
auto locked_chain = chain().lock();
LOCK(cs_wallet);
@@ -1076,8 +1094,11 @@ void CWallet::BlockDisconnected(const CBlock& block) {
// be unconfirmed, whether or not the transaction is added back to the mempool.
// User may have to call abandontransaction again. It may be addressed in the
// future with a stickier abandoned state or even removing abandontransaction call.
+ m_last_block_processed_height = height - 1;
+ m_last_block_processed = block.hashPrevBlock;
for (const CTransactionRef& ptx : block.vtx) {
- SyncTransaction(ptx, CWalletTx::Status::UNCONFIRMED, {} /* block hash */, 0 /* position in block */);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::UNCONFIRMED, /* block_height */ 0, {}, /* nIndex */ 0);
+ SyncTransaction(ptx, confirm);
}
}
@@ -1094,7 +1115,7 @@ void CWallet::BlockUntilSyncedToCurrentChain() {
// for the queue to drain enough to execute it (indicating we are caught up
// at least with the time we entered this function).
uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);
- chain().waitForNotificationsIfNewBlocksConnected(last_block_hash);
+ chain().waitForNotificationsIfTipChanged(last_block_hash);
}
@@ -1342,7 +1363,11 @@ bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig
const CScript& scriptPubKey = txout.scriptPubKey;
SignatureData sigdata;
- const SigningProvider* provider = GetSigningProvider();
+ const SigningProvider* provider = GetSigningProvider(scriptPubKey);
+ if (!provider) {
+ // We don't know about this scriptpbuKey;
+ return false;
+ }
if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
return false;
@@ -1623,7 +1648,8 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- SyncTransaction(block.vtx[posInBlock], CWalletTx::Status::CONFIRMED, block_hash, posInBlock, fUpdate);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, *block_height, block_hash, posInBlock);
+ SyncTransaction(block.vtx[posInBlock], confirm, fUpdate);
}
// scan succeeded, record block as most recent successfully scanned
result.last_scanned_block = block_hash;
@@ -1671,7 +1697,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
return result;
}
-void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
+void CWallet::ReacceptWalletTransactions()
{
// If transactions aren't being broadcasted, don't let them into local mempool either
if (!fBroadcastTransactions)
@@ -1684,7 +1710,7 @@ void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid);
- int nDepth = wtx.GetDepthInMainChain(locked_chain);
+ int nDepth = wtx.GetDepthInMainChain();
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
@@ -1695,11 +1721,11 @@ void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
std::string unused_err_string;
- wtx.SubmitMemoryPoolAndRelay(unused_err_string, false, locked_chain);
+ wtx.SubmitMemoryPoolAndRelay(unused_err_string, false);
}
}
-bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay, interfaces::Chain::Lock& locked_chain)
+bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay)
{
// Can't relay if wallet is not broadcasting
if (!pwallet->GetBroadcastTransactions()) return false;
@@ -1709,7 +1735,7 @@ bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay, in
// cause log spam.
if (IsCoinBase()) return false;
// Don't try to submit conflicted or confirmed transactions.
- if (GetDepthInMainChain(locked_chain) != 0) return false;
+ if (GetDepthInMainChain() != 0) return false;
// Submit transaction to mempool for relay
pwallet->WalletLogPrintf("Submitting wtx %s to mempool for relay\n", GetHash().ToString());
@@ -1763,10 +1789,10 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
return debit;
}
-CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const isminefilter& filter) const
+CAmount CWalletTx::GetCredit(const isminefilter& filter) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsImmatureCoinBase(locked_chain))
+ if (IsImmatureCoinBase())
return 0;
CAmount credit = 0;
@@ -1780,16 +1806,16 @@ CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const ismine
return credit;
}
-CAmount CWalletTx::GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache) const
+CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
{
- if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
+ if (IsImmatureCoinBase() && IsInMainChain()) {
return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
}
return 0;
}
-CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache, const isminefilter& filter) const
+CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
{
if (pwallet == nullptr)
return 0;
@@ -1798,7 +1824,7 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL;
// Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsImmatureCoinBase(locked_chain))
+ if (IsImmatureCoinBase())
return 0;
if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) {
@@ -1810,7 +1836,7 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
uint256 hashTx = GetHash();
for (unsigned int i = 0; i < tx->vout.size(); i++)
{
- if (!pwallet->IsSpent(locked_chain, hashTx, i) && (allow_used_addresses || !pwallet->IsUsedDestination(hashTx, i))) {
+ if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsUsedDestination(hashTx, i))) {
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, filter);
if (!MoneyRange(nCredit))
@@ -1825,9 +1851,9 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
return nCredit;
}
-CAmount CWalletTx::GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache) const
+CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
{
- if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
+ if (IsImmatureCoinBase() && IsInMainChain()) {
return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
}
@@ -1850,32 +1876,37 @@ bool CWalletTx::InMempool() const
bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain) const
{
+ std::set<uint256> s;
+ return IsTrusted(locked_chain, s);
+}
+
+bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain, std::set<uint256>& trusted_parents) const
+{
// Quick answer in most cases
- if (!locked_chain.checkFinalTx(*tx)) {
- return false;
- }
- int nDepth = GetDepthInMainChain(locked_chain);
- if (nDepth >= 1)
- return true;
- if (nDepth < 0)
- return false;
- if (!pwallet->m_spend_zero_conf_change || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit
- return false;
+ if (!locked_chain.checkFinalTx(*tx)) return false;
+ int nDepth = GetDepthInMainChain();
+ if (nDepth >= 1) return true;
+ if (nDepth < 0) return false;
+ // using wtx's cached debit
+ if (!pwallet->m_spend_zero_conf_change || !IsFromMe(ISMINE_ALL)) return false;
// Don't trust unconfirmed transactions from us unless they are in the mempool.
- if (!InMempool())
- return false;
+ if (!InMempool()) return false;
// Trusted if all inputs are from us and are in the mempool:
for (const CTxIn& txin : tx->vin)
{
// Transactions not sent by us: not trusted
const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash);
- if (parent == nullptr)
- return false;
+ if (parent == nullptr) return false;
const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
- if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE)
- return false;
+ // Check that this specific input being spent is trusted
+ if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) return false;
+ // If we've already trusted this parent, continue
+ if (trusted_parents.count(parent->GetHash())) continue;
+ // Recurse to check that the parent is also trusted
+ if (!parent->IsTrusted(locked_chain, trusted_parents)) return false;
+ trusted_parents.insert(parent->GetHash());
}
return true;
}
@@ -1929,7 +1960,7 @@ void CWallet::ResendWalletTransactions()
// any confirmed or conflicting txs.
if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
std::string unused_err_string;
- if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true, *locked_chain)) ++submitted_tx_count;
+ if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true)) ++submitted_tx_count;
}
} // locked_chain and cs_wallet
@@ -1961,13 +1992,14 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) cons
{
auto locked_chain = chain().lock();
LOCK(cs_wallet);
+ std::set<uint256> trusted_parents;
for (const auto& entry : mapWallet)
{
const CWalletTx& wtx = entry.second;
- const bool is_trusted{wtx.IsTrusted(*locked_chain)};
- const int tx_depth{wtx.GetDepthInMainChain(*locked_chain)};
- const CAmount tx_credit_mine{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
- const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
+ const bool is_trusted{wtx.IsTrusted(*locked_chain, trusted_parents)};
+ const int tx_depth{wtx.GetDepthInMainChain()};
+ const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
+ const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
if (is_trusted && tx_depth >= min_depth) {
ret.m_mine_trusted += tx_credit_mine;
ret.m_watchonly_trusted += tx_credit_watchonly;
@@ -1976,8 +2008,8 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) cons
ret.m_mine_untrusted_pending += tx_credit_mine;
ret.m_watchonly_untrusted_pending += tx_credit_watchonly;
}
- ret.m_mine_immature += wtx.GetImmatureCredit(*locked_chain);
- ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit(*locked_chain);
+ ret.m_mine_immature += wtx.GetImmatureCredit();
+ ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit();
}
}
return ret;
@@ -2011,6 +2043,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
const int min_depth = {coinControl ? coinControl->m_min_depth : DEFAULT_MIN_DEPTH};
const int max_depth = {coinControl ? coinControl->m_max_depth : DEFAULT_MAX_DEPTH};
+ std::set<uint256> trusted_parents;
for (const auto& entry : mapWallet)
{
const uint256& wtxid = entry.first;
@@ -2020,10 +2053,10 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
continue;
}
- if (wtx.IsImmatureCoinBase(locked_chain))
+ if (wtx.IsImmatureCoinBase())
continue;
- int nDepth = wtx.GetDepthInMainChain(locked_chain);
+ int nDepth = wtx.GetDepthInMainChain();
if (nDepth < 0)
continue;
@@ -2032,7 +2065,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
if (nDepth == 0 && !wtx.InMempool())
continue;
- bool safeTx = wtx.IsTrusted(locked_chain);
+ bool safeTx = wtx.IsTrusted(locked_chain, trusted_parents);
// We should not consider coins from transactions that are replacing
// other transactions.
@@ -2083,7 +2116,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
if (IsLockedCoin(entry.first, i))
continue;
- if (IsSpent(locked_chain, wtxid, i))
+ if (IsSpent(wtxid, i))
continue;
isminetype mine = IsMine(wtx.tx->vout[i]);
@@ -2096,7 +2129,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
continue;
}
- const SigningProvider* provider = GetSigningProvider();
+ const SigningProvider* provider = GetSigningProvider(wtx.tx->vout[i].scriptPubKey);
bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false;
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
@@ -2131,7 +2164,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Ch
for (const COutput& coin : availableCoins) {
CTxDestination address;
- if (coin.fSpendable &&
+ if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) &&
ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) {
result[address].emplace_back(std::move(coin));
}
@@ -2139,12 +2172,16 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Ch
std::vector<COutPoint> lockedCoins;
ListLockedCoins(lockedCoins);
+ // Include watch-only for wallets without private keys
+ const bool include_watch_only = IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
for (const COutPoint& output : lockedCoins) {
auto it = mapWallet.find(output.hash);
if (it != mapWallet.end()) {
- int depth = it->second.GetDepthInMainChain(locked_chain);
+ int depth = it->second.GetDepthInMainChain();
if (depth >= 0 && output.n < it->second.tx->vout.size() &&
- IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) {
+ IsMine(it->second.tx->vout[output.n]) == is_mine_filter
+ ) {
CTxDestination address;
if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) {
result[address].emplace_back(
@@ -2205,7 +2242,11 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
if (effective_value > 0) {
group.fee += coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
group.long_term_fee += coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
- group.effective_value += effective_value;
+ if (coin_selection_params.m_subtract_fee_outputs) {
+ group.effective_value += coin.txout.nValue;
+ } else {
+ group.effective_value += effective_value;
+ }
++it;
} else {
it = group.Discard(coin);
@@ -2231,6 +2272,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const
{
std::vector<COutput> vCoins(vAvailableCoins);
+ CAmount value_to_select = nTargetValue;
// coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs)
@@ -2256,22 +2298,33 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
coin_control.ListSelected(vPresetInputs);
for (const COutPoint& outpoint : vPresetInputs)
{
- // For now, don't use BnB if preset inputs are selected. TODO: Enable this later
- bnb_used = false;
- coin_selection_params.use_bnb = false;
-
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
if (it != mapWallet.end())
{
const CWalletTx& wtx = it->second;
// Clearly invalid input, fail
- if (wtx.tx->vout.size() <= outpoint.n)
+ if (wtx.tx->vout.size() <= outpoint.n) {
+ bnb_used = false;
return false;
+ }
// Just to calculate the marginal byte size
- nValueFromPresetInputs += wtx.tx->vout[outpoint.n].nValue;
- setPresetCoins.insert(CInputCoin(wtx.tx, outpoint.n));
- } else
+ CInputCoin coin(wtx.tx, outpoint.n, wtx.GetSpendSize(outpoint.n, false));
+ nValueFromPresetInputs += coin.txout.nValue;
+ if (coin.m_input_bytes <= 0) {
+ bnb_used = false;
+ return false; // Not solvable, can't estimate size for fee
+ }
+ coin.effective_value = coin.txout.nValue - coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
+ if (coin_selection_params.use_bnb) {
+ value_to_select -= coin.effective_value;
+ } else {
+ value_to_select -= coin.txout.nValue;
+ }
+ setPresetCoins.insert(coin);
+ } else {
+ bnb_used = false;
return false; // TODO: Allow non-wallet inputs
+ }
}
// remove preset inputs from vCoins
@@ -2300,14 +2353,14 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count);
bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
- bool res = nTargetValue <= nValueFromPresetInputs ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 6, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 1, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ bool res = value_to_select <= 0 ||
+ SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
+ SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
util::insert(setCoinsRet, setPresetCoins);
@@ -2333,8 +2386,9 @@ bool CWallet::SignTransaction(CMutableTransaction& tx)
const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
SignatureData sigdata;
- const SigningProvider* provider = GetSigningProvider();
+ const SigningProvider* provider = GetSigningProvider(scriptPubKey);
if (!provider) {
+ // We don't know about this scriptpbuKey;
return false;
}
@@ -2573,7 +2627,8 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// BnB selector is the only selector used when this is true.
// That should only happen on the first pass through the loop.
- coin_selection_params.use_bnb = nSubtractFeeFromAmount == 0; // If we are doing subtract fee from recipient, then don't use BnB
+ coin_selection_params.use_bnb = true;
+ coin_selection_params.m_subtract_fee_outputs = nSubtractFeeFromAmount != 0; // If we are doing subtract fee from recipient, don't use effective values
// Start with no fee and loop until there is enough fee
while (true)
{
@@ -2587,7 +2642,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
nValueToSelect += nFeeRet;
// vouts to the payees
- coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size)
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size)
+ }
for (const auto& recipient : vecSend)
{
CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
@@ -2604,7 +2661,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
}
}
// Include the fee cost for outputs. Note this is only used for BnB right now
- coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
+ }
if (IsDust(txout, chain().relayDustFee()))
{
@@ -2796,7 +2855,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
- const SigningProvider* provider = GetSigningProvider();
+ const SigningProvider* provider = GetSigningProvider(scriptPubKey);
if (!provider || !ProduceSignature(*provider, MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata))
{
strFailReason = _("Signing transaction failed").translated;
@@ -2882,7 +2941,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
}
std::string err_string;
- if (!wtx.SubmitMemoryPoolAndRelay(err_string, true, *locked_chain)) {
+ if (!wtx.SubmitMemoryPoolAndRelay(err_string, true)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
}
@@ -3094,17 +3153,18 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
{
LOCK(cs_wallet);
+ std::set<uint256> trusted_parents;
for (const auto& walletEntry : mapWallet)
{
const CWalletTx& wtx = walletEntry.second;
- if (!wtx.IsTrusted(locked_chain))
+ if (!wtx.IsTrusted(locked_chain, trusted_parents))
continue;
- if (wtx.IsImmatureCoinBase(locked_chain))
+ if (wtx.IsImmatureCoinBase())
continue;
- int nDepth = wtx.GetDepthInMainChain(locked_chain);
+ int nDepth = wtx.GetDepthInMainChain();
if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1))
continue;
@@ -3116,7 +3176,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr))
continue;
- CAmount n = IsSpent(locked_chain, walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
+ CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
if (!balances.count(addr))
balances[addr] = 0;
@@ -3431,20 +3491,20 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
return nTimeSmart;
}
-bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
+bool CWallet::AddDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key, const std::string &value)
{
if (boost::get<CNoDestination>(&dest))
return false;
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
- return WalletBatch(*database).WriteDestData(EncodeDestination(dest), key, value);
+ return batch.WriteDestData(EncodeDestination(dest), key, value);
}
-bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
+bool CWallet::EraseDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key)
{
if (!mapAddressBook[dest].destdata.erase(key))
return false;
- return WalletBatch(*database).EraseDestData(EncodeDestination(dest), key);
+ return batch.EraseDestData(EncodeDestination(dest), key);
}
void CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
@@ -3622,9 +3682,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
if (auto spk_man = walletInstance->m_spk_man.get()) {
- std::string error;
if (!spk_man->Upgrade(prev_version, error)) {
- chain.initError(error);
return nullptr;
}
}
@@ -3776,8 +3834,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
const Optional<int> tip_height = locked_chain->getHeight();
if (tip_height) {
walletInstance->m_last_block_processed = locked_chain->getBlockHash(*tip_height);
+ walletInstance->m_last_block_processed_height = *tip_height;
} else {
walletInstance->m_last_block_processed.SetNull();
+ walletInstance->m_last_block_processed_height = -1;
}
if (tip_height && *tip_height != rescan_height)
@@ -3879,7 +3939,7 @@ void CWallet::postInitProcess()
// Add wallet transactions that aren't already in a block to mempool
// Do this here as mempool requires genesis block to be loaded
- ReacceptWalletTransactions(*locked_chain);
+ ReacceptWalletTransactions();
// Update wallet transactions with current mempool transactions.
chain().requestMempoolTransactions(*this);
@@ -3905,38 +3965,28 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn)
m_pre_split = false;
}
-void CWalletTx::SetConf(Status status, const uint256& block_hash, int posInBlock)
-{
- // Update tx status
- m_confirm.status = status;
-
- // Update the tx's hashBlock
- m_confirm.hashBlock = block_hash;
-
- // set the position of the transaction in the block
- m_confirm.nIndex = posInBlock;
-}
-
-int CWalletTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const
+int CWalletTx::GetDepthInMainChain() const
{
+ assert(pwallet != nullptr);
+ AssertLockHeld(pwallet->cs_wallet);
if (isUnconfirmed() || isAbandoned()) return 0;
- return locked_chain.getBlockDepth(m_confirm.hashBlock) * (isConflicted() ? -1 : 1);
+ return (pwallet->GetLastBlockHeight() - m_confirm.block_height + 1) * (isConflicted() ? -1 : 1);
}
-int CWalletTx::GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const
+int CWalletTx::GetBlocksToMaturity() const
{
if (!IsCoinBase())
return 0;
- int chain_depth = GetDepthInMainChain(locked_chain);
+ int chain_depth = GetDepthInMainChain();
assert(chain_depth >= 0); // coinbase tx should not be conflicted
return std::max(0, (COINBASE_MATURITY+1) - chain_depth);
}
-bool CWalletTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const
+bool CWalletTx::IsImmatureCoinBase() const
{
// note GetBlocksToMaturity is 0 for non-coinbase tx
- return GetBlocksToMaturity(locked_chain) > 0;
+ return GetBlocksToMaturity() > 0;
}
std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const {
@@ -4003,12 +4053,17 @@ bool CWallet::Lock()
return true;
}
-ScriptPubKeyMan* CWallet::GetScriptPubKeyMan() const
+ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const CScript& script) const
+{
+ return m_spk_man.get();
+}
+
+const SigningProvider* CWallet::GetSigningProvider(const CScript& script) const
{
return m_spk_man.get();
}
-const SigningProvider* CWallet::GetSigningProvider() const
+const SigningProvider* CWallet::GetSigningProvider(const CScript& script, SignatureData& sigdata) const
{
return m_spk_man.get();
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 50c5bad16f..fce49ec56c 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -356,14 +356,17 @@ public:
ABANDONED
};
- /* Confirmation includes tx status and a pair of {block hash/tx index in block} at which tx has been confirmed.
- * This pair is both 0 if tx hasn't confirmed yet. Meaning of these fields changes with CONFLICTED state
- * where they instead point to block hash and index of the deepest conflicting tx.
+ /* Confirmation includes tx status and a triplet of {block height/block hash/tx index in block}
+ * at which tx has been confirmed. All three are set to 0 if tx is unconfirmed or abandoned.
+ * Meaning of these fields changes with CONFLICTED state where they instead point to block hash
+ * and block height of the deepest conflicting tx.
*/
struct Confirmation {
- Status status = UNCONFIRMED;
- uint256 hashBlock = uint256();
- int nIndex = 0;
+ Status status;
+ int block_height;
+ uint256 hashBlock;
+ int nIndex;
+ Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {}
};
Confirmation m_confirm;
@@ -406,7 +409,6 @@ public:
* compatibility (pre-commit 9ac63d6).
*/
if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) {
- m_confirm.hashBlock = uint256();
setAbandoned();
} else if (serializedIndex == -1) {
setConflicted();
@@ -447,14 +449,14 @@ public:
//! filter decides which addresses will count towards the debit
CAmount GetDebit(const isminefilter& filter) const;
- CAmount GetCredit(interfaces::Chain::Lock& locked_chain, const isminefilter& filter) const;
- CAmount GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true) const;
+ CAmount GetCredit(const isminefilter& filter) const;
+ CAmount GetImmatureCredit(bool fUseCache = true) const;
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
// annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
// having to resolve the issue of member access into incomplete type CWallet.
- CAmount GetAvailableCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
- CAmount GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache=true) const;
+ CAmount GetAvailableCredit(bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
+ CAmount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const;
CAmount GetChange() const;
// Get the marginal bytes if spending the specified output from this transaction
@@ -476,11 +478,12 @@ public:
bool InMempool() const;
bool IsTrusted(interfaces::Chain::Lock& locked_chain) const;
+ bool IsTrusted(interfaces::Chain::Lock& locked_chain, std::set<uint256>& trusted_parents) const;
int64_t GetTxTime() const;
// Pass this transaction to node for mempool insertion and relay to peers if flag set to true
- bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay, interfaces::Chain::Lock& locked_chain);
+ bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
@@ -490,38 +493,44 @@ public:
// in place.
std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
- void SetConf(Status status, const uint256& block_hash, int posInBlock);
-
/**
* Return depth of transaction in blockchain:
* <0 : conflicts with a transaction this deep in the blockchain
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
- int GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const;
- bool IsInMainChain(interfaces::Chain::Lock& locked_chain) const { return GetDepthInMainChain(locked_chain) > 0; }
+ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
+ // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
+ // resolve the issue of member access into incomplete type CWallet. Note
+ // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
+ // in place.
+ int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS;
+ bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
/**
* @return number of blocks to maturity for this transaction:
* 0 : is not a coinbase transaction, or is a mature coinbase transaction
* >0 : is a coinbase transaction which matures in this many blocks
*/
- int GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const;
+ int GetBlocksToMaturity() const;
bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; }
void setAbandoned()
{
m_confirm.status = CWalletTx::ABANDONED;
m_confirm.hashBlock = uint256();
+ m_confirm.block_height = 0;
m_confirm.nIndex = 0;
}
bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; }
void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; }
bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; }
void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; }
+ bool isConfirmed() const { return m_confirm.status == CWalletTx::CONFIRMED; }
void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
- bool IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const;
+ bool IsImmatureCoinBase() const;
};
class COutput
@@ -575,6 +584,8 @@ struct CoinSelectionParams
size_t change_spend_size = 0;
CFeeRate effective_fee = CFeeRate(0);
size_t tx_noinputs_size = 0;
+ //! Indicate that we are subtracting the fee from outputs
+ bool m_subtract_fee_outputs = false;
CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size) : use_bnb(use_bnb), change_output_size(change_output_size), change_spend_size(change_spend_size), effective_fee(effective_fee), tx_noinputs_size(tx_noinputs_size) {}
CoinSelectionParams() {}
@@ -641,10 +652,10 @@ private:
* Abandoned state should probably be more carefully tracked via different
* posInBlock signals or by checking mempool presence when necessary.
*/
- bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Status status, const uint256& block_hash, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
- void MarkConflicted(const uint256& hashBlock, const uint256& hashTx);
+ void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx);
/* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -653,7 +664,7 @@ private:
/* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
* Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */
- void SyncTransaction(const CTransactionRef& tx, CWalletTx::Status status, const uint256& block_hash, int posInBlock = 0, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::atomic<uint64_t> m_wallet_flags{0};
@@ -678,12 +689,18 @@ private:
* The following is used to keep track of how far behind the wallet is
* from the chain sync, and to allow clients to block on us being caught up.
*
- * Note that this is *not* how far we've processed, we may need some rescan
- * to have seen all transactions in the chain, but is only used to track
- * live BlockConnected callbacks.
+ * Processed hash is a pointer on node's tip and doesn't imply that the wallet
+ * has scanned sequentially all blocks up to this one.
*/
uint256 m_last_block_processed GUARDED_BY(cs_wallet);
+ /* Height of last block processed is used by wallet to know depth of transactions
+ * without relying on Chain interface beyond asynchronous updates. For safety, we
+ * initialize it to -1. Height is a pointer on node's tip and doesn't imply
+ * that the wallet has scanned sequentially all blocks up to this one.
+ */
+ int m_last_block_processed_height GUARDED_BY(cs_wallet) = -1;
+
public:
/*
* Main wallet lock.
@@ -793,12 +810,12 @@ public:
bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
- bool IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
// Whether this or any UTXO with the same CTxDestination has been spent.
bool IsUsedDestination(const CTxDestination& dst) const;
bool IsUsedDestination(const uint256& hash, unsigned int n) const;
- void SetUsedDestinationState(const uint256& hash, unsigned int n, bool used);
+ void SetUsedDestinationState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
@@ -823,9 +840,9 @@ public:
bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
//! Adds a destination data tuple to the store, and saves it to disk
- bool AddDestData(const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool AddDestData(WalletBatch& batch, const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Erases a destination data tuple in the store and on disk
- bool EraseDestData(const CTxDestination& dest, const std::string& key) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool EraseDestData(WalletBatch& batch, const CTxDestination& dest, const std::string& key) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Adds a destination data tuple to the store, without saving it to disk
void LoadDestData(const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Look up a destination data tuple in the store, return true if found false otherwise
@@ -854,8 +871,8 @@ public:
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
void LoadToWallet(CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void TransactionAddedToMempool(const CTransactionRef& tx) override;
- void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) override;
- void BlockDisconnected(const CBlock& block) override;
+ void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted, int height) override;
+ void BlockDisconnected(const CBlock& block, int height) override;
void UpdatedBlockTip() override;
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
@@ -876,7 +893,7 @@ public:
};
ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
- void ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions();
struct Balance {
CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
@@ -1055,7 +1072,7 @@ public:
bool TransactionCanBeAbandoned(const uint256& hashTx) const;
/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
- bool AbandonTransaction(interfaces::Chain::Lock& locked_chain, const uint256& hashTx);
+ bool AbandonTransaction(const uint256& hashTx);
/** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
@@ -1113,8 +1130,13 @@ public:
LogPrintf(("%s " + fmt).c_str(), GetDisplayName(), parameters...);
};
- ScriptPubKeyMan* GetScriptPubKeyMan() const;
- const SigningProvider* GetSigningProvider() const;
+ //! Get the ScriptPubKeyMan for a script
+ ScriptPubKeyMan* GetScriptPubKeyMan(const CScript& script) const;
+
+ //! Get the SigningProvider for a script
+ const SigningProvider* GetSigningProvider(const CScript& script) const;
+ const SigningProvider* GetSigningProvider(const CScript& script, SignatureData& sigdata) const;
+
LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const;
// Temporary LegacyScriptPubKeyMan accessors and aliases.
@@ -1128,6 +1150,21 @@ public:
LegacyScriptPubKeyMan::WatchKeyMap& mapWatchKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapWatchKeys;
WalletBatch*& encrypted_batch GUARDED_BY(cs_wallet) = m_spk_man->encrypted_batch;
using CryptedKeyMap = LegacyScriptPubKeyMan::CryptedKeyMap;
+
+ /** Get last block processed height */
+ int GetLastBlockHeight() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
+ {
+ AssertLockHeld(cs_wallet);
+ assert(m_last_block_processed_height >= 0);
+ return m_last_block_processed_height;
+ };
+ /** Set last block processed height, currently only use in unit test */
+ void SetLastBlockProcessed(int block_height, uint256 block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
+ {
+ AssertLockHeld(cs_wallet);
+ m_last_block_processed_height = block_height;
+ m_last_block_processed = block_hash;
+ };
};
/**
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 2ba7cdac36..7d04b04764 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -629,7 +629,7 @@ DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<u
}
else if ((*it) == hash) {
if(!EraseTx(hash)) {
- LogPrint(BCLog::DB, "Transaction was found for deletion but returned database error: %s\n", hash.GetHex());
+ LogPrint(BCLog::WALLETDB, "Transaction was found for deletion but returned database error: %s\n", hash.GetHex());
delerror = true;
}
vTxHashOut.push_back(hash);
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index ebbaf8683d..b3b97b6a2a 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -185,7 +185,7 @@ void CZMQNotificationInterface::BlockConnected(const std::shared_ptr<const CBloc
}
}
-void CZMQNotificationInterface::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock)
+void CZMQNotificationInterface::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected)
{
for (const CTransactionRef& ptx : pblock->vtx) {
// Do a normal notify for each transaction removed in block disconnection
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index 6be0554a65..8bf9b0ba47 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -27,7 +27,7 @@ protected:
// CValidationInterface
void TransactionAddedToMempool(const CTransactionRef& tx) override;
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override;
- void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected) override;
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
private: