aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am60
-rw-r--r--src/Makefile.bench.include8
-rw-r--r--src/Makefile.qttest.include4
-rw-r--r--src/Makefile.test.include10
-rw-r--r--src/addrman.cpp13
-rw-r--r--src/addrman.h3
-rw-r--r--src/bench/bench.cpp19
-rw-r--r--src/bench/bench_bitcoin.cpp26
-rw-r--r--src/bench/block_assemble.cpp75
-rw-r--r--src/bench/coin_selection.cpp4
-rw-r--r--src/bench/duplicate_inputs.cpp37
-rw-r--r--src/bench/poly1305.cpp41
-rw-r--r--src/bench/wallet_balance.cpp53
-rw-r--r--src/bitcoin-cli.cpp6
-rw-r--r--src/bitcoin-tx.cpp1
-rw-r--r--src/bitcoind.cpp3
-rw-r--r--src/blockfilter.cpp60
-rw-r--r--src/blockfilter.h18
-rw-r--r--src/chain.cpp7
-rw-r--r--src/chain.h4
-rw-r--r--src/checkpoints.cpp32
-rw-r--r--src/checkpoints.h27
-rw-r--r--src/coins.h4
-rw-r--r--src/compat/assumptions.h18
-rw-r--r--src/consensus/tx_check.cpp57
-rw-r--r--src/consensus/tx_check.h20
-rw-r--r--src/consensus/tx_verify.cpp49
-rw-r--r--src/consensus/tx_verify.h3
-rw-r--r--src/core_io.h6
-rw-r--r--src/core_read.cpp28
-rw-r--r--src/crypto/aes.cpp62
-rw-r--r--src/crypto/aes.h51
-rw-r--r--src/crypto/poly1305.cpp141
-rw-r--r--src/crypto/poly1305.h17
-rw-r--r--src/dummywallet.cpp32
-rw-r--r--src/fs.cpp2
-rw-r--r--src/httpserver.cpp23
-rw-r--r--src/httpserver.h2
-rw-r--r--src/index/base.cpp63
-rw-r--r--src/index/base.h21
-rw-r--r--src/index/blockfilterindex.cpp467
-rw-r--r--src/index/blockfilterindex.h94
-rw-r--r--src/init.cpp145
-rw-r--r--src/interfaces/chain.cpp142
-rw-r--r--src/interfaces/chain.h112
-rw-r--r--src/interfaces/node.cpp2
-rw-r--r--src/interfaces/node.h3
-rw-r--r--src/interfaces/wallet.cpp37
-rw-r--r--src/interfaces/wallet.h6
-rw-r--r--src/logging.cpp26
-rw-r--r--src/logging.h2
-rw-r--r--src/miner.cpp1
-rw-r--r--src/net.cpp11
-rw-r--r--src/net.h16
-rw-r--r--src/net_processing.cpp196
-rw-r--r--src/netaddress.cpp121
-rw-r--r--src/netaddress.h8
-rw-r--r--src/node/README.md22
-rw-r--r--src/node/coin.cpp22
-rw-r--r--src/node/coin.h22
-rw-r--r--src/node/psbt.cpp134
-rw-r--r--src/node/psbt.h43
-rw-r--r--src/node/transaction.cpp27
-rw-r--r--src/node/transaction.h15
-rw-r--r--src/policy/fees.cpp34
-rw-r--r--src/policy/fees.h4
-rw-r--r--src/policy/policy.cpp26
-rw-r--r--src/policy/policy.h24
-rw-r--r--src/policy/rbf.cpp11
-rw-r--r--src/policy/rbf.h6
-rw-r--r--src/policy/settings.cpp14
-rw-r--r--src/policy/settings.h35
-rw-r--r--src/primitives/transaction.h4
-rw-r--r--src/psbt.cpp43
-rw-r--r--src/psbt.h18
-rw-r--r--src/qt/addressbookpage.cpp2
-rw-r--r--src/qt/bitcoin.cpp3
-rw-r--r--src/qt/clientmodel.cpp1
-rw-r--r--src/qt/forms/receivecoinsdialog.ui10
-rw-r--r--src/qt/guiutil.cpp9
-rw-r--r--src/qt/guiutil.h5
-rw-r--r--src/qt/intro.cpp13
-rw-r--r--src/qt/intro.h5
-rw-r--r--src/qt/optionsdialog.cpp8
-rw-r--r--src/qt/optionsmodel.cpp5
-rw-r--r--src/qt/receivecoinsdialog.cpp6
-rw-r--r--src/qt/rpcconsole.cpp3
-rw-r--r--src/qt/sendcoinsdialog.cpp2
-rw-r--r--src/qt/signverifymessagedialog.cpp2
-rw-r--r--src/qt/test/addressbooktests.cpp4
-rw-r--r--src/qt/test/rpcnestedtests.cpp2
-rw-r--r--src/qt/test/wallettests.cpp4
-rw-r--r--src/qt/walletcontroller.cpp14
-rw-r--r--src/qt/walletframe.cpp24
-rw-r--r--src/qt/walletframe.h6
-rw-r--r--src/qt/walletmodel.cpp4
-rw-r--r--src/qt/walletmodeltransaction.cpp2
-rw-r--r--src/rpc/blockchain.cpp101
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/mining.cpp24
-rw-r--r--src/rpc/mining.h15
-rw-r--r--src/rpc/misc.cpp14
-rw-r--r--src/rpc/net.cpp1
-rw-r--r--src/rpc/rawtransaction.cpp607
-rw-r--r--src/rpc/rawtransaction.h22
-rw-r--r--src/rpc/rawtransaction_util.cpp283
-rw-r--r--src/rpc/rawtransaction_util.h32
-rw-r--r--src/rpc/server.cpp181
-rw-r--r--src/rpc/server.h73
-rw-r--r--src/rpc/util.cpp133
-rw-r--r--src/rpc/util.h42
-rw-r--r--src/script/descriptor.cpp2
-rw-r--r--src/script/ismine.h29
-rw-r--r--src/script/script.h9
-rw-r--r--src/script/sign.cpp8
-rw-r--r--src/script/sign.h2
-rw-r--r--src/script/standard.h3
-rw-r--r--src/serialize.h11
-rw-r--r--src/sync.cpp29
-rw-r--r--src/test/README.md2
-rw-r--r--src/test/addrman_tests.cpp4
-rw-r--r--src/test/allocator_tests.cpp4
-rw-r--r--src/test/amount_tests.cpp4
-rw-r--r--src/test/arith_uint256_tests.cpp4
-rw-r--r--src/test/base32_tests.cpp4
-rw-r--r--src/test/base58_tests.cpp4
-rw-r--r--src/test/base64_tests.cpp4
-rw-r--r--src/test/bech32_tests.cpp2
-rw-r--r--src/test/bip32_tests.cpp4
-rw-r--r--src/test/blockchain_tests.cpp2
-rw-r--r--src/test/blockencodings_tests.cpp5
-rw-r--r--src/test/blockfilter_index_tests.cpp307
-rw-r--r--src/test/blockfilter_tests.cpp35
-rw-r--r--src/test/bloom_tests.cpp4
-rw-r--r--src/test/bswap_tests.cpp4
-rw-r--r--src/test/checkqueue_tests.cpp5
-rw-r--r--src/test/coins_tests.cpp4
-rw-r--r--src/test/compress_tests.cpp4
-rw-r--r--src/test/crypto_tests.cpp175
-rw-r--r--src/test/cuckoocache_tests.cpp4
-rw-r--r--src/test/data/blockfilters.json10
-rw-r--r--src/test/dbwrapper_tests.cpp4
-rw-r--r--src/test/denialofservice_tests.cpp4
-rw-r--r--src/test/descriptor_tests.cpp8
-rw-r--r--src/test/flatfile_tests.cpp2
-rw-r--r--src/test/fs_tests.cpp4
-rw-r--r--src/test/fuzz/deserialize.cpp2
-rw-r--r--src/test/getarg_tests.cpp4
-rw-r--r--src/test/hash_tests.cpp4
-rw-r--r--src/test/key_io_tests.cpp4
-rw-r--r--src/test/key_properties.cpp4
-rw-r--r--src/test/key_tests.cpp4
-rw-r--r--src/test/limitedmap_tests.cpp4
-rw-r--r--src/test/mempool_tests.cpp4
-rw-r--r--src/test/merkle_tests.cpp4
-rw-r--r--src/test/merkleblock_tests.cpp4
-rw-r--r--src/test/miner_tests.cpp4
-rw-r--r--src/test/multisig_tests.cpp4
-rw-r--r--src/test/net_tests.cpp4
-rw-r--r--src/test/netbase_tests.cpp4
-rw-r--r--src/test/pmt_tests.cpp4
-rw-r--r--src/test/policyestimator_tests.cpp4
-rw-r--r--src/test/pow_tests.cpp4
-rw-r--r--src/test/prevector_tests.cpp4
-rw-r--r--src/test/raii_event_tests.cpp4
-rw-r--r--src/test/random_tests.cpp4
-rw-r--r--src/test/reverselock_tests.cpp4
-rw-r--r--src/test/rpc_tests.cpp9
-rw-r--r--src/test/sanity_tests.cpp4
-rw-r--r--src/test/scheduler_tests.cpp4
-rw-r--r--src/test/script_p2sh_tests.cpp5
-rw-r--r--src/test/script_standard_tests.cpp4
-rw-r--r--src/test/script_tests.cpp6
-rw-r--r--src/test/scriptnum_tests.cpp4
-rw-r--r--src/test/serialize_tests.cpp4
-rw-r--r--src/test/setup_common.cpp (renamed from src/test/test_bitcoin.cpp)23
-rw-r--r--src/test/setup_common.h (renamed from src/test/test_bitcoin.h)6
-rw-r--r--src/test/sighash_tests.cpp6
-rw-r--r--src/test/sigopcount_tests.cpp4
-rw-r--r--src/test/skiplist_tests.cpp50
-rw-r--r--src/test/streams_tests.cpp4
-rw-r--r--src/test/sync_tests.cpp4
-rw-r--r--src/test/timedata_tests.cpp4
-rw-r--r--src/test/torcontrol_tests.cpp5
-rw-r--r--src/test/transaction_tests.cpp7
-rw-r--r--src/test/txindex_tests.cpp4
-rw-r--r--src/test/txvalidation_tests.cpp4
-rw-r--r--src/test/txvalidationcache_tests.cpp4
-rw-r--r--src/test/uint256_tests.cpp4
-rw-r--r--src/test/util.cpp91
-rw-r--r--src/test/util.h38
-rw-r--r--src/test/util_tests.cpp277
-rw-r--r--src/test/util_threadnames_tests.cpp73
-rw-r--r--src/test/validation_block_tests.cpp4
-rw-r--r--src/test/validation_tests.cpp2
-rw-r--r--src/test/versionbits_tests.cpp4
-rw-r--r--src/txdb.h2
-rw-r--r--src/txmempool.cpp1
-rw-r--r--src/ui_interface.cpp14
-rw-r--r--src/ui_interface.h7
-rw-r--r--src/util/error.cpp43
-rw-r--r--src/util/error.h38
-rw-r--r--src/util/fees.cpp42
-rw-r--r--src/util/fees.h16
-rw-r--r--src/util/rbf.cpp17
-rw-r--r--src/util/rbf.h18
-rw-r--r--src/util/strencodings.h12
-rw-r--r--src/util/system.cpp20
-rw-r--r--src/util/system.h6
-rw-r--r--src/util/threadnames.cpp57
-rw-r--r--src/util/threadnames.h21
-rw-r--r--src/util/url.cpp21
-rw-r--r--src/util/url.h12
-rw-r--r--src/util/validation.cpp20
-rw-r--r--src/util/validation.h18
-rw-r--r--src/validation.cpp377
-rw-r--r--src/validation.h26
-rw-r--r--src/validationinterface.cpp7
-rw-r--r--src/validationinterface.h4
-rw-r--r--src/wallet/coincontrol.h2
-rw-r--r--src/wallet/db.cpp8
-rw-r--r--src/wallet/db.h3
-rw-r--r--src/wallet/feebumper.cpp171
-rw-r--r--src/wallet/feebumper.h13
-rw-r--r--src/wallet/fees.cpp9
-rw-r--r--src/wallet/init.cpp107
-rw-r--r--src/wallet/load.cpp112
-rw-r--r--src/wallet/load.h38
-rw-r--r--src/wallet/rpcdump.cpp46
-rw-r--r--src/wallet/rpcwallet.cpp169
-rw-r--r--src/wallet/rpcwallet.h9
-rw-r--r--src/wallet/test/coinselector_tests.cpp10
-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.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.cpp14
-rw-r--r--src/wallet/test/wallet_test_fixture.h4
-rw-r--r--src/wallet/test/wallet_tests.cpp30
-rw-r--r--src/wallet/wallet.cpp725
-rw-r--r--src/wallet/wallet.h152
-rw-r--r--src/wallet/walletdb.cpp13
-rw-r--r--src/wallet/wallettool.cpp7
-rw-r--r--src/wallet/walletutil.cpp15
245 files changed, 5603 insertions, 2980 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 14db963253..ed5cab7f04 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -113,7 +113,6 @@ BITCOIN_CORE_H = \
chainparams.h \
chainparamsbase.h \
chainparamsseeds.h \
- checkpoints.h \
checkqueue.h \
clientversion.h \
coins.h \
@@ -124,6 +123,7 @@ BITCOIN_CORE_H = \
compat/sanity.h \
compressor.h \
consensus/consensus.h \
+ consensus/tx_check.h \
consensus/tx_verify.h \
core_io.h \
core_memusage.h \
@@ -133,6 +133,7 @@ BITCOIN_CORE_H = \
httprpc.h \
httpserver.h \
index/base.h \
+ index/blockfilterindex.h \
index/txindex.h \
indirectmap.h \
init.h \
@@ -154,6 +155,8 @@ BITCOIN_CORE_H = \
netaddress.h \
netbase.h \
netmessagemaker.h \
+ node/coin.h \
+ node/psbt.h \
node/transaction.h \
noui.h \
optional.h \
@@ -162,6 +165,7 @@ BITCOIN_CORE_H = \
policy/fees.h \
policy/policy.h \
policy/rbf.h \
+ policy/settings.h \
pow.h \
protocol.h \
psbt.h \
@@ -170,10 +174,9 @@ BITCOIN_CORE_H = \
reverselock.h \
rpc/blockchain.h \
rpc/client.h \
- rpc/mining.h \
rpc/protocol.h \
rpc/server.h \
- rpc/rawtransaction.h \
+ rpc/rawtransaction_util.h \
rpc/register.h \
rpc/util.h \
scheduler.h \
@@ -200,10 +203,16 @@ BITCOIN_CORE_H = \
undo.h \
util/bip32.h \
util/bytevectorhash.h \
+ util/error.h \
+ util/fees.h \
util/system.h \
util/memory.h \
util/moneystr.h \
+ util/rbf.h \
+ util/threadnames.h \
util/time.h \
+ util/url.h \
+ util/validation.h \
validation.h \
validationinterface.h \
versionbits.h \
@@ -214,6 +223,7 @@ BITCOIN_CORE_H = \
wallet/db.h \
wallet/feebumper.h \
wallet/fees.h \
+ wallet/load.h \
wallet/psbtwallet.h \
wallet/rpcwallet.h \
wallet/wallet.h \
@@ -236,38 +246,39 @@ obj/build.h: FORCE
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
# server: shared between bitcoind and bitcoin-qt
+# Contains code accessing mempool and chain state that is meant to be separated
+# from wallet and gui code (see node/README.md). Shared code should go in
+# libbitcoin_common or libbitcoin_util libraries, instead.
libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrdb.cpp \
addrman.cpp \
banman.cpp \
- bloom.cpp \
blockencodings.cpp \
blockfilter.cpp \
chain.cpp \
- checkpoints.cpp \
consensus/tx_verify.cpp \
flatfile.cpp \
httprpc.cpp \
httpserver.cpp \
index/base.cpp \
+ index/blockfilterindex.cpp \
index/txindex.cpp \
interfaces/chain.cpp \
- interfaces/handler.cpp \
interfaces/node.cpp \
init.cpp \
dbwrapper.cpp \
- merkleblock.cpp \
miner.cpp \
net.cpp \
net_processing.cpp \
+ node/coin.cpp \
+ node/psbt.cpp \
node/transaction.cpp \
noui.cpp \
- outputtype.cpp \
policy/fees.cpp \
- policy/policy.cpp \
policy/rbf.cpp \
+ policy/settings.cpp \
pow.cpp \
rest.cpp \
rpc/blockchain.cpp \
@@ -276,7 +287,6 @@ libbitcoin_server_a_SOURCES = \
rpc/net.cpp \
rpc/rawtransaction.cpp \
rpc/server.cpp \
- rpc/util.cpp \
script/sigcache.cpp \
shutdown.cpp \
timedata.cpp \
@@ -289,6 +299,9 @@ libbitcoin_server_a_SOURCES = \
versionbits.cpp \
$(BITCOIN_CORE_H)
+if ENABLE_WALLET
+libbitcoin_server_a_SOURCES += wallet/init.cpp
+endif
if !ENABLE_WALLET
libbitcoin_server_a_SOURCES += dummywallet.cpp
endif
@@ -315,7 +328,7 @@ libbitcoin_wallet_a_SOURCES = \
wallet/db.cpp \
wallet/feebumper.cpp \
wallet/fees.cpp \
- wallet/init.cpp \
+ wallet/load.cpp \
wallet/psbtwallet.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
@@ -344,6 +357,8 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/hmac_sha256.h \
crypto/hmac_sha512.cpp \
crypto/hmac_sha512.h \
+ crypto/poly1305.h \
+ crypto/poly1305.cpp \
crypto/ripemd160.cpp \
crypto/ripemd160.h \
crypto/sha1.cpp \
@@ -387,6 +402,7 @@ libbitcoin_consensus_a_SOURCES = \
consensus/merkle.cpp \
consensus/merkle.h \
consensus/params.h \
+ consensus/tx_check.cpp \
consensus/validation.h \
hash.cpp \
hash.h \
@@ -419,6 +435,7 @@ libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_common_a_SOURCES = \
base58.cpp \
bech32.cpp \
+ bloom.cpp \
chainparams.cpp \
coins.cpp \
compressor.cpp \
@@ -427,11 +444,16 @@ libbitcoin_common_a_SOURCES = \
key.cpp \
key_io.cpp \
keystore.cpp \
+ merkleblock.cpp \
netaddress.cpp \
netbase.cpp \
+ outputtype.cpp \
policy/feerate.cpp \
- psbt.cpp \
+ policy/policy.cpp \
protocol.cpp \
+ psbt.cpp \
+ rpc/rawtransaction_util.cpp \
+ rpc/util.cpp \
scheduler.cpp \
script/descriptor.cpp \
script/ismine.cpp \
@@ -454,6 +476,7 @@ libbitcoin_util_a_SOURCES = \
compat/glibcxx_sanity.cpp \
compat/strnlen.cpp \
fs.cpp \
+ interfaces/handler.cpp \
logging.cpp \
random.cpp \
rpc/protocol.cpp \
@@ -462,10 +485,16 @@ libbitcoin_util_a_SOURCES = \
threadinterrupt.cpp \
util/bip32.cpp \
util/bytevectorhash.cpp \
+ util/error.cpp \
+ util/fees.cpp \
util/system.cpp \
util/moneystr.cpp \
+ util/rbf.cpp \
+ util/threadnames.cpp \
util/strencodings.cpp \
util/time.cpp \
+ util/url.cpp \
+ util/validation.cpp \
$(BITCOIN_CORE_H)
if GLIBC_BACK_COMPAT
@@ -493,6 +522,8 @@ if TARGET_WINDOWS
bitcoind_SOURCES += bitcoind-res.rc
endif
+# Libraries below may be listed more than once to resolve circular dependencies (see
+# https://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking#circular-dependency)
bitcoind_LDADD = \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_WALLET) \
@@ -563,7 +594,6 @@ endif
bitcoin_wallet_LDADD = \
$(LIBBITCOIN_WALLET_TOOL) \
$(LIBBITCOIN_WALLET) \
- $(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_UTIL) \
@@ -642,13 +672,13 @@ clean-local:
check-symbols: $(bin_PROGRAMS)
if GLIBC_BACK_COMPAT
@echo "Checking glibc back compat..."
- $(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py < $(bin_PROGRAMS)
+ $(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py < $(bin_PROGRAMS)
endif
check-security: $(bin_PROGRAMS)
if HARDEN
@echo "Checking binary security..."
- $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS)
+ $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS)
endif
if ENABLE_BIP70
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 3042f6df19..ae7eb19ceb 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -31,7 +31,12 @@ bench_bench_bitcoin_SOURCES = \
bench/base58.cpp \
bench/bech32.cpp \
bench/lockedpool.cpp \
- bench/prevector.cpp
+ bench/poly1305.cpp \
+ bench/prevector.cpp \
+ test/setup_common.h \
+ test/setup_common.cpp \
+ test/util.h \
+ test/util.cpp
nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES)
@@ -59,6 +64,7 @@ endif
if ENABLE_WALLET
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
+bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
endif
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS)
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 61977b50cd..4acfff809e 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -33,10 +33,10 @@ TEST_QT_H = \
qt/test/wallettests.h
TEST_BITCOIN_CPP = \
- test/test_bitcoin.cpp
+ test/setup_common.cpp
TEST_BITCOIN_H = \
- test/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) $(PROTOBUF_CFLAGS)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index a6e0785616..1144ca8a78 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -52,12 +52,12 @@ GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.r
BITCOIN_TEST_SUITE = \
test/main.cpp \
- test/test_bitcoin.h \
- test/test_bitcoin.cpp
+ test/setup_common.h \
+ test/setup_common.cpp
FUZZ_SUITE = \
- test/test_bitcoin.h \
- test/test_bitcoin.cpp \
+ test/setup_common.h \
+ test/setup_common.cpp \
test/fuzz/fuzz.cpp \
test/fuzz/fuzz.h
@@ -92,6 +92,7 @@ BITCOIN_TESTS =\
test/blockchain_tests.cpp \
test/blockencodings_tests.cpp \
test/blockfilter_tests.cpp \
+ test/blockfilter_index_tests.cpp \
test/bloom_tests.cpp \
test/bswap_tests.cpp \
test/checkqueue_tests.cpp \
@@ -137,6 +138,7 @@ BITCOIN_TESTS =\
test/skiplist_tests.cpp \
test/streams_tests.cpp \
test/sync_tests.cpp \
+ test/util_threadnames_tests.cpp \
test/timedata_tests.cpp \
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 06c342ba73..8a5f78d1c5 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -239,7 +239,9 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
// Will moving this address into tried evict another entry?
if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
- LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table, moving %s to m_tried_collisions=%d\n", addr.ToString(), m_tried_collisions.size());
+ // Output the entry we'd be colliding with, for debugging purposes
+ auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
+ LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table (%s), moving %s to m_tried_collisions=%d\n", colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() : "", addr.ToString(), m_tried_collisions.size());
if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
m_tried_collisions.insert(nId);
}
@@ -561,12 +563,19 @@ void CAddrMan::ResolveCollisions_()
// Give address at least 60 seconds to successfully connect
if (GetAdjustedTime() - info_old.nLastTry > 60) {
- LogPrint(BCLog::ADDRMAN, "Swapping %s for %s in tried table\n", info_new.ToString(), info_old.ToString());
+ LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToString(), info_new.ToString());
// Replaces an existing address already in the tried table with the new address
Good_(info_new, false, GetAdjustedTime());
erase_collision = true;
}
+ } else if (GetAdjustedTime() - info_new.nLastSuccess > ADDRMAN_TEST_WINDOW) {
+ // If the collision hasn't resolved in some reasonable amount of time,
+ // just evict the old entry -- we must not be able to
+ // connect to it for some reason.
+ LogPrint(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToString(), info_new.ToString());
+ Good_(info_new, false, GetAdjustedTime());
+ erase_collision = true;
}
} else { // Collision is not actually a collision anymore
Good_(info_new, false, GetAdjustedTime());
diff --git a/src/addrman.h b/src/addrman.h
index 003bd059f8..e54184ce35 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -166,6 +166,9 @@ public:
//! the maximum number of tried addr collisions to store
#define ADDRMAN_SET_TRIED_COLLISION_SIZE 10
+//! the maximum time we'll spend trying to resolve a tried table collision, in seconds
+static const int64_t ADDRMAN_TEST_WINDOW = 40*60; // 40 minutes
+
/**
* Stochastical (IP) address manager
*/
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 966b99f6c8..b08ecbb621 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -1,15 +1,19 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-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 <bench/bench.h>
+#include <chainparams.h>
+#include <test/setup_common.h>
+#include <validation.h>
+
+#include <algorithm>
#include <assert.h>
-#include <iostream>
#include <iomanip>
-#include <algorithm>
-#include <regex>
+#include <iostream>
#include <numeric>
+#include <regex>
void benchmark::ConsolePrinter::header()
{
@@ -108,6 +112,13 @@ void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double
printer.header();
for (const auto& p : benchmarks()) {
+ TestingSetup test{CBaseChainParams::REGTEST};
+ {
+ assert(::chainActive.Height() == 0);
+ const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), Params().GetConsensus())};
+ assert(witness_enabled);
+ }
+
if (!std::regex_match(p.first, baseMatch, reFilter)) {
continue;
}
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index d67b2c5bc7..3cf0bf9530 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-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.
@@ -6,14 +6,11 @@
#include <crypto/sha256.h>
#include <key.h>
-#include <util/system.h>
#include <util/strencodings.h>
-#include <validation.h>
+#include <util/system.h>
#include <memory>
-const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
-
static const int64_t DEFAULT_BENCH_EVALUATIONS = 5;
static const char* DEFAULT_BENCH_FILTER = ".*";
static const char* DEFAULT_BENCH_SCALING = "1.0";
@@ -36,14 +33,6 @@ static void SetupBenchArgs()
gArgs.AddArg("-plot-height=<x>", strprintf("Plot height in pixel (default: %u)", DEFAULT_PLOT_HEIGHT), false, OptionsCategory::OPTIONS);
}
-static fs::path SetDataDir()
-{
- fs::path ret = fs::temp_directory_path() / "bench_bitcoin" / fs::unique_path();
- fs::create_directories(ret);
- gArgs.ForceSetArg("-datadir", ret.string());
- return ret;
-}
-
int main(int argc, char** argv)
{
SetupBenchArgs();
@@ -59,13 +48,6 @@ int main(int argc, char** argv)
return EXIT_SUCCESS;
}
- // Set the datadir after parsing the bench options
- const fs::path bench_datadir{SetDataDir()};
-
- SHA256AutoDetect();
- ECC_Start();
- SetupEnvironment();
-
int64_t evaluations = gArgs.GetArg("-evals", DEFAULT_BENCH_EVALUATIONS);
std::string regex_filter = gArgs.GetArg("-filter", DEFAULT_BENCH_FILTER);
std::string scaling_str = gArgs.GetArg("-scaling", DEFAULT_BENCH_SCALING);
@@ -88,9 +70,5 @@ int main(int argc, char** argv)
benchmark::BenchRunner::RunAll(*printer, evaluations, scaling_factor, regex_filter, is_list_only);
- fs::remove_all(bench_datadir);
-
- ECC_Stop();
-
return EXIT_SUCCESS;
}
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index cd4543c012..fb33c09ab2 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -1,58 +1,18 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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 <bench/bench.h>
-#include <chainparams.h>
-#include <coins.h>
-#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
-#include <miner.h>
-#include <policy/policy.h>
-#include <pow.h>
-#include <scheduler.h>
-#include <txdb.h>
+#include <test/util.h>
#include <txmempool.h>
-#include <util/time.h>
#include <validation.h>
-#include <validationinterface.h>
-#include <boost/thread.hpp>
#include <list>
#include <vector>
-static std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey)
-{
- auto block = std::make_shared<CBlock>(
- BlockAssembler{Params()}
- .CreateNewBlock(coinbase_scriptPubKey)
- ->block);
-
- block->nTime = ::chainActive.Tip()->GetMedianTimePast() + 1;
- block->hashMerkleRoot = BlockMerkleRoot(*block);
-
- return block;
-}
-
-
-static CTxIn MineBlock(const CScript& coinbase_scriptPubKey)
-{
- auto block = PrepareBlock(coinbase_scriptPubKey);
-
- while (!CheckProofOfWork(block->GetHash(), block->nBits, Params().GetConsensus())) {
- ++block->nNonce;
- assert(block->nNonce);
- }
-
- bool processed{ProcessNewBlock(Params(), block, true, nullptr)};
- assert(processed);
-
- return CTxIn{block->vtx[0]->GetHash(), 0};
-}
-
-
static void AssembleBlock(benchmark::State& state)
{
const std::vector<unsigned char> op_true{OP_TRUE};
@@ -64,32 +24,6 @@ static void AssembleBlock(benchmark::State& state)
const CScript SCRIPT_PUB{CScript(OP_0) << std::vector<unsigned char>{witness_program.begin(), witness_program.end()}};
- // Switch to regtest so we can mine faster
- // Also segwit is active, so we can include witness transactions
- SelectParams(CBaseChainParams::REGTEST);
-
- InitScriptExecutionCache();
-
- boost::thread_group thread_group;
- CScheduler scheduler;
- {
- LOCK(cs_main);
- ::pblocktree.reset(new CBlockTreeDB(1 << 20, true));
- ::pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
- ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
- }
- {
- const CChainParams& chainparams = Params();
- thread_group.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
- GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
- LoadGenesisBlock(chainparams);
- CValidationState state;
- ActivateBestChain(state, chainparams);
- assert(::chainActive.Tip() != nullptr);
- const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), chainparams.GetConsensus())};
- assert(witness_enabled);
- }
-
// Collect some loose transactions that spend the coinbases of our mined blocks
constexpr size_t NUM_BLOCKS{200};
std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs;
@@ -114,11 +48,6 @@ static void AssembleBlock(benchmark::State& state)
while (state.KeepRunning()) {
PrepareBlock(SCRIPT_PUB);
}
-
- thread_group.interrupt_all();
- thread_group.join_all();
- GetMainSignals().FlushBackgroundCallbacks();
- GetMainSignals().UnregisterBackgroundSignalScheduler();
}
BENCHMARK(AssembleBlock, 700);
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 74641191a1..f2ab03e20e 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -29,7 +29,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<st
static void CoinSelection(benchmark::State& state)
{
auto chain = interfaces::MakeChain();
- const CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ const CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
@@ -61,7 +61,7 @@ static void CoinSelection(benchmark::State& state)
typedef std::set<CInputCoin> CoinSet;
static auto testChain = interfaces::MakeChain();
-static const CWallet testWallet(*testChain, WalletLocation(), WalletDatabase::CreateDummy());
+static const CWallet testWallet(testChain.get(), WalletLocation(), WalletDatabase::CreateDummy());
std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
index 1f6840d813..2d7a351523 100644
--- a/src/bench/duplicate_inputs.cpp
+++ b/src/bench/duplicate_inputs.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -10,15 +10,11 @@
#include <miner.h>
#include <policy/policy.h>
#include <pow.h>
-#include <scheduler.h>
-#include <txdb.h>
+#include <test/util.h>
#include <txmempool.h>
-#include <util/time.h>
#include <validation.h>
#include <validationinterface.h>
-#include <boost/thread.hpp>
-
#include <list>
#include <vector>
@@ -27,31 +23,7 @@ static void DuplicateInputs(benchmark::State& state)
{
const CScript SCRIPT_PUB{CScript(OP_TRUE)};
- // Switch to regtest so we can mine faster
- // Also segwit is active, so we can include witness transactions
- SelectParams(CBaseChainParams::REGTEST);
-
- InitScriptExecutionCache();
-
- boost::thread_group thread_group;
- CScheduler scheduler;
const CChainParams& chainparams = Params();
- {
- LOCK(cs_main);
- ::pblocktree.reset(new CBlockTreeDB(1 << 20, true));
- ::pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
- ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
- }
- {
- thread_group.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
- GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
- LoadGenesisBlock(chainparams);
- CValidationState cvstate;
- ActivateBestChain(cvstate, chainparams);
- assert(::chainActive.Tip() != nullptr);
- const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), chainparams.GetConsensus())};
- assert(witness_enabled);
- }
CBlock block{};
CMutableTransaction coinbaseTx{};
@@ -92,11 +64,6 @@ static void DuplicateInputs(benchmark::State& state)
assert(!CheckBlock(block, cvstate, chainparams.GetConsensus(), false, false));
assert(cvstate.GetRejectReason() == "bad-txns-inputs-duplicate");
}
-
- thread_group.interrupt_all();
- thread_group.join_all();
- GetMainSignals().FlushBackgroundCallbacks();
- GetMainSignals().UnregisterBackgroundSignalScheduler();
}
BENCHMARK(DuplicateInputs, 10);
diff --git a/src/bench/poly1305.cpp b/src/bench/poly1305.cpp
new file mode 100644
index 0000000000..16342d0fbe
--- /dev/null
+++ b/src/bench/poly1305.cpp
@@ -0,0 +1,41 @@
+// 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 <iostream>
+
+#include <bench/bench.h>
+#include <crypto/poly1305.h>
+
+/* Number of bytes to process per iteration */
+static constexpr uint64_t BUFFER_SIZE_TINY = 64;
+static constexpr uint64_t BUFFER_SIZE_SMALL = 256;
+static constexpr uint64_t BUFFER_SIZE_LARGE = 1024*1024;
+
+static void POLY1305(benchmark::State& state, size_t buffersize)
+{
+ std::vector<unsigned char> tag(POLY1305_TAGLEN, 0);
+ std::vector<unsigned char> key(POLY1305_KEYLEN, 0);
+ std::vector<unsigned char> in(buffersize, 0);
+ while (state.KeepRunning())
+ poly1305_auth(tag.data(), in.data(), in.size(), key.data());
+}
+
+static void POLY1305_64BYTES(benchmark::State& state)
+{
+ POLY1305(state, BUFFER_SIZE_TINY);
+}
+
+static void POLY1305_256BYTES(benchmark::State& state)
+{
+ POLY1305(state, BUFFER_SIZE_SMALL);
+}
+
+static void POLY1305_1MB(benchmark::State& state)
+{
+ POLY1305(state, BUFFER_SIZE_LARGE);
+}
+
+BENCHMARK(POLY1305_64BYTES, 500000);
+BENCHMARK(POLY1305_256BYTES, 250000);
+BENCHMARK(POLY1305_1MB, 340);
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
new file mode 100644
index 0000000000..46ca12826b
--- /dev/null
+++ b/src/bench/wallet_balance.cpp
@@ -0,0 +1,53 @@
+// Copyright (c) 2012-2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+#include <interfaces/chain.h>
+#include <key_io.h>
+#include <optional.h>
+#include <test/util.h>
+#include <validationinterface.h>
+#include <wallet/wallet.h>
+
+static void WalletBalance(benchmark::State& state, const bool set_dirty, const bool add_watchonly, const bool add_mine)
+{
+ const auto& ADDRESS_WATCHONLY = ADDRESS_BCRT1_UNSPENDABLE;
+
+ std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain();
+ CWallet wallet{chain.get(), WalletLocation(), WalletDatabase::CreateMock()};
+ {
+ bool first_run;
+ if (wallet.LoadWallet(first_run) != DBErrors::LOAD_OK) assert(false);
+ wallet.handleNotifications();
+ }
+
+
+ const Optional<std::string> address_mine{add_mine ? Optional<std::string>{getnewaddress(wallet)} : nullopt};
+ if (add_watchonly) importaddress(wallet, ADDRESS_WATCHONLY);
+
+ for (int i = 0; i < 100; ++i) {
+ generatetoaddress(address_mine.get_value_or(ADDRESS_WATCHONLY));
+ generatetoaddress(ADDRESS_WATCHONLY);
+ }
+ SyncWithValidationInterfaceQueue();
+
+ auto bal = wallet.GetBalance(); // Cache
+
+ while (state.KeepRunning()) {
+ if (set_dirty) wallet.MarkDirty();
+ bal = wallet.GetBalance();
+ if (add_mine) assert(bal.m_mine_trusted > 0);
+ if (add_watchonly) assert(bal.m_watchonly_trusted > 0);
+ }
+}
+
+static void WalletBalanceDirty(benchmark::State& state) { WalletBalance(state, /* set_dirty */ true, /* add_watchonly */ true, /* add_mine */ true); }
+static void WalletBalanceClean(benchmark::State& state) { WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ true); }
+static void WalletBalanceMine(benchmark::State& state) { WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ false, /* add_mine */ true); }
+static void WalletBalanceWatch(benchmark::State& state) { WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ false); }
+
+BENCHMARK(WalletBalanceDirty, 2500);
+BENCHMARK(WalletBalanceClean, 8000);
+BENCHMARK(WalletBalanceMine, 16000);
+BENCHMARK(WalletBalanceWatch, 8000);
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index b0e1f67d93..1009a771f8 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -253,16 +253,12 @@ public:
}
result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]);
result.pushKV("protocolversion", batch[ID_NETWORKINFO]["result"]["protocolversion"]);
- if (!batch[ID_WALLETINFO].isNull()) {
- result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]);
- result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]);
- }
result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]);
result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]);
result.pushKV("connections", batch[ID_NETWORKINFO]["result"]["connections"]);
result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]);
result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
- result.pushKV("testnet", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"].get_str() == "test"));
+ result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
if (!batch[ID_WALLETINFO].isNull()) {
result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]);
result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]);
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 4be89aab6c..7f41ea7aed 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -18,6 +18,7 @@
#include <script/script.h>
#include <script/sign.h>
#include <univalue.h>
+#include <util/rbf.h>
#include <util/system.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index dde75c1b12..b31f86cdd9 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -19,6 +19,7 @@
#include <util/system.h>
#include <httpserver.h>
#include <httprpc.h>
+#include <util/threadnames.h>
#include <util/strencodings.h>
#include <walletinitinterface.h>
@@ -64,6 +65,8 @@ static bool AppInit(int argc, char* argv[])
bool fRet = false;
+ util::ThreadRename("init");
+
//
// Parameters
//
diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp
index bcf24047ff..787390be31 100644
--- a/src/blockfilter.cpp
+++ b/src/blockfilter.cpp
@@ -2,6 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <mutex>
+#include <sstream>
+
#include <blockfilter.h>
#include <crypto/siphash.h>
#include <hash.h>
@@ -15,6 +18,10 @@ static constexpr int GCS_SER_TYPE = SER_NETWORK;
/// Protocol version used to serialize parameters in GCS filter encoding.
static constexpr int GCS_SER_VERSION = 0;
+static const std::map<BlockFilterType, std::string> g_filter_types = {
+ {BlockFilterType::BASIC, "basic"},
+};
+
template <typename OStream>
static void GolombRiceEncode(BitStreamWriter<OStream>& bitwriter, uint8_t P, uint64_t x)
{
@@ -197,6 +204,57 @@ bool GCSFilter::MatchAny(const ElementSet& elements) const
return MatchInternal(queries.data(), queries.size());
}
+const std::string& BlockFilterTypeName(BlockFilterType filter_type)
+{
+ static std::string unknown_retval = "";
+ auto it = g_filter_types.find(filter_type);
+ return it != g_filter_types.end() ? it->second : unknown_retval;
+}
+
+bool BlockFilterTypeByName(const std::string& name, BlockFilterType& filter_type) {
+ for (const auto& entry : g_filter_types) {
+ if (entry.second == name) {
+ filter_type = entry.first;
+ return true;
+ }
+ }
+ return false;
+}
+
+const std::vector<BlockFilterType>& AllBlockFilterTypes()
+{
+ static std::vector<BlockFilterType> types;
+
+ static std::once_flag flag;
+ std::call_once(flag, []() {
+ types.reserve(g_filter_types.size());
+ for (auto entry : g_filter_types) {
+ types.push_back(entry.first);
+ }
+ });
+
+ return types;
+}
+
+const std::string& ListBlockFilterTypes()
+{
+ static std::string type_list;
+
+ static std::once_flag flag;
+ std::call_once(flag, []() {
+ std::stringstream ret;
+ bool first = true;
+ for (auto entry : g_filter_types) {
+ if (!first) ret << ", ";
+ ret << entry.second;
+ first = false;
+ }
+ type_list = ret.str();
+ });
+
+ return type_list;
+}
+
static GCSFilter::ElementSet BasicFilterElements(const CBlock& block,
const CBlockUndo& block_undo)
{
@@ -251,6 +309,8 @@ bool BlockFilter::BuildParams(GCSFilter::Params& params) const
params.m_P = BASIC_FILTER_P;
params.m_M = BASIC_FILTER_M;
return true;
+ case BlockFilterType::INVALID:
+ return false;
}
return false;
diff --git a/src/blockfilter.h b/src/blockfilter.h
index 4d1f51dd60..914b94fec1 100644
--- a/src/blockfilter.h
+++ b/src/blockfilter.h
@@ -6,6 +6,7 @@
#define BITCOIN_BLOCKFILTER_H
#include <stdint.h>
+#include <string>
#include <unordered_set>
#include <vector>
@@ -83,11 +84,24 @@ public:
constexpr uint8_t BASIC_FILTER_P = 19;
constexpr uint32_t BASIC_FILTER_M = 784931;
-enum BlockFilterType : uint8_t
+enum class BlockFilterType : uint8_t
{
BASIC = 0,
+ INVALID = 255,
};
+/** Get the human-readable name for a filter type. Returns empty string for unknown types. */
+const std::string& BlockFilterTypeName(BlockFilterType filter_type);
+
+/** Find a filter type by its human-readable name. */
+bool BlockFilterTypeByName(const std::string& name, BlockFilterType& filter_type);
+
+/** Get a list of known filter types. */
+const std::vector<BlockFilterType>& AllBlockFilterTypes();
+
+/** Get a comma-separated list of known filter type names. */
+const std::string& ListBlockFilterTypes();
+
/**
* Complete block filter struct as defined in BIP 157. Serialization matches
* payload of "cfilter" messages.
@@ -95,7 +109,7 @@ enum BlockFilterType : uint8_t
class BlockFilter
{
private:
- BlockFilterType m_filter_type;
+ BlockFilterType m_filter_type = BlockFilterType::INVALID;
uint256 m_block_hash;
GCSFilter m_filter;
diff --git a/src/chain.cpp b/src/chain.cpp
index d462f94ab5..5520d8149a 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -59,10 +59,11 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
return pindex;
}
-CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const
+CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime, int height) const
{
- std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime,
- [](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTimeMax() < time; });
+ std::pair<int64_t, int> blockparams = std::make_pair(nTime, height);
+ std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), blockparams,
+ [](CBlockIndex* pBlock, const std::pair<int64_t, int>& blockparams) -> bool { return pBlock->GetBlockTimeMax() < blockparams.first || pBlock->nHeight < blockparams.second; });
return (lower == vChain.end() ? nullptr : *lower);
}
diff --git a/src/chain.h b/src/chain.h
index 2b6d2d082c..dd9cc2a598 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -465,8 +465,8 @@ public:
/** Find the last common block between this chain and a block index entry. */
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
- /** Find the earliest block with timestamp equal or greater than the given. */
- CBlockIndex* FindEarliestAtLeast(int64_t nTime) const;
+ /** Find the earliest block with timestamp equal or greater than the given time and height equal or greater than the given height. */
+ CBlockIndex* FindEarliestAtLeast(int64_t nTime, int height) const;
};
#endif // BITCOIN_CHAIN_H
diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp
deleted file mode 100644
index ad5edfeb39..0000000000
--- a/src/checkpoints.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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.
-
-#include <checkpoints.h>
-
-#include <chain.h>
-#include <chainparams.h>
-#include <reverse_iterator.h>
-#include <validation.h>
-
-#include <stdint.h>
-
-
-namespace Checkpoints {
-
- CBlockIndex* GetLastCheckpoint(const CCheckpointData& data)
- {
- const MapCheckpoints& checkpoints = data.mapCheckpoints;
-
- for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
- {
- const uint256& hash = i.second;
- CBlockIndex* pindex = LookupBlockIndex(hash);
- if (pindex) {
- return pindex;
- }
- }
- return nullptr;
- }
-
-} // namespace Checkpoints
diff --git a/src/checkpoints.h b/src/checkpoints.h
deleted file mode 100644
index a25e97e469..0000000000
--- a/src/checkpoints.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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_CHECKPOINTS_H
-#define BITCOIN_CHECKPOINTS_H
-
-#include <uint256.h>
-
-#include <map>
-
-class CBlockIndex;
-struct CCheckpointData;
-
-/**
- * Block-chain checkpoints are compiled-in sanity checks.
- * They are updated every release or three.
- */
-namespace Checkpoints
-{
-
-//! Returns last CBlockIndex* that is a checkpoint
-CBlockIndex* GetLastCheckpoint(const CCheckpointData& data);
-
-} //namespace Checkpoints
-
-#endif // BITCOIN_CHECKPOINTS_H
diff --git a/src/coins.h b/src/coins.h
index d39ebf9062..482e233e8c 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -294,6 +294,10 @@ public:
bool HaveInputs(const CTransaction& tx) const;
private:
+ /**
+ * @note this is marked const, but may actually append to `cacheCoins`, increasing
+ * memory usage.
+ */
CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const;
};
diff --git a/src/compat/assumptions.h b/src/compat/assumptions.h
index 820c9b93d9..6e7b4d3ded 100644
--- a/src/compat/assumptions.h
+++ b/src/compat/assumptions.h
@@ -17,6 +17,17 @@
# error "Bitcoin cannot be compiled without assertions."
#endif
+// Assumption: We assume a C++11 (ISO/IEC 14882:2011) compiler (minimum requirement).
+// Example(s): We assume the presence of C++11 features everywhere :-)
+// Note: MSVC does not report the expected __cplusplus value due to legacy
+// reasons.
+#if !defined(_MSC_VER)
+// ISO Standard C++11 [cpp.predefined]p1:
+// "The name __cplusplus is defined to the value 201103L when compiling a C++
+// translation unit."
+static_assert(__cplusplus >= 201103L, "C++11 standard assumed");
+#endif
+
// Assumption: We assume the floating-point types to fulfill the requirements of
// IEC 559 (IEEE 754) standard.
// Example(s): Floating-point division by zero in ConnectBlock, CreateTransaction
@@ -40,8 +51,13 @@ static_assert(sizeof(double) == 8, "64-bit double assumed");
static_assert(sizeof(short) == 2, "16-bit short assumed");
static_assert(sizeof(int) == 4, "32-bit int assumed");
+// Assumption: We assume size_t to be 32-bit or 64-bit.
+// Example(s): size_t assumed to be at least 32-bit in ecdsa_signature_parse_der_lax(...).
+// size_t assumed to be 32-bit or 64-bit in MallocUsage(...).
+static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t assumed to be 32-bit or 64-bit");
+static_assert(sizeof(size_t) == sizeof(void*), "Sizes of size_t and void* assumed to be equal");
+
// Some important things we are NOT assuming (non-exhaustive list):
-// * We are NOT assuming a specific value for sizeof(std::size_t).
// * We are NOT assuming a specific value for std::endian::native.
// * We are NOT assuming a specific value for std::locale("").name().
// * We are NOT assuming a specific value for std::numeric_limits<char>::is_signed.
diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp
new file mode 100644
index 0000000000..61a607ef7f
--- /dev/null
+++ b/src/consensus/tx_check.cpp
@@ -0,0 +1,57 @@
+// Copyright (c) 2017-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.
+
+#include <consensus/tx_check.h>
+
+#include <primitives/transaction.h>
+#include <consensus/validation.h>
+
+bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
+{
+ // Basic checks that don't depend on any context
+ if (tx.vin.empty())
+ return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
+ if (tx.vout.empty())
+ return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
+ // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
+ if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
+
+ // Check for negative or overflow output values
+ CAmount nValueOut = 0;
+ for (const auto& txout : tx.vout)
+ {
+ if (txout.nValue < 0)
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
+ if (txout.nValue > MAX_MONEY)
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
+ nValueOut += txout.nValue;
+ if (!MoneyRange(nValueOut))
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
+ }
+
+ // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
+ if (fCheckDuplicateInputs) {
+ std::set<COutPoint> vInOutPoints;
+ for (const auto& txin : tx.vin)
+ {
+ if (!vInOutPoints.insert(txin.prevout).second)
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
+ }
+ }
+
+ if (tx.IsCoinBase())
+ {
+ if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
+ return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
+ }
+ else
+ {
+ for (const auto& txin : tx.vin)
+ if (txin.prevout.IsNull())
+ return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
+ }
+
+ return true;
+}
diff --git a/src/consensus/tx_check.h b/src/consensus/tx_check.h
new file mode 100644
index 0000000000..bcfdf36bf9
--- /dev/null
+++ b/src/consensus/tx_check.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2017-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_CONSENSUS_TX_CHECK_H
+#define BITCOIN_CONSENSUS_TX_CHECK_H
+
+/**
+ * Context-independent transaction checking code that can be called outside the
+ * bitcoin server and doesn't depend on chain or mempool state. Transaction
+ * verification code that does call server functions or depend on server state
+ * belongs in tx_verify.h/cpp instead.
+ */
+
+class CTransaction;
+class CValidationState;
+
+bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true);
+
+#endif // BITCOIN_CONSENSUS_TX_CHECK_H
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index 0a7eacfb91..fbbbcfd040 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -156,55 +156,6 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
return nSigOps;
}
-bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
-{
- // Basic checks that don't depend on any context
- if (tx.vin.empty())
- return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
- if (tx.vout.empty())
- return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
- // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
- if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
-
- // Check for negative or overflow output values
- CAmount nValueOut = 0;
- for (const auto& txout : tx.vout)
- {
- if (txout.nValue < 0)
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
- if (txout.nValue > MAX_MONEY)
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
- nValueOut += txout.nValue;
- if (!MoneyRange(nValueOut))
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
- }
-
- // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
- if (fCheckDuplicateInputs) {
- std::set<COutPoint> vInOutPoints;
- for (const auto& txin : tx.vin)
- {
- if (!vInOutPoints.insert(txin.prevout).second)
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
- }
- }
-
- if (tx.IsCoinBase())
- {
- if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
- return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
- }
- else
- {
- for (const auto& txin : tx.vin)
- if (txin.prevout.IsNull())
- return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
- }
-
- return true;
-}
-
bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
{
// are the actual inputs available?
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index 0519cef8c0..3519fc555d 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -17,9 +17,6 @@ class CValidationState;
/** Transaction validation functions */
-/** Context-independent validity checks */
-bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true);
-
namespace Consensus {
/**
* Check whether all inputs of this transaction are valid (no double spends and amounts)
diff --git a/src/core_io.h b/src/core_io.h
index ae377eb6e8..19fb7b29f6 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -16,7 +16,6 @@ class CBlockHeader;
class CScript;
class CTransaction;
struct CMutableTransaction;
-struct PartiallySignedTransaction;
class uint256;
class UniValue;
@@ -37,11 +36,6 @@ bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);
*/
bool ParseHashStr(const std::string& strHex, uint256& result);
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
-
-//! Decode a base64ed PSBT into a PartiallySignedTransaction
-NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
-//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
-NODISCARD bool DecodeRawPSBT(PartiallySignedTransaction& decoded_psbt, const std::string& raw_psbt, std::string& error);
int ParseSighashString(const UniValue& sighash);
// core_write.cpp
diff --git a/src/core_read.cpp b/src/core_read.cpp
index 536a7f4f17..a879a375ce 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -4,7 +4,6 @@
#include <core_io.h>
-#include <psbt.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/script.h>
@@ -177,33 +176,6 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
return true;
}
-bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
-{
- bool invalid;
- std::string tx_data = DecodeBase64(base64_tx, &invalid);
- if (invalid) {
- error = "invalid base64";
- return false;
- }
- return DecodeRawPSBT(psbt, tx_data, error);
-}
-
-bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data, std::string& error)
-{
- CDataStream ss_data(tx_data.data(), tx_data.data() + tx_data.size(), SER_NETWORK, PROTOCOL_VERSION);
- try {
- ss_data >> psbt;
- if (!ss_data.empty()) {
- error = "extra data after PSBT";
- return false;
- }
- } catch (const std::exception& e) {
- error = e.what();
- return false;
- }
- return true;
-}
-
bool ParseHashStr(const std::string& strHex, uint256& result)
{
if ((strHex.size() != 64) || !IsHex(strHex))
diff --git a/src/crypto/aes.cpp b/src/crypto/aes.cpp
index 919ea593b7..2dc2133434 100644
--- a/src/crypto/aes.cpp
+++ b/src/crypto/aes.cpp
@@ -12,36 +12,6 @@ extern "C" {
#include <crypto/ctaes/ctaes.c>
}
-AES128Encrypt::AES128Encrypt(const unsigned char key[16])
-{
- AES128_init(&ctx, key);
-}
-
-AES128Encrypt::~AES128Encrypt()
-{
- memset(&ctx, 0, sizeof(ctx));
-}
-
-void AES128Encrypt::Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const
-{
- AES128_encrypt(&ctx, 1, ciphertext, plaintext);
-}
-
-AES128Decrypt::AES128Decrypt(const unsigned char key[16])
-{
- AES128_init(&ctx, key);
-}
-
-AES128Decrypt::~AES128Decrypt()
-{
- memset(&ctx, 0, sizeof(ctx));
-}
-
-void AES128Decrypt::Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const
-{
- AES128_decrypt(&ctx, 1, plaintext, ciphertext);
-}
-
AES256Encrypt::AES256Encrypt(const unsigned char key[32])
{
AES256_init(&ctx, key);
@@ -182,35 +152,3 @@ AES256CBCDecrypt::~AES256CBCDecrypt()
{
memset(iv, 0, sizeof(iv));
}
-
-AES128CBCEncrypt::AES128CBCEncrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
- : enc(key), pad(padIn)
-{
- memcpy(iv, ivIn, AES_BLOCKSIZE);
-}
-
-AES128CBCEncrypt::~AES128CBCEncrypt()
-{
- memset(iv, 0, AES_BLOCKSIZE);
-}
-
-int AES128CBCEncrypt::Encrypt(const unsigned char* data, int size, unsigned char* out) const
-{
- return CBCEncrypt(enc, iv, data, size, pad, out);
-}
-
-AES128CBCDecrypt::AES128CBCDecrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
- : dec(key), pad(padIn)
-{
- memcpy(iv, ivIn, AES_BLOCKSIZE);
-}
-
-AES128CBCDecrypt::~AES128CBCDecrypt()
-{
- memset(iv, 0, AES_BLOCKSIZE);
-}
-
-int AES128CBCDecrypt::Decrypt(const unsigned char* data, int size, unsigned char* out) const
-{
- return CBCDecrypt(dec, iv, data, size, pad, out);
-}
diff --git a/src/crypto/aes.h b/src/crypto/aes.h
index fdad70c593..e06c8de272 100644
--- a/src/crypto/aes.h
+++ b/src/crypto/aes.h
@@ -12,33 +12,8 @@ extern "C" {
}
static const int AES_BLOCKSIZE = 16;
-static const int AES128_KEYSIZE = 16;
static const int AES256_KEYSIZE = 32;
-/** An encryption class for AES-128. */
-class AES128Encrypt
-{
-private:
- AES128_ctx ctx;
-
-public:
- explicit AES128Encrypt(const unsigned char key[16]);
- ~AES128Encrypt();
- void Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const;
-};
-
-/** A decryption class for AES-128. */
-class AES128Decrypt
-{
-private:
- AES128_ctx ctx;
-
-public:
- explicit AES128Decrypt(const unsigned char key[16]);
- ~AES128Decrypt();
- void Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const;
-};
-
/** An encryption class for AES-256. */
class AES256Encrypt
{
@@ -89,30 +64,4 @@ private:
unsigned char iv[AES_BLOCKSIZE];
};
-class AES128CBCEncrypt
-{
-public:
- AES128CBCEncrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
- ~AES128CBCEncrypt();
- int Encrypt(const unsigned char* data, int size, unsigned char* out) const;
-
-private:
- const AES128Encrypt enc;
- const bool pad;
- unsigned char iv[AES_BLOCKSIZE];
-};
-
-class AES128CBCDecrypt
-{
-public:
- AES128CBCDecrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
- ~AES128CBCDecrypt();
- int Decrypt(const unsigned char* data, int size, unsigned char* out) const;
-
-private:
- const AES128Decrypt dec;
- const bool pad;
- unsigned char iv[AES_BLOCKSIZE];
-};
-
#endif // BITCOIN_CRYPTO_AES_H
diff --git a/src/crypto/poly1305.cpp b/src/crypto/poly1305.cpp
new file mode 100644
index 0000000000..8a86c9601c
--- /dev/null
+++ b/src/crypto/poly1305.cpp
@@ -0,0 +1,141 @@
+// 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.
+
+// Based on the public domain implementation by Andrew Moon
+// poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna
+
+#include <crypto/common.h>
+#include <crypto/poly1305.h>
+
+#include <string.h>
+
+#define mul32x32_64(a,b) ((uint64_t)(a) * (b))
+
+void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) {
+ uint32_t t0,t1,t2,t3;
+ uint32_t h0,h1,h2,h3,h4;
+ uint32_t r0,r1,r2,r3,r4;
+ uint32_t s1,s2,s3,s4;
+ uint32_t b, nb;
+ size_t j;
+ uint64_t t[5];
+ uint64_t f0,f1,f2,f3;
+ uint64_t g0,g1,g2,g3,g4;
+ uint64_t c;
+ unsigned char mp[16];
+
+ /* clamp key */
+ t0 = ReadLE32(key+0);
+ t1 = ReadLE32(key+4);
+ t2 = ReadLE32(key+8);
+ t3 = ReadLE32(key+12);
+
+ /* precompute multipliers */
+ r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6;
+ r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12;
+ r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18;
+ r3 = t2 & 0x3f03fff; t3 >>= 8;
+ r4 = t3 & 0x00fffff;
+
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ /* init state */
+ h0 = 0;
+ h1 = 0;
+ h2 = 0;
+ h3 = 0;
+ h4 = 0;
+
+ /* full blocks */
+ if (inlen < 16) goto poly1305_donna_atmost15bytes;
+poly1305_donna_16bytes:
+ m += 16;
+ inlen -= 16;
+
+ t0 = ReadLE32(m-16);
+ t1 = ReadLE32(m-12);
+ t2 = ReadLE32(m-8);
+ t3 = ReadLE32(m-4);
+
+ h0 += t0 & 0x3ffffff;
+ h1 += ((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff;
+ h2 += ((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff;
+ h3 += ((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff;
+ h4 += (t3 >> 8) | (1 << 24);
+
+
+poly1305_donna_mul:
+ t[0] = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
+ t[1] = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
+ t[2] = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
+ t[3] = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
+ t[4] = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
+
+ h0 = (uint32_t)t[0] & 0x3ffffff; c = (t[0] >> 26);
+ t[1] += c; h1 = (uint32_t)t[1] & 0x3ffffff; b = (uint32_t)(t[1] >> 26);
+ t[2] += b; h2 = (uint32_t)t[2] & 0x3ffffff; b = (uint32_t)(t[2] >> 26);
+ t[3] += b; h3 = (uint32_t)t[3] & 0x3ffffff; b = (uint32_t)(t[3] >> 26);
+ t[4] += b; h4 = (uint32_t)t[4] & 0x3ffffff; b = (uint32_t)(t[4] >> 26);
+ h0 += b * 5;
+
+ if (inlen >= 16) goto poly1305_donna_16bytes;
+
+ /* final bytes */
+poly1305_donna_atmost15bytes:
+ if (!inlen) goto poly1305_donna_finish;
+
+ for (j = 0; j < inlen; j++) mp[j] = m[j];
+ mp[j++] = 1;
+ for (; j < 16; j++) mp[j] = 0;
+ inlen = 0;
+
+ t0 = ReadLE32(mp+0);
+ t1 = ReadLE32(mp+4);
+ t2 = ReadLE32(mp+8);
+ t3 = ReadLE32(mp+12);
+
+ h0 += t0 & 0x3ffffff;
+ h1 += ((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff;
+ h2 += ((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff;
+ h3 += ((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff;
+ h4 += (t3 >> 8);
+
+ goto poly1305_donna_mul;
+
+poly1305_donna_finish:
+ b = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff;
+ h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff;
+ h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff;
+ h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff;
+ h0 += b * 5; b = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += b;
+
+ g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff;
+ g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff;
+ g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff;
+ g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff;
+ g4 = h4 + b - (1 << 26);
+
+ b = (g4 >> 31) - 1;
+ nb = ~b;
+ h0 = (h0 & nb) | (g0 & b);
+ h1 = (h1 & nb) | (g1 & b);
+ h2 = (h2 & nb) | (g2 & b);
+ h3 = (h3 & nb) | (g3 & b);
+ h4 = (h4 & nb) | (g4 & b);
+
+ f0 = ((h0 ) | (h1 << 26)) + (uint64_t)ReadLE32(&key[16]);
+ f1 = ((h1 >> 6) | (h2 << 20)) + (uint64_t)ReadLE32(&key[20]);
+ f2 = ((h2 >> 12) | (h3 << 14)) + (uint64_t)ReadLE32(&key[24]);
+ f3 = ((h3 >> 18) | (h4 << 8)) + (uint64_t)ReadLE32(&key[28]);
+
+ WriteLE32(&out[ 0], f0); f1 += (f0 >> 32);
+ WriteLE32(&out[ 4], f1); f2 += (f1 >> 32);
+ WriteLE32(&out[ 8], f2); f3 += (f2 >> 32);
+ WriteLE32(&out[12], f3);
+}
diff --git a/src/crypto/poly1305.h b/src/crypto/poly1305.h
new file mode 100644
index 0000000000..1598b013b9
--- /dev/null
+++ b/src/crypto/poly1305.h
@@ -0,0 +1,17 @@
+// 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_CRYPTO_POLY1305_H
+#define BITCOIN_CRYPTO_POLY1305_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define POLY1305_KEYLEN 32
+#define POLY1305_TAGLEN 16
+
+void poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen,
+ const unsigned char key[POLY1305_KEYLEN]);
+
+#endif // BITCOIN_CRYPTO_POLY1305_H
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 8a76021a5b..eeec6dec25 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -23,11 +23,33 @@ public:
void DummyWalletInit::AddWalletOptions() const
{
- std::vector<std::string> opts = {"-addresstype", "-changetype", "-disablewallet", "-discardfee=<amt>", "-fallbackfee=<amt>",
- "-keypool=<n>", "-mintxfee=<amt>", "-paytxfee=<amt>", "-rescan", "-salvagewallet", "-spendzeroconfchange", "-txconfirmtarget=<n>",
- "-upgradewallet", "-wallet=<path>", "-walletbroadcast", "-walletdir=<dir>", "-walletnotify=<cmd>", "-walletrbf", "-zapwallettxes=<mode>",
- "-dblogsize=<n>", "-flushwallet", "-privdb", "-walletrejectlongchains"};
- gArgs.AddHiddenArgs(opts);
+ gArgs.AddHiddenArgs({
+ "-addresstype",
+ "-avoidpartialspends",
+ "-changetype",
+ "-disablewallet",
+ "-discardfee=<amt>",
+ "-fallbackfee=<amt>",
+ "-keypool=<n>",
+ "-maxtxfee=<amt>",
+ "-mintxfee=<amt>",
+ "-paytxfee=<amt>",
+ "-rescan",
+ "-salvagewallet",
+ "-spendzeroconfchange",
+ "-txconfirmtarget=<n>",
+ "-upgradewallet",
+ "-wallet=<path>",
+ "-walletbroadcast",
+ "-walletdir=<dir>",
+ "-walletnotify=<cmd>",
+ "-walletrbf",
+ "-zapwallettxes=<mode>",
+ "-dblogsize=<n>",
+ "-flushwallet",
+ "-privdb",
+ "-walletrejectlongchains",
+ });
}
const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
diff --git a/src/fs.cpp b/src/fs.cpp
index f937f64e04..7b422b8d70 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -3,7 +3,9 @@
#ifndef WIN32
#include <fcntl.h>
#else
+#ifndef NOMINMAX
#define NOMINMAX
+#endif
#include <codecvt>
#include <windows.h>
#endif
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index b9ca037c9d..63639fa3e0 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -6,6 +6,7 @@
#include <chainparamsbase.h>
#include <compat.h>
+#include <util/threadnames.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <netbase.h>
@@ -17,7 +18,7 @@
#include <memory>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
+#include <string>
#include <sys/types.h>
#include <sys/stat.h>
@@ -284,7 +285,7 @@ static void http_reject_request_cb(struct evhttp_request* req, void*)
/** Event dispatcher thread */
static bool ThreadHTTP(struct event_base* base)
{
- RenameThread("bitcoin-http");
+ util::ThreadRename("http");
LogPrint(BCLog::HTTP, "Entering http event loop\n");
event_base_dispatch(base);
// Event loop will be interrupted by InterruptHTTPServer()
@@ -335,9 +336,9 @@ static bool HTTPBindAddresses(struct evhttp* http)
}
/** Simple wrapper to set thread name and run work queue */
-static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue)
+static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue, int worker_num)
{
- RenameThread("bitcoin-httpworker");
+ util::ThreadRename(strprintf("httpworker.%i", worker_num));
queue->Run();
}
@@ -430,7 +431,7 @@ void StartHTTPServer()
threadHTTP = std::thread(ThreadHTTP, eventBase);
for (int i = 0; i < rpcThreads; i++) {
- g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue);
+ g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue, i);
}
}
@@ -655,15 +656,3 @@ void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
pathHandlers.erase(i);
}
}
-
-std::string urlDecode(const std::string &urlEncoded) {
- std::string res;
- if (!urlEncoded.empty()) {
- char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
- if (decoded) {
- res = std::string(decoded);
- free(decoded);
- }
- }
- return res;
-}
diff --git a/src/httpserver.h b/src/httpserver.h
index 63f96734f8..7943f0094b 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -148,6 +148,4 @@ private:
struct event* ev;
};
-std::string urlDecode(const std::string &urlEncoded);
-
#endif // BITCOIN_HTTPSERVER_H
diff --git a/src/index/base.cpp b/src/index/base.cpp
index f6f59572ce..9e48f0bd27 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -41,9 +41,9 @@ bool BaseIndex::DB::ReadBestBlock(CBlockLocator& locator) const
return success;
}
-bool BaseIndex::DB::WriteBestBlock(const CBlockLocator& locator)
+void BaseIndex::DB::WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator)
{
- return Write(DB_BEST_BLOCK, locator);
+ batch.Write(DB_BEST_BLOCK, locator);
}
BaseIndex::~BaseIndex()
@@ -95,7 +95,11 @@ void BaseIndex::ThreadSync()
int64_t last_locator_write_time = 0;
while (true) {
if (m_interrupt) {
- WriteBestBlock(pindex);
+ m_best_block_index = pindex;
+ // No need to handle errors in Commit. If it fails, the error will be already be
+ // logged. The best way to recover is to continue, as index cannot be corrupted by
+ // a missed commit to disk for an advanced index state.
+ Commit();
return;
}
@@ -103,11 +107,17 @@ void BaseIndex::ThreadSync()
LOCK(cs_main);
const CBlockIndex* pindex_next = NextSyncBlock(pindex);
if (!pindex_next) {
- WriteBestBlock(pindex);
m_best_block_index = pindex;
m_synced = true;
+ // No need to handle errors in Commit. See rationale above.
+ Commit();
break;
}
+ if (pindex_next->pprev != pindex && !Rewind(pindex, pindex_next->pprev)) {
+ FatalError("%s: Failed to rewind index %s to a previous chain tip",
+ __func__, GetName());
+ return;
+ }
pindex = pindex_next;
}
@@ -119,8 +129,10 @@ void BaseIndex::ThreadSync()
}
if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) {
- WriteBestBlock(pindex);
+ m_best_block_index = pindex;
last_locator_write_time = current_time;
+ // No need to handle errors in Commit. See rationale above.
+ Commit();
}
CBlock block;
@@ -144,12 +156,35 @@ void BaseIndex::ThreadSync()
}
}
-bool BaseIndex::WriteBestBlock(const CBlockIndex* block_index)
+bool BaseIndex::Commit()
+{
+ CDBBatch batch(GetDB());
+ if (!CommitInternal(batch) || !GetDB().WriteBatch(batch)) {
+ return error("%s: Failed to commit latest %s state", __func__, GetName());
+ }
+ return true;
+}
+
+bool BaseIndex::CommitInternal(CDBBatch& batch)
{
LOCK(cs_main);
- if (!GetDB().WriteBestBlock(chainActive.GetLocator(block_index))) {
- return error("%s: Failed to write locator to disk", __func__);
+ GetDB().WriteBestBlock(batch, chainActive.GetLocator(m_best_block_index));
+ return true;
+}
+
+bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip)
+{
+ assert(current_tip == m_best_block_index);
+ assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
+
+ // In the case of a reorg, ensure persisted block locator is not stale.
+ m_best_block_index = new_tip;
+ if (!Commit()) {
+ // If commit fails, revert the best block index to avoid corruption.
+ m_best_block_index = current_tip;
+ return false;
}
+
return true;
}
@@ -180,6 +215,11 @@ void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const
best_block_index->GetBlockHash().ToString());
return;
}
+ if (best_block_index != pindex->pprev && !Rewind(best_block_index, pindex->pprev)) {
+ FatalError("%s: Failed to rewind index %s to a previous chain tip",
+ __func__, GetName());
+ return;
+ }
}
if (WriteBlock(*block, pindex)) {
@@ -224,9 +264,10 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator)
return;
}
- if (!GetDB().WriteBestBlock(locator)) {
- error("%s: Failed to write locator to disk", __func__);
- }
+ // No need to handle errors in Commit. If it fails, the error will be already be logged. The
+ // best way to recover is to continue, as index cannot be corrupted by a missed commit to disk
+ // for an advanced index state.
+ Commit();
}
bool BaseIndex::BlockUntilSyncedToCurrentChain()
diff --git a/src/index/base.h b/src/index/base.h
index 04ee6e6cc2..31acbed0c1 100644
--- a/src/index/base.h
+++ b/src/index/base.h
@@ -32,7 +32,7 @@ protected:
bool ReadBestBlock(CBlockLocator& locator) const;
/// Write block locator of the chain that the txindex is in sync with.
- bool WriteBestBlock(const CBlockLocator& locator);
+ void WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator);
};
private:
@@ -54,8 +54,15 @@ private:
/// over and the sync thread exits.
void ThreadSync();
- /// Write the current chain block locator to the DB.
- bool WriteBestBlock(const CBlockIndex* block_index);
+ /// Write the current index state (eg. chain block locator and subclass-specific items) to disk.
+ ///
+ /// Recommendations for error handling:
+ /// If called on a successor of the previous committed best block in the index, the index can
+ /// continue processing without risk of corruption, though the index state will need to catch up
+ /// from further behind on reboot. If the new state is not a successor of the previous state (due
+ /// to a chain reorganization), the index must halt until Commit succeeds or else it could end up
+ /// getting corrupted.
+ bool Commit();
protected:
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex,
@@ -69,6 +76,14 @@ protected:
/// Write update index entries for a newly connected block.
virtual bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) { return true; }
+ /// Virtual method called internally by Commit that can be overridden to atomically
+ /// commit more index state.
+ virtual bool CommitInternal(CDBBatch& batch);
+
+ /// Rewind index to an earlier chain tip during a chain reorg. The tip must
+ /// be an ancestor of the current best block.
+ virtual bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip);
+
virtual DB& GetDB() const = 0;
/// Get the name of the index for display in logs.
diff --git a/src/index/blockfilterindex.cpp b/src/index/blockfilterindex.cpp
new file mode 100644
index 0000000000..20f33baf2c
--- /dev/null
+++ b/src/index/blockfilterindex.cpp
@@ -0,0 +1,467 @@
+// Copyright (c) 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.
+
+#include <map>
+
+#include <dbwrapper.h>
+#include <index/blockfilterindex.h>
+#include <util/system.h>
+#include <validation.h>
+
+/* The index database stores three items for each block: the disk location of the encoded filter,
+ * its dSHA256 hash, and the header. Those belonging to blocks on the active chain are indexed by
+ * height, and those belonging to blocks that have been reorganized out of the active chain are
+ * indexed by block hash. This ensures that filter data for any block that becomes part of the
+ * active chain can always be retrieved, alleviating timing concerns.
+ *
+ * The filters themselves are stored in flat files and referenced by the LevelDB entries. This
+ * minimizes the amount of data written to LevelDB and keeps the database values constant size. The
+ * disk location of the next block filter to be written (represented as a FlatFilePos) is stored
+ * under the DB_FILTER_POS key.
+ *
+ * Keys for the height index have the type [DB_BLOCK_HEIGHT, uint32 (BE)]. The height is represented
+ * as big-endian so that sequential reads of filters by height are fast.
+ * Keys for the hash index have the type [DB_BLOCK_HASH, uint256].
+ */
+constexpr char DB_BLOCK_HASH = 's';
+constexpr char DB_BLOCK_HEIGHT = 't';
+constexpr char DB_FILTER_POS = 'P';
+
+constexpr unsigned int MAX_FLTR_FILE_SIZE = 0x1000000; // 16 MiB
+/** The pre-allocation chunk size for fltr?????.dat files */
+constexpr unsigned int FLTR_FILE_CHUNK_SIZE = 0x100000; // 1 MiB
+
+namespace {
+
+struct DBVal {
+ uint256 hash;
+ uint256 header;
+ FlatFilePos pos;
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ READWRITE(hash);
+ READWRITE(header);
+ READWRITE(pos);
+ }
+};
+
+struct DBHeightKey {
+ int height;
+
+ DBHeightKey() : height(0) {}
+ DBHeightKey(int height_in) : height(height_in) {}
+
+ template<typename Stream>
+ void Serialize(Stream& s) const
+ {
+ ser_writedata8(s, DB_BLOCK_HEIGHT);
+ ser_writedata32be(s, height);
+ }
+
+ template<typename Stream>
+ void Unserialize(Stream& s)
+ {
+ char prefix = ser_readdata8(s);
+ if (prefix != DB_BLOCK_HEIGHT) {
+ throw std::ios_base::failure("Invalid format for block filter index DB height key");
+ }
+ height = ser_readdata32be(s);
+ }
+};
+
+struct DBHashKey {
+ uint256 hash;
+
+ DBHashKey(const uint256& hash_in) : hash(hash_in) {}
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ char prefix = DB_BLOCK_HASH;
+ READWRITE(prefix);
+ if (prefix != DB_BLOCK_HASH) {
+ throw std::ios_base::failure("Invalid format for block filter index DB hash key");
+ }
+
+ READWRITE(hash);
+ }
+};
+
+}; // namespace
+
+static std::map<BlockFilterType, BlockFilterIndex> g_filter_indexes;
+
+BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type,
+ size_t n_cache_size, bool f_memory, bool f_wipe)
+ : m_filter_type(filter_type)
+{
+ const std::string& filter_name = BlockFilterTypeName(filter_type);
+ if (filter_name.empty()) throw std::invalid_argument("unknown filter_type");
+
+ fs::path path = GetDataDir() / "indexes" / "blockfilter" / filter_name;
+ fs::create_directories(path);
+
+ m_name = filter_name + " block filter index";
+ m_db = MakeUnique<BaseIndex::DB>(path / "db", n_cache_size, f_memory, f_wipe);
+ m_filter_fileseq = MakeUnique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE);
+}
+
+bool BlockFilterIndex::Init()
+{
+ if (!m_db->Read(DB_FILTER_POS, m_next_filter_pos)) {
+ // Check that the cause of the read failure is that the key does not exist. Any other errors
+ // indicate database corruption or a disk failure, and starting the index would cause
+ // further corruption.
+ if (m_db->Exists(DB_FILTER_POS)) {
+ return error("%s: Cannot read current %s state; index may be corrupted",
+ __func__, GetName());
+ }
+
+ // If the DB_FILTER_POS is not set, then initialize to the first location.
+ m_next_filter_pos.nFile = 0;
+ m_next_filter_pos.nPos = 0;
+ }
+ return BaseIndex::Init();
+}
+
+bool BlockFilterIndex::CommitInternal(CDBBatch& batch)
+{
+ const FlatFilePos& pos = m_next_filter_pos;
+
+ // Flush current filter file to disk.
+ CAutoFile file(m_filter_fileseq->Open(pos), SER_DISK, CLIENT_VERSION);
+ if (file.IsNull()) {
+ return error("%s: Failed to open filter file %d", __func__, pos.nFile);
+ }
+ if (!FileCommit(file.Get())) {
+ return error("%s: Failed to commit filter file %d", __func__, pos.nFile);
+ }
+
+ batch.Write(DB_FILTER_POS, pos);
+ return BaseIndex::CommitInternal(batch);
+}
+
+bool BlockFilterIndex::ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const
+{
+ CAutoFile filein(m_filter_fileseq->Open(pos, true), SER_DISK, CLIENT_VERSION);
+ if (filein.IsNull()) {
+ return false;
+ }
+
+ uint256 block_hash;
+ std::vector<unsigned char> encoded_filter;
+ try {
+ filein >> block_hash >> encoded_filter;
+ filter = BlockFilter(GetFilterType(), block_hash, std::move(encoded_filter));
+ }
+ catch (const std::exception& e) {
+ return error("%s: Failed to deserialize block filter from disk: %s", __func__, e.what());
+ }
+
+ return true;
+}
+
+size_t BlockFilterIndex::WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter)
+{
+ assert(filter.GetFilterType() == GetFilterType());
+
+ size_t data_size =
+ GetSerializeSize(filter.GetBlockHash(), CLIENT_VERSION) +
+ GetSerializeSize(filter.GetEncodedFilter(), CLIENT_VERSION);
+
+ // If writing the filter would overflow the file, flush and move to the next one.
+ if (pos.nPos + data_size > MAX_FLTR_FILE_SIZE) {
+ CAutoFile last_file(m_filter_fileseq->Open(pos), SER_DISK, CLIENT_VERSION);
+ if (last_file.IsNull()) {
+ LogPrintf("%s: Failed to open filter file %d\n", __func__, pos.nFile);
+ return 0;
+ }
+ if (!TruncateFile(last_file.Get(), pos.nPos)) {
+ LogPrintf("%s: Failed to truncate filter file %d\n", __func__, pos.nFile);
+ return 0;
+ }
+ if (!FileCommit(last_file.Get())) {
+ LogPrintf("%s: Failed to commit filter file %d\n", __func__, pos.nFile);
+ return 0;
+ }
+
+ pos.nFile++;
+ pos.nPos = 0;
+ }
+
+ // Pre-allocate sufficient space for filter data.
+ bool out_of_space;
+ m_filter_fileseq->Allocate(pos, data_size, out_of_space);
+ if (out_of_space) {
+ LogPrintf("%s: out of disk space\n", __func__);
+ return 0;
+ }
+
+ CAutoFile fileout(m_filter_fileseq->Open(pos), SER_DISK, CLIENT_VERSION);
+ if (fileout.IsNull()) {
+ LogPrintf("%s: Failed to open filter file %d\n", __func__, pos.nFile);
+ return 0;
+ }
+
+ fileout << filter.GetBlockHash() << filter.GetEncodedFilter();
+ return data_size;
+}
+
+bool BlockFilterIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
+{
+ CBlockUndo block_undo;
+ uint256 prev_header;
+
+ if (pindex->nHeight > 0) {
+ if (!UndoReadFromDisk(block_undo, pindex)) {
+ return false;
+ }
+
+ std::pair<uint256, DBVal> read_out;
+ if (!m_db->Read(DBHeightKey(pindex->nHeight - 1), read_out)) {
+ return false;
+ }
+
+ uint256 expected_block_hash = pindex->pprev->GetBlockHash();
+ if (read_out.first != expected_block_hash) {
+ return error("%s: previous block header belongs to unexpected block %s; expected %s",
+ __func__, read_out.first.ToString(), expected_block_hash.ToString());
+ }
+
+ prev_header = read_out.second.header;
+ }
+
+ BlockFilter filter(m_filter_type, block, block_undo);
+
+ size_t bytes_written = WriteFilterToDisk(m_next_filter_pos, filter);
+ if (bytes_written == 0) return false;
+
+ std::pair<uint256, DBVal> value;
+ value.first = pindex->GetBlockHash();
+ value.second.hash = filter.GetHash();
+ value.second.header = filter.ComputeHeader(prev_header);
+ value.second.pos = m_next_filter_pos;
+
+ if (!m_db->Write(DBHeightKey(pindex->nHeight), value)) {
+ return false;
+ }
+
+ m_next_filter_pos.nPos += bytes_written;
+ return true;
+}
+
+static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
+ const std::string& index_name,
+ int start_height, int stop_height)
+{
+ DBHeightKey key(start_height);
+ db_it.Seek(key);
+
+ for (int height = start_height; height <= stop_height; ++height) {
+ if (!db_it.GetKey(key) || key.height != height) {
+ return error("%s: unexpected key in %s: expected (%c, %d)",
+ __func__, index_name, DB_BLOCK_HEIGHT, height);
+ }
+
+ std::pair<uint256, DBVal> value;
+ if (!db_it.GetValue(value)) {
+ return error("%s: unable to read value in %s at key (%c, %d)",
+ __func__, index_name, DB_BLOCK_HEIGHT, height);
+ }
+
+ batch.Write(DBHashKey(value.first), std::move(value.second));
+
+ db_it.Next();
+ }
+ return true;
+}
+
+bool BlockFilterIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip)
+{
+ assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
+
+ CDBBatch batch(*m_db);
+ std::unique_ptr<CDBIterator> db_it(m_db->NewIterator());
+
+ // During a reorg, we need to copy all filters for blocks that are getting disconnected from the
+ // height index to the hash index so we can still find them when the height index entries are
+ // overwritten.
+ if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip->nHeight, current_tip->nHeight)) {
+ return false;
+ }
+
+ // The latest filter position gets written in Commit by the call to the BaseIndex::Rewind.
+ // But since this creates new references to the filter, the position should get updated here
+ // atomically as well in case Commit fails.
+ batch.Write(DB_FILTER_POS, m_next_filter_pos);
+ if (!m_db->WriteBatch(batch)) return false;
+
+ return BaseIndex::Rewind(current_tip, new_tip);
+}
+
+static bool LookupOne(const CDBWrapper& db, const CBlockIndex* block_index, DBVal& result)
+{
+ // First check if the result is stored under the height index and the value there matches the
+ // block hash. This should be the case if the block is on the active chain.
+ std::pair<uint256, DBVal> read_out;
+ if (!db.Read(DBHeightKey(block_index->nHeight), read_out)) {
+ return false;
+ }
+ if (read_out.first == block_index->GetBlockHash()) {
+ result = std::move(read_out.second);
+ return true;
+ }
+
+ // If value at the height index corresponds to an different block, the result will be stored in
+ // the hash index.
+ return db.Read(DBHashKey(block_index->GetBlockHash()), result);
+}
+
+static bool LookupRange(CDBWrapper& db, const std::string& index_name, int start_height,
+ const CBlockIndex* stop_index, std::vector<DBVal>& results)
+{
+ if (start_height < 0) {
+ return error("%s: start height (%d) is negative", __func__, start_height);
+ }
+ if (start_height > stop_index->nHeight) {
+ return error("%s: start height (%d) is greater than stop height (%d)",
+ __func__, start_height, stop_index->nHeight);
+ }
+
+ size_t results_size = static_cast<size_t>(stop_index->nHeight - start_height + 1);
+ std::vector<std::pair<uint256, DBVal>> values(results_size);
+
+ DBHeightKey key(start_height);
+ std::unique_ptr<CDBIterator> db_it(db.NewIterator());
+ db_it->Seek(DBHeightKey(start_height));
+ for (int height = start_height; height <= stop_index->nHeight; ++height) {
+ if (!db_it->Valid() || !db_it->GetKey(key) || key.height != height) {
+ return false;
+ }
+
+ size_t i = static_cast<size_t>(height - start_height);
+ if (!db_it->GetValue(values[i])) {
+ return error("%s: unable to read value in %s at key (%c, %d)",
+ __func__, index_name, DB_BLOCK_HEIGHT, height);
+ }
+
+ db_it->Next();
+ }
+
+ results.resize(results_size);
+
+ // Iterate backwards through block indexes collecting results in order to access the block hash
+ // of each entry in case we need to look it up in the hash index.
+ for (const CBlockIndex* block_index = stop_index;
+ block_index && block_index->nHeight >= start_height;
+ block_index = block_index->pprev) {
+ uint256 block_hash = block_index->GetBlockHash();
+
+ size_t i = static_cast<size_t>(block_index->nHeight - start_height);
+ if (block_hash == values[i].first) {
+ results[i] = std::move(values[i].second);
+ continue;
+ }
+
+ if (!db.Read(DBHashKey(block_hash), results[i])) {
+ return error("%s: unable to read value in %s at key (%c, %s)",
+ __func__, index_name, DB_BLOCK_HASH, block_hash.ToString());
+ }
+ }
+
+ return true;
+}
+
+bool BlockFilterIndex::LookupFilter(const CBlockIndex* block_index, BlockFilter& filter_out) const
+{
+ DBVal entry;
+ if (!LookupOne(*m_db, block_index, entry)) {
+ return false;
+ }
+
+ return ReadFilterFromDisk(entry.pos, filter_out);
+}
+
+bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) const
+{
+ DBVal entry;
+ if (!LookupOne(*m_db, block_index, entry)) {
+ return false;
+ }
+
+ header_out = entry.header;
+ return true;
+}
+
+bool BlockFilterIndex::LookupFilterRange(int start_height, const CBlockIndex* stop_index,
+ std::vector<BlockFilter>& filters_out) const
+{
+ std::vector<DBVal> entries;
+ if (!LookupRange(*m_db, m_name, start_height, stop_index, entries)) {
+ return false;
+ }
+
+ filters_out.resize(entries.size());
+ auto filter_pos_it = filters_out.begin();
+ for (const auto& entry : entries) {
+ if (!ReadFilterFromDisk(entry.pos, *filter_pos_it)) {
+ return false;
+ }
+ ++filter_pos_it;
+ }
+
+ return true;
+}
+
+bool BlockFilterIndex::LookupFilterHashRange(int start_height, const CBlockIndex* stop_index,
+ std::vector<uint256>& hashes_out) const
+
+{
+ std::vector<DBVal> entries;
+ if (!LookupRange(*m_db, m_name, start_height, stop_index, entries)) {
+ return false;
+ }
+
+ hashes_out.clear();
+ hashes_out.reserve(entries.size());
+ for (const auto& entry : entries) {
+ hashes_out.push_back(entry.hash);
+ }
+ return true;
+}
+
+BlockFilterIndex* GetBlockFilterIndex(BlockFilterType filter_type)
+{
+ auto it = g_filter_indexes.find(filter_type);
+ return it != g_filter_indexes.end() ? &it->second : nullptr;
+}
+
+void ForEachBlockFilterIndex(std::function<void (BlockFilterIndex&)> fn)
+{
+ for (auto& entry : g_filter_indexes) fn(entry.second);
+}
+
+bool InitBlockFilterIndex(BlockFilterType filter_type,
+ size_t n_cache_size, bool f_memory, bool f_wipe)
+{
+ auto result = g_filter_indexes.emplace(std::piecewise_construct,
+ std::forward_as_tuple(filter_type),
+ std::forward_as_tuple(filter_type,
+ n_cache_size, f_memory, f_wipe));
+ return result.second;
+}
+
+bool DestroyBlockFilterIndex(BlockFilterType filter_type)
+{
+ return g_filter_indexes.erase(filter_type);
+}
+
+void DestroyAllBlockFilterIndexes()
+{
+ g_filter_indexes.clear();
+}
diff --git a/src/index/blockfilterindex.h b/src/index/blockfilterindex.h
new file mode 100644
index 0000000000..436d52515f
--- /dev/null
+++ b/src/index/blockfilterindex.h
@@ -0,0 +1,94 @@
+// Copyright (c) 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_INDEX_BLOCKFILTERINDEX_H
+#define BITCOIN_INDEX_BLOCKFILTERINDEX_H
+
+#include <blockfilter.h>
+#include <chain.h>
+#include <flatfile.h>
+#include <index/base.h>
+
+/**
+ * BlockFilterIndex is used to store and retrieve block filters, hashes, and headers for a range of
+ * blocks by height. An index is constructed for each supported filter type with its own database
+ * (ie. filter data for different types are stored in separate databases).
+ *
+ * This index is used to serve BIP 157 net requests.
+ */
+class BlockFilterIndex final : public BaseIndex
+{
+private:
+ BlockFilterType m_filter_type;
+ std::string m_name;
+ std::unique_ptr<BaseIndex::DB> m_db;
+
+ FlatFilePos m_next_filter_pos;
+ std::unique_ptr<FlatFileSeq> m_filter_fileseq;
+
+ bool ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const;
+ size_t WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter);
+
+protected:
+ bool Init() override;
+
+ bool CommitInternal(CDBBatch& batch) override;
+
+ bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
+
+ bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip) override;
+
+ BaseIndex::DB& GetDB() const override { return *m_db; }
+
+ const char* GetName() const override { return m_name.c_str(); }
+
+public:
+ /** Constructs the index, which becomes available to be queried. */
+ explicit BlockFilterIndex(BlockFilterType filter_type,
+ size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
+
+ BlockFilterType GetFilterType() const { return m_filter_type; }
+
+ /** Get a single filter by block. */
+ bool LookupFilter(const CBlockIndex* block_index, BlockFilter& filter_out) const;
+
+ /** Get a single filter header by block. */
+ bool LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) const;
+
+ /** Get a range of filters between two heights on a chain. */
+ bool LookupFilterRange(int start_height, const CBlockIndex* stop_index,
+ std::vector<BlockFilter>& filters_out) const;
+
+ /** Get a range of filter hashes between two heights on a chain. */
+ bool LookupFilterHashRange(int start_height, const CBlockIndex* stop_index,
+ std::vector<uint256>& hashes_out) const;
+};
+
+/**
+ * Get a block filter index by type. Returns nullptr if index has not been initialized or was
+ * already destroyed.
+ */
+BlockFilterIndex* GetBlockFilterIndex(BlockFilterType filter_type);
+
+/** Iterate over all running block filter indexes, invoking fn on each. */
+void ForEachBlockFilterIndex(std::function<void (BlockFilterIndex&)> fn);
+
+/**
+ * Initialize a block filter index for the given type if one does not already exist. Returns true if
+ * a new index is created and false if one has already been initialized.
+ */
+bool InitBlockFilterIndex(BlockFilterType filter_type,
+ size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
+
+/**
+ * Destroy the block filter index with the given type. Returns false if no such index exists. This
+ * just releases the allocated memory and closes the database connection, it does not delete the
+ * index data.
+ */
+bool DestroyBlockFilterIndex(BlockFilterType filter_type);
+
+/** Destroy all open block filter indexes. */
+void DestroyAllBlockFilterIndexes();
+
+#endif // BITCOIN_INDEX_BLOCKFILTERINDEX_H
diff --git a/src/init.cpp b/src/init.cpp
index cf3be0556e..92b3c9510a 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -12,14 +12,15 @@
#include <addrman.h>
#include <amount.h>
#include <banman.h>
+#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
-#include <checkpoints.h>
#include <compat/sanity.h>
#include <consensus/validation.h>
#include <fs.h>
#include <httpserver.h>
#include <httprpc.h>
+#include <index/blockfilterindex.h>
#include <interfaces/chain.h>
#include <index/txindex.h>
#include <key.h>
@@ -31,6 +32,7 @@
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <rpc/server.h>
#include <rpc/register.h>
#include <rpc/blockchain.h>
@@ -39,6 +41,7 @@
#include <script/sigcache.h>
#include <scheduler.h>
#include <shutdown.h>
+#include <util/threadnames.h>
#include <timedata.h>
#include <txdb.h>
#include <txmempool.h>
@@ -46,6 +49,7 @@
#include <ui_interface.h>
#include <util/system.h>
#include <util/moneystr.h>
+#include <util/validation.h>
#include <validationinterface.h>
#include <warnings.h>
#include <walletinitinterface.h>
@@ -63,7 +67,6 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/thread.hpp>
-#include <openssl/crypto.h>
#if ENABLE_ZMQ
#include <zmq/zmqabstractnotifier.h>
@@ -189,6 +192,7 @@ void Interrupt()
if (g_txindex) {
g_txindex->Interrupt();
}
+ ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Interrupt(); });
}
void Shutdown(InitInterfaces& interfaces)
@@ -203,7 +207,7 @@ void Shutdown(InitInterfaces& interfaces)
/// for example if the data directory was found to be locked.
/// Be sure that anything that writes files or flushes caches only does this if the respective
/// module was initialized.
- RenameThread("bitcoin-shutoff");
+ util::ThreadRename("shutoff");
mempool.AddTransactionsUpdated(1);
StopHTTPRPC();
@@ -220,6 +224,7 @@ void Shutdown(InitInterfaces& interfaces)
if (peerLogic) UnregisterValidationInterface(peerLogic.get());
if (g_connman) g_connman->Stop();
if (g_txindex) g_txindex->Stop();
+ ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Stop(); });
StopTorControl();
@@ -234,6 +239,7 @@ void Shutdown(InitInterfaces& interfaces)
g_connman.reset();
g_banman.reset();
g_txindex.reset();
+ DestroyAllBlockFilterIndexes();
if (::mempool.IsLoaded() && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool(::mempool);
@@ -339,14 +345,15 @@ static void registerSignalHandler(int signal, void(*handler)(int))
}
#endif
+static boost::signals2::connection rpc_notify_block_change_connection;
static void OnRPCStarted()
{
- uiInterface.NotifyBlockTip_connect(&RPCNotifyBlockChange);
+ rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(&RPCNotifyBlockChange);
}
static void OnRPCStopped()
{
- uiInterface.NotifyBlockTip_disconnect(&RPCNotifyBlockChange);
+ rpc_notify_block_change_connection.disconnect();
RPCNotifyBlockChange(false, nullptr);
g_best_block_cv.notify_all();
LogPrint(BCLog::RPC, "RPC stopped.\n");
@@ -404,6 +411,10 @@ void SetupServerArgs()
hidden_args.emplace_back("-sysperms");
#endif
gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blockfilterindex=<type>",
+ strprintf("Maintain an index of compact filters by block (default: %s, values: %s).", DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
+ " If <type> is not supplied or if <type> = 1, indexes for all known types are enabled.",
+ false, OptionsCategory::OPTIONS);
gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION);
@@ -496,12 +507,11 @@ void SetupServerArgs()
gArgs.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-maxtxfee=<amt>", strprintf("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)",
- CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", false, OptionsCategory::DEBUG_TEST);
@@ -511,7 +521,7 @@ void SetupServerArgs()
gArgs.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), true, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), true, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), true, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), true, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), false, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), false, OptionsCategory::NODE_RELAY);
@@ -658,7 +668,7 @@ static void CleanupBlockRevFiles()
static void ThreadImport(std::vector<fs::path> vImportFiles)
{
const CChainParams& chainparams = Params();
- RenameThread("bitcoin-loadblk");
+ util::ThreadRename("loadblk");
ScheduleBatchPriority();
{
@@ -828,19 +838,6 @@ void InitParameterInteraction()
if (gArgs.SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);
}
-
- // Warn if network-specific options (-addnode, -connect, etc) are
- // specified in default section of config file, but not overridden
- // on the command line or in this network's section of the config file.
- std::string network = gArgs.GetChainName();
- for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
- InitWarning(strprintf(_("Config setting for %s only applied on %s network when in [%s] section."), arg, network, network));
- }
-
- // Warn if unrecognized section name are present in the config file.
- for (const auto& section : gArgs.GetUnrecognizedSections()) {
- InitWarning(strprintf("%s:%i " + _("Section [%s] is not recognized."), section.m_file, section.m_line, section.m_name));
- }
}
static std::string ResolveErrMsg(const char * const optname, const std::string& strBind)
@@ -867,6 +864,7 @@ void InitLogging()
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
+ LogInstance().m_log_threadnames = gArgs.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
@@ -886,6 +884,7 @@ int nUserMaxConnections;
int nFD;
ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
int64_t peer_connect_timeout;
+std::vector<BlockFilterType> g_enabled_filter_types;
} // namespace
@@ -950,14 +949,46 @@ bool AppInitParameterInteraction()
// also see: InitParameterInteraction()
+ // Warn if network-specific options (-addnode, -connect, etc) are
+ // specified in default section of config file, but not overridden
+ // on the command line or in this network's section of the config file.
+ std::string network = gArgs.GetChainName();
+ for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
+ return InitError(strprintf(_("Config setting for %s only applied on %s network when in [%s] section."), arg, network, network));
+ }
+
+ // Warn if unrecognized section name are present in the config file.
+ for (const auto& section : gArgs.GetUnrecognizedSections()) {
+ InitWarning(strprintf("%s:%i " + _("Section [%s] is not recognized."), section.m_file, section.m_line, section.m_name));
+ }
+
if (!fs::is_directory(GetBlocksDir())) {
return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), gArgs.GetArg("-blocksdir", "").c_str()));
}
+ // parse and validate enabled filter types
+ std::string blockfilterindex_value = gArgs.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
+ if (blockfilterindex_value == "" || blockfilterindex_value == "1") {
+ g_enabled_filter_types = AllBlockFilterTypes();
+ } else if (blockfilterindex_value != "0") {
+ const std::vector<std::string> names = gArgs.GetArgs("-blockfilterindex");
+ g_enabled_filter_types.reserve(names.size());
+ for (const auto& name : names) {
+ BlockFilterType filter_type;
+ if (!BlockFilterTypeByName(name, filter_type)) {
+ return InitError(strprintf(_("Unknown -blockfilterindex value %s."), name));
+ }
+ g_enabled_filter_types.push_back(filter_type);
+ }
+ }
+
// if using block pruning, then disallow txindex
if (gArgs.GetArg("-prune", 0)) {
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
+ if (!g_enabled_filter_types.empty()) {
+ return InitError(_("Prune mode is incompatible with -blockfilterindex."));
+ }
}
// -bind and -whitebind can't be set when not listening
@@ -1121,22 +1152,6 @@ bool AppInitParameterInteraction()
dustRelayFee = CFeeRate(n);
}
- // This is required by both the wallet and node
- if (gArgs.IsArgSet("-maxtxfee"))
- {
- CAmount nMaxFee = 0;
- if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee))
- return InitError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")));
- if (nMaxFee > HIGH_MAX_TX_FEE)
- InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
- maxTxFee = nMaxFee;
- if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee)
- {
- return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
- gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
- }
- }
-
fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (chainparams.RequireStandard() && !fRequireStandard)
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
@@ -1274,7 +1289,7 @@ bool AppInitMain(InitInterfaces& interfaces)
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
if (nScriptCheckThreads) {
for (int i=0; i<nScriptCheckThreads-1; i++)
- threadGroup.create_thread(&ThreadScriptCheck);
+ threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
}
// Start the lightweight task scheduler thread
@@ -1448,6 +1463,13 @@ bool AppInitMain(InitInterfaces& interfaces)
nTotalCache -= nBlockTreeDBCache;
int64_t nTxIndexCache = std::min(nTotalCache / 8, gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0);
nTotalCache -= nTxIndexCache;
+ int64_t filter_index_cache = 0;
+ if (!g_enabled_filter_types.empty()) {
+ size_t n_indexes = g_enabled_filter_types.size();
+ int64_t max_cache = std::min(nTotalCache / 8, max_filter_index_cache << 20);
+ filter_index_cache = max_cache / n_indexes;
+ nTotalCache -= filter_index_cache * n_indexes;
+ }
int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache
nTotalCache -= nCoinDBCache;
@@ -1458,6 +1480,10 @@ bool AppInitMain(InitInterfaces& interfaces)
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
LogPrintf("* Using %.1f MiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024));
}
+ for (BlockFilterType filter_type : g_enabled_filter_types) {
+ LogPrintf("* Using %.1f MiB for %s block filter index database\n",
+ filter_index_cache * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
+ }
LogPrintf("* Using %.1f MiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024));
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024));
@@ -1468,11 +1494,11 @@ bool AppInitMain(InitInterfaces& interfaces)
uiInterface.InitMessage(_("Loading block index..."));
- LOCK(cs_main);
-
do {
const int64_t load_block_index_start_time = GetTimeMillis();
+ bool is_coinsview_empty;
try {
+ LOCK(cs_main);
UnloadBlockIndex();
pcoinsTip.reset();
pcoinsdbview.reset();
@@ -1544,7 +1570,7 @@ bool AppInitMain(InitInterfaces& interfaces)
// The on-disk coinsdb is now in a good state, create the cache
pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get()));
- bool is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
+ is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
if (!is_coinsview_empty) {
// LoadChainTip sets chainActive based on pcoinsTip's best block
if (!LoadChainTip(chainparams)) {
@@ -1553,18 +1579,25 @@ bool AppInitMain(InitInterfaces& interfaces)
}
assert(chainActive.Tip() != nullptr);
}
+ } catch (const std::exception& e) {
+ LogPrintf("%s\n", e.what());
+ strLoadError = _("Error opening block database");
+ break;
+ }
- if (!fReset) {
- // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
- // It both disconnects blocks based on chainActive, and drops block data in
- // mapBlockIndex based on lack of available witness data.
- uiInterface.InitMessage(_("Rewinding blocks..."));
- if (!RewindBlockIndex(chainparams)) {
- strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
- break;
- }
+ if (!fReset) {
+ // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
+ // It both disconnects blocks based on chainActive, and drops block data in
+ // mapBlockIndex based on lack of available witness data.
+ uiInterface.InitMessage(_("Rewinding blocks..."));
+ if (!RewindBlockIndex(chainparams)) {
+ strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
+ break;
}
+ }
+ try {
+ LOCK(cs_main);
if (!is_coinsview_empty) {
uiInterface.InitMessage(_("Verifying blocks..."));
if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
@@ -1638,6 +1671,11 @@ bool AppInitMain(InitInterfaces& interfaces)
g_txindex->Start();
}
+ for (const auto& filter_type : g_enabled_filter_types) {
+ InitBlockFilterIndex(filter_type, filter_index_cache, false, fReindex);
+ GetBlockFilterIndex(filter_type)->Start();
+ }
+
// ********************************************************* Step 9: load wallet
for (const auto& client : interfaces.chain_clients) {
if (!client->load()) {
@@ -1680,8 +1718,9 @@ bool AppInitMain(InitInterfaces& interfaces)
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.
// No locking, as this happens before any background thread is started.
+ boost::signals2::connection block_notify_genesis_wait_connection;
if (chainActive.Tip() == nullptr) {
- uiInterface.NotifyBlockTip_connect(BlockNotifyGenesisWait);
+ block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(BlockNotifyGenesisWait);
} else {
fHaveGenesis = true;
}
@@ -1705,7 +1744,7 @@ bool AppInitMain(InitInterfaces& interfaces)
while (!fHaveGenesis && !ShutdownRequested()) {
g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
}
- uiInterface.NotifyBlockTip_disconnect(BlockNotifyGenesisWait);
+ block_notify_genesis_wait_connection.disconnect();
}
if (ShutdownRequested()) {
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 2eecea28d0..839af650bb 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -6,22 +6,30 @@
#include <chain.h>
#include <chainparams.h>
+#include <interfaces/handler.h>
#include <interfaces/wallet.h>
#include <net.h>
+#include <node/coin.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
+#include <policy/settings.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <protocol.h>
+#include <rpc/protocol.h>
+#include <rpc/server.h>
+#include <shutdown.h>
#include <sync.h>
#include <threadsafety.h>
#include <timedata.h>
#include <txmempool.h>
#include <ui_interface.h>
#include <uint256.h>
+#include <univalue.h>
#include <util/system.h>
#include <validation.h>
+#include <validationinterface.h>
#include <memory>
#include <utility>
@@ -76,29 +84,15 @@ class LockImpl : public Chain::Lock
CBlockIndex* block = ::chainActive[height];
return block && ((block->nStatus & BLOCK_HAVE_DATA) != 0) && block->nTx > 0;
}
- Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) override
+ Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) override
{
- CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time);
+ CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time, height);
if (block) {
if (hash) *hash = block->GetBlockHash();
return block->nHeight;
}
return nullopt;
}
- Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) override
- {
- // TODO: Could update CChain::FindEarliestAtLeast() to take a height
- // parameter and use it with std::lower_bound() to make this
- // implementation more efficient and allow combining
- // findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into one
- // method.
- for (CBlockIndex* block = ::chainActive[height]; block; block = ::chainActive.Next(block)) {
- if (block->GetBlockTime() >= time) {
- return block->nHeight;
- }
- }
- return nullopt;
- }
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
{
if (::fPruneMode) {
@@ -161,6 +155,88 @@ class LockingStateImpl : public LockImpl, public UniqueLock<CCriticalSection>
using UniqueLock::UniqueLock;
};
+class NotificationsHandlerImpl : public Handler, CValidationInterface
+{
+public:
+ explicit NotificationsHandlerImpl(Chain& chain, Chain::Notifications& notifications)
+ : m_chain(chain), m_notifications(&notifications)
+ {
+ RegisterValidationInterface(this);
+ }
+ ~NotificationsHandlerImpl() override { disconnect(); }
+ void disconnect() override
+ {
+ if (m_notifications) {
+ m_notifications = nullptr;
+ UnregisterValidationInterface(this);
+ }
+ }
+ void TransactionAddedToMempool(const CTransactionRef& tx) override
+ {
+ m_notifications->TransactionAddedToMempool(tx);
+ }
+ void TransactionRemovedFromMempool(const CTransactionRef& tx) override
+ {
+ m_notifications->TransactionRemovedFromMempool(tx);
+ }
+ void BlockConnected(const std::shared_ptr<const CBlock>& block,
+ const CBlockIndex* index,
+ const std::vector<CTransactionRef>& tx_conflicted) override
+ {
+ m_notifications->BlockConnected(*block, tx_conflicted);
+ }
+ void BlockDisconnected(const std::shared_ptr<const CBlock>& block) override
+ {
+ m_notifications->BlockDisconnected(*block);
+ }
+ void UpdatedBlockTip(const CBlockIndex* index, const CBlockIndex* fork_index, bool is_ibd) override
+ {
+ m_notifications->UpdatedBlockTip();
+ }
+ void ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->ChainStateFlushed(locator); }
+ Chain& m_chain;
+ Chain::Notifications* m_notifications;
+};
+
+class RpcHandlerImpl : public Handler
+{
+public:
+ RpcHandlerImpl(const CRPCCommand& command) : m_command(command), m_wrapped_command(&command)
+ {
+ m_command.actor = [this](const JSONRPCRequest& request, UniValue& result, bool last_handler) {
+ if (!m_wrapped_command) return false;
+ try {
+ return m_wrapped_command->actor(request, result, last_handler);
+ } catch (const UniValue& e) {
+ // If this is not the last handler and a wallet not found
+ // exception was thrown, return false so the next handler can
+ // try to handle the request. Otherwise, reraise the exception.
+ if (!last_handler) {
+ const UniValue& code = e["code"];
+ if (code.isNum() && code.get_int() == RPC_WALLET_NOT_FOUND) {
+ return false;
+ }
+ }
+ throw;
+ }
+ };
+ ::tableRPC.appendCommand(m_command.name, &m_command);
+ }
+
+ void disconnect() override final
+ {
+ if (m_wrapped_command) {
+ m_wrapped_command = nullptr;
+ ::tableRPC.removeCommand(m_command.name, &m_command);
+ }
+ }
+
+ ~RpcHandlerImpl() override { disconnect(); }
+
+ CRPCCommand m_command;
+ const CRPCCommand* m_wrapped_command;
+};
+
class ChainImpl : public Chain
{
public:
@@ -194,6 +270,7 @@ public:
}
return true;
}
+ void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(coins); }
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
@@ -245,17 +322,46 @@ public:
{
return ::mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
- CAmount maxTxFee() override { return ::maxTxFee; }
+ CFeeRate relayMinFee() override { return ::minRelayTxFee; }
+ CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
+ CFeeRate relayDustFee() override { return ::dustRelayFee; }
bool getPruneMode() override { return ::fPruneMode; }
bool p2pEnabled() override { return g_connman != nullptr; }
+ bool isReadyToBroadcast() override { return !::fImporting && !::fReindex && !IsInitialBlockDownload(); }
bool isInitialBlockDownload() override { return IsInitialBlockDownload(); }
+ bool shutdownRequested() override { return ShutdownRequested(); }
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); }
void initWarning(const std::string& message) override { InitWarning(message); }
void initError(const std::string& message) override { InitError(message); }
void loadWallet(std::unique_ptr<Wallet> wallet) override { ::uiInterface.LoadWallet(wallet); }
+ void showProgress(const std::string& title, int progress, bool resume_possible) override
+ {
+ ::uiInterface.ShowProgress(title, progress, resume_possible);
+ }
+ std::unique_ptr<Handler> handleNotifications(Notifications& notifications) override
+ {
+ return MakeUnique<NotificationsHandlerImpl>(*this, notifications);
+ }
+ void waitForNotifications() override { SyncWithValidationInterfaceQueue(); }
+ std::unique_ptr<Handler> handleRpc(const CRPCCommand& command) override
+ {
+ return MakeUnique<RpcHandlerImpl>(command);
+ }
+ bool rpcEnableDeprecated(const std::string& method) override { return IsDeprecatedRPCEnabled(method); }
+ void rpcRunLater(const std::string& name, std::function<void()> fn, int64_t seconds) override
+ {
+ RPCRunLater(name, std::move(fn), seconds);
+ }
+ int rpcSerializationFlags() override { return RPCSerializationFlags(); }
+ void requestMempoolTransactions(Notifications& notifications) override
+ {
+ LOCK2(::cs_main, ::mempool.cs);
+ for (const CTxMemPoolEntry& entry : ::mempool.mapTx) {
+ notifications.TransactionAddedToMempool(entry.GetSharedTx());
+ }
+ }
};
-
} // namespace
std::unique_ptr<Chain> MakeChain() { return MakeUnique<ChainImpl>(); }
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 037e8e9ff5..7564ad26ac 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -16,8 +16,10 @@
class CBlock;
class CFeeRate;
+class CRPCCommand;
class CScheduler;
class CValidationState;
+class Coin;
class uint256;
enum class RBFTransactionState;
struct CBlockLocator;
@@ -25,6 +27,7 @@ struct FeeCalculation;
namespace interfaces {
+class Handler;
class Wallet;
//! Interface giving clients (wallet processes, maybe other analysis tools in
@@ -40,6 +43,12 @@ class Wallet;
//! asynchronously
//! (https://github.com/bitcoin/bitcoin/pull/10973#issuecomment-380101269).
//!
+//! * The isPotentialTip() and waitForNotifications() methods are too low-level
+//! and should be replaced with a higher level
+//! waitForNotificationsUpTo(block_hash) method that the wallet can call
+//! instead
+//! (https://github.com/bitcoin/bitcoin/pull/10973#discussion_r266995234).
+//!
//! * The relayTransactions() and submitToMemoryPool() methods could be replaced
//! with a higher-level broadcastTransaction method
//! (https://github.com/bitcoin/bitcoin/pull/14978#issuecomment-459373984).
@@ -48,6 +57,10 @@ class Wallet;
//! notifications to the GUI should go away when GUI and wallet can directly
//! communicate with each other without going through the node
//! (https://github.com/bitcoin/bitcoin/pull/15288#discussion_r253321096).
+//!
+//! * The handleRpc, registerRpcs, rpcEnableDeprecated methods and other RPC
+//! methods can go away if wallets listen for HTTP requests on their own
+//! ports instead of registering to handle requests on the node HTTP port.
class Chain
{
public:
@@ -92,27 +105,19 @@ public:
virtual bool haveBlockOnDisk(int height) = 0;
//! Return height of the first block in the chain with timestamp equal
- //! or greater than the given time, or nullopt if there is no block with
- //! a high enough timestamp. Also return the block hash as an optional
- //! output parameter (to avoid the cost of a second lookup in case this
- //! information is needed.)
- virtual Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) = 0;
-
- //! Return height of the first block in the chain with timestamp equal
//! or greater than the given time and height equal or greater than the
- //! given height, or nullopt if there is no such block.
- //!
- //! Calling this with height 0 is equivalent to calling
- //! findFirstBlockWithTime, but less efficient because it requires a
- //! linear instead of a binary search.
- virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) = 0;
+ //! given height, or nullopt if there is no block with a high enough
+ //! timestamp and height. Also return the block hash as an optional output parameter
+ //! (to avoid the cost of a second lookup in case this information is needed.)
+ virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height, uint256* hash) = 0;
//! Return height of last block in the specified range which is pruned, or
//! nullopt if no block in the range is pruned. Range is inclusive.
virtual Optional<int> findPruned(int start_height = 0, Optional<int> stop_height = nullopt) = 0;
- //! Return height of the highest block on the chain that is an ancestor
- //! of the specified block, or nullopt if no common ancestor is found.
+ //! Return height of the specified block if it is on the chain, otherwise
+ //! return the height of the highest block on chain that's an ancestor
+ //! of the specified block, or nullopt if there is no common ancestor.
//! Also return the height of the specified block as an optional output
//! parameter (to avoid the cost of a second hash lookup in case this
//! information is desired).
@@ -126,9 +131,9 @@ public:
//! Get locator for the current chain tip.
virtual CBlockLocator getTipLocator() = 0;
- //! Return height of the latest block common to locator and chain, which
- //! is guaranteed to be an ancestor of the block used to create the
- //! locator.
+ //! Return height of the highest block on chain in common with the locator,
+ //! which will either be the original block used to create the locator,
+ //! or one of its ancestors.
virtual Optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
//! Check if transaction will be final given chain height current time.
@@ -160,6 +165,11 @@ public:
int64_t* time = nullptr,
int64_t* max_time = nullptr) = 0;
+ //! Look up unspent output information. Returns coins in the mempool and in
+ //! the current chain UTXO set. Iterates through all the keys in the map and
+ //! populates the values.
+ virtual void findCoins(std::map<COutPoint, Coin>& coins) = 0;
+
//! Estimate fraction of total transactions verified if blocks up to
//! the specified block hash are verified.
virtual double guessVerificationProgress(const uint256& block_hash) = 0;
@@ -188,11 +198,14 @@ public:
//! Mempool minimum fee.
virtual CFeeRate mempoolMinFee() = 0;
- //! Node max tx fee setting (-maxtxfee).
- //! This could be replaced by a per-wallet max fee, as proposed at
- //! https://github.com/bitcoin/bitcoin/issues/15355
- //! But for the time being, wallets call this to access the node setting.
- virtual CAmount maxTxFee() = 0;
+ //! Relay current minimum fee (from -minrelaytxfee and -incrementalrelayfee settings).
+ virtual CFeeRate relayMinFee() = 0;
+
+ //! Relay incremental fee setting (-incrementalrelayfee), reflecting cost of relay.
+ virtual CFeeRate relayIncrementalFee() = 0;
+
+ //! Relay dust fee setting (-dustrelayfee), reflecting lowest rate it's economical to spend.
+ virtual CFeeRate relayDustFee() = 0;
//! Check if pruning is enabled.
virtual bool getPruneMode() = 0;
@@ -200,9 +213,15 @@ public:
//! Check if p2p enabled.
virtual bool p2pEnabled() = 0;
- // Check if in IBD.
+ //! Check if the node is ready to broadcast transactions.
+ virtual bool isReadyToBroadcast() = 0;
+
+ //! Check if in IBD.
virtual bool isInitialBlockDownload() = 0;
+ //! Check if shutdown requested.
+ virtual bool shutdownRequested() = 0;
+
//! Get adjusted time.
virtual int64_t getAdjustedTime() = 0;
@@ -217,6 +236,51 @@ public:
//! Send wallet load notification to the GUI.
virtual void loadWallet(std::unique_ptr<Wallet> wallet) = 0;
+
+ //! Send progress indicator.
+ virtual void showProgress(const std::string& title, int progress, bool resume_possible) = 0;
+
+ //! Chain notifications.
+ class Notifications
+ {
+ 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 UpdatedBlockTip() {}
+ virtual void ChainStateFlushed(const CBlockLocator& locator) {}
+ };
+
+ //! Register handler for notifications.
+ virtual std::unique_ptr<Handler> handleNotifications(Notifications& notifications) = 0;
+
+ //! Wait for pending notifications to be handled.
+ virtual void waitForNotifications() = 0;
+
+ //! Register handler for RPC. Command is not copied, so reference
+ //! needs to remain valid until Handler is disconnected.
+ virtual std::unique_ptr<Handler> handleRpc(const CRPCCommand& command) = 0;
+
+ //! Check if deprecated RPC is enabled.
+ virtual bool rpcEnableDeprecated(const std::string& method) = 0;
+
+ //! Run function after given number of seconds. Cancel any previous calls with same name.
+ virtual void rpcRunLater(const std::string& name, std::function<void()> fn, int64_t seconds) = 0;
+
+ //! Current RPC serialization flags.
+ virtual int rpcSerializationFlags() = 0;
+
+ //! Synchronously send TransactionAddedToMempool notifications about all
+ //! current mempool transactions to the specified handler and return after
+ //! the last one is sent. These notifications aren't coordinated with async
+ //! notifications sent by handleNotifications, so out of date async
+ //! notifications from handleNotifications can arrive during and after
+ //! synchronous notifications from requestMempoolTransactions. Clients need
+ //! to be prepared to handle this by ignoring notifications about unknown
+ //! removed transactions and already added new transactions.
+ virtual void requestMempoolTransactions(Notifications& notifications) = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 6f7dce0c24..f3ee8fe364 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -20,6 +20,7 @@
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <primitives/block.h>
#include <rpc/server.h>
#include <scheduler.h>
@@ -206,7 +207,6 @@ public:
}
}
bool getNetworkActive() override { return g_connman && g_connman->GetNetworkActive(); }
- CAmount getMaxTxFee() override { return ::maxTxFee; }
CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* returned_target = nullptr) override
{
FeeCalculation fee_calc;
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 76b93af234..1ccd2a31b7 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -159,9 +159,6 @@ public:
//! Get network active.
virtual bool getNetworkActive() = 0;
- //! Get max tx fee.
- virtual CAmount getMaxTxFee() = 0;
-
//! Estimate smart fee.
virtual CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* returned_target = nullptr) = 0;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 7abbee0912..b57299d78d 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -29,6 +29,7 @@
#include <wallet/feebumper.h>
#include <wallet/fees.h>
#include <wallet/rpcwallet.h>
+#include <wallet/load.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
@@ -47,8 +48,6 @@ public:
const CTransaction& get() override { return *m_tx; }
- int64_t getVirtualSize() override { return GetVirtualTransactionSize(*m_tx); }
-
bool commit(WalletValueMap value_map,
WalletOrderForm order_form,
std::string& reject_reason) override
@@ -99,12 +98,8 @@ WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, co
//! Construct wallet tx status struct.
WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx)
{
- LockAnnotation lock(::cs_main); // Temporary, for mapBlockIndex below. Removed in upcoming commit.
-
WalletTxStatus result;
- auto mi = ::mapBlockIndex.find(wtx.hashBlock);
- CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr;
- result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max());
+ result.block_height = locked_chain.getBlockHeight(wtx.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.time_received = wtx.nTimeReceived;
@@ -274,8 +269,13 @@ public:
CAmount& new_fee,
CMutableTransaction& mtx) override
{
- return feebumper::CreateTransaction(m_wallet.get(), txid, coin_control, total_fee, errors, old_fee, new_fee, mtx) ==
- feebumper::Result::OK;
+ if (total_fee > 0) {
+ return feebumper::CreateTotalBumpTransaction(m_wallet.get(), txid, coin_control, total_fee, errors, old_fee, new_fee, mtx) ==
+ feebumper::Result::OK;
+ } else {
+ return feebumper::CreateRateBumpTransaction(m_wallet.get(), txid, coin_control, errors, old_fee, new_fee, mtx) ==
+ feebumper::Result::OK;
+ }
}
bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(m_wallet.get(), mtx); }
bool commitBumpTransaction(const uint256& txid,
@@ -364,15 +364,16 @@ public:
}
WalletBalances getBalances() override
{
+ const auto bal = m_wallet->GetBalance();
WalletBalances result;
- result.balance = m_wallet->GetBalance();
- result.unconfirmed_balance = m_wallet->GetUnconfirmedBalance();
- result.immature_balance = m_wallet->GetImmatureBalance();
+ result.balance = bal.m_mine_trusted;
+ result.unconfirmed_balance = bal.m_mine_untrusted_pending;
+ result.immature_balance = bal.m_mine_immature;
result.have_watch_only = m_wallet->HaveWatchOnly();
if (result.have_watch_only) {
- result.watch_only_balance = m_wallet->GetBalance(ISMINE_WATCH_ONLY);
- result.unconfirmed_watch_only_balance = m_wallet->GetUnconfirmedWatchOnlyBalance();
- result.immature_watch_only_balance = m_wallet->GetImmatureWatchOnlyBalance();
+ result.watch_only_balance = bal.m_watchonly_trusted;
+ result.unconfirmed_watch_only_balance = bal.m_watchonly_untrusted_pending;
+ result.immature_watch_only_balance = bal.m_watchonly_immature;
}
return result;
}
@@ -388,7 +389,7 @@ public:
num_blocks = locked_chain->getHeight().get_value_or(-1);
return true;
}
- CAmount getBalance() override { return m_wallet->GetBalance(); }
+ CAmount getBalance() override { return m_wallet->GetBalance().m_mine_trusted; }
CAmount getAvailableBalance(const CCoinControl& coin_control) override
{
return m_wallet->GetAvailableBalance(&coin_control);
@@ -468,6 +469,7 @@ public:
bool IsWalletFlagSet(uint64_t flag) override { return m_wallet->IsWalletFlagSet(flag); }
OutputType getDefaultAddressType() override { return m_wallet->m_default_address_type; }
OutputType getDefaultChangeType() override { return m_wallet->m_default_change_type; }
+ CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; }
void remove() override
{
RemoveWallet(m_wallet);
@@ -514,7 +516,7 @@ public:
: m_chain(chain), m_wallet_filenames(std::move(wallet_filenames))
{
}
- void registerRpcs() override { return RegisterWalletRPCCommands(::tableRPC); }
+ void registerRpcs() override { return RegisterWalletRPCCommands(m_chain, m_rpc_handlers); }
bool verify() override { return VerifyWallets(m_chain, m_wallet_filenames); }
bool load() override { return LoadWallets(m_chain, m_wallet_filenames); }
void start(CScheduler& scheduler) override { return StartWallets(scheduler); }
@@ -524,6 +526,7 @@ public:
Chain& m_chain;
std::vector<std::string> m_wallet_filenames;
+ std::vector<std::unique_ptr<Handler>> m_rpc_handlers;
};
} // namespace
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index a931e5fafb..7096f54047 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -247,6 +247,9 @@ public:
// Get default change type.
virtual OutputType getDefaultChangeType() = 0;
+ //! Get max tx fee.
+ virtual CAmount getDefaultMaxTxFee() = 0;
+
// Remove wallet.
virtual void remove() = 0;
@@ -292,9 +295,6 @@ public:
//! Get transaction data.
virtual const CTransaction& get() = 0;
- //! Get virtual transaction size.
- virtual int64_t getVirtualSize() = 0;
-
//! Send pending transaction and commit to wallet.
virtual bool commit(WalletValueMap value_map,
WalletOrderForm order_form,
diff --git a/src/logging.cpp b/src/logging.cpp
index 36cad6573a..3eda4995db 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -4,8 +4,11 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <logging.h>
+#include <util/threadnames.h>
#include <util/time.h>
+#include <mutex>
+
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
BCLog::Logger& LogInstance()
@@ -174,7 +177,7 @@ std::vector<CLogCategoryActive> ListActiveLogCategories()
return ret;
}
-std::string BCLog::Logger::LogTimestampStr(const std::string &str)
+std::string BCLog::Logger::LogTimestampStr(const std::string& str)
{
std::string strStamped;
@@ -196,21 +199,24 @@ std::string BCLog::Logger::LogTimestampStr(const std::string &str)
} else
strStamped = str;
- if (!str.empty() && str[str.size()-1] == '\n')
- m_started_new_line = true;
- else
- m_started_new_line = false;
-
return strStamped;
}
void BCLog::Logger::LogPrintStr(const std::string &str)
{
- std::string strTimestamped = LogTimestampStr(str);
+ std::string str_prefixed = str;
+
+ if (m_log_threadnames && m_started_new_line) {
+ str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
+ }
+
+ str_prefixed = LogTimestampStr(str_prefixed);
+
+ m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
if (m_print_to_console) {
// print to console
- fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
+ fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
fflush(stdout);
}
if (m_print_to_file) {
@@ -218,7 +224,7 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
// buffer if we haven't opened the log yet
if (m_fileout == nullptr) {
- m_msgs_before_open.push_back(strTimestamped);
+ m_msgs_before_open.push_back(str_prefixed);
}
else
{
@@ -232,7 +238,7 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
m_fileout = new_fileout;
}
}
- FileWriteStr(strTimestamped, m_fileout);
+ FileWriteStr(str_prefixed, m_fileout);
}
}
}
diff --git a/src/logging.h b/src/logging.h
index ac9d0dc0c7..e399d4c307 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -19,6 +19,7 @@
static const bool DEFAULT_LOGTIMEMICROS = false;
static const bool DEFAULT_LOGIPS = false;
static const bool DEFAULT_LOGTIMESTAMPS = true;
+static const bool DEFAULT_LOGTHREADNAMES = false;
extern const char * const DEFAULT_DEBUGLOGFILE;
extern bool fLogIPs;
@@ -81,6 +82,7 @@ namespace BCLog {
bool m_log_timestamps = DEFAULT_LOGTIMESTAMPS;
bool m_log_time_micros = DEFAULT_LOGTIMEMICROS;
+ bool m_log_threadnames = DEFAULT_LOGTHREADNAMES;
fs::path m_file_path;
std::atomic<bool> m_reopen_file{false};
diff --git a/src/miner.cpp b/src/miner.cpp
index 80a2f8f018..6a88e8321d 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -23,6 +23,7 @@
#include <timedata.h>
#include <util/moneystr.h>
#include <util/system.h>
+#include <util/validation.h>
#include <validationinterface.h>
#include <algorithm>
diff --git a/src/net.cpp b/src/net.cpp
index 87f1ef0577..1335804b06 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1765,9 +1765,15 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
addr = addrman.Select(fFeeler);
}
- // if we selected an invalid address, restart
- if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
+ // Require outbound connections, other than feelers, to be to distinct network groups
+ if (!fFeeler && setConnected.count(addr.GetGroup())) {
break;
+ }
+
+ // if we selected an invalid or local address, restart
+ if (!addr.IsValid() || IsLocal(addr)) {
+ break;
+ }
// If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
// stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
@@ -2621,7 +2627,6 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
{
hSocket = hSocketIn;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
- strSubVer = "";
hashContinue = uint256();
filterInventoryKnown.reset();
pfilter = MakeUnique<CBloomFilter>();
diff --git a/src/net.h b/src/net.h
index f4a90e01f1..7af33ef13b 100644
--- a/src/net.h
+++ b/src/net.h
@@ -53,7 +53,7 @@ static const unsigned int MAX_LOCATOR_SZ = 101;
static const unsigned int MAX_ADDR_TO_SEND = 1000;
/** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
-/** Maximum length of strSubVer in `version` message */
+/** Maximum length of the user agent string in `version` message */
static const unsigned int MAX_SUBVERSION_LENGTH = 256;
/** Maximum number of automatic outgoing nodes */
static const int MAX_OUTBOUND_CONNECTIONS = 8;
@@ -650,12 +650,12 @@ public:
// Bind address of our side of the connection
const CAddress addrBind;
std::atomic<int> nVersion{0};
- // strSubVer is whatever byte array we read from the wire. However, this field is intended
- // to be printed out, displayed to humans in various forms and so on. So we sanitize it and
- // store the sanitized version in cleanSubVer. The original should be used when dealing with
- // the network or wire types and the cleaned string used when displayed or logged.
- std::string strSubVer GUARDED_BY(cs_SubVer), cleanSubVer GUARDED_BY(cs_SubVer);
- CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer
+ RecursiveMutex cs_SubVer;
+ /**
+ * cleanSubVer is a sanitized string of the user agent byte array we read
+ * from the wire. This cleaned string can safely be logged or displayed.
+ */
+ std::string cleanSubVer GUARDED_BY(cs_SubVer){};
bool m_prefer_evict{false}; // This peer is preferred for eviction.
bool fWhitelisted{false}; // This peer can bypass DoS banning.
bool fFeeler{false}; // If true this node is being used as a short lived feeler.
@@ -739,6 +739,8 @@ public:
CAmount lastSentFeeFilter{0};
int64_t nextSendTimeFeeFilter{0};
+ std::set<uint256> orphan_work_set;
+
CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false);
~CNode();
CNode(const CNode&) = delete;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 18dd2f010c..74e33189dc 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -29,6 +29,7 @@
#include <util/system.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
+#include <util/validation.h>
#include <memory>
@@ -175,8 +176,6 @@ namespace {
/** Expiration-time ordered list of (expire time, relay map entry) pairs. */
std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration GUARDED_BY(cs_main);
- std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
-
struct IteratorComparator
{
template<typename I>
@@ -1121,8 +1120,6 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
});
connman->WakeMessageHandler();
}
-
- nTimeBestReceived = GetTime();
}
/**
@@ -1713,6 +1710,67 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
return true;
}
+void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
+{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(g_cs_orphans);
+ std::set<NodeId> setMisbehaving;
+ bool done = false;
+ while (!done && !orphan_work_set.empty()) {
+ const uint256 orphanHash = *orphan_work_set.begin();
+ orphan_work_set.erase(orphan_work_set.begin());
+
+ auto orphan_it = mapOrphanTransactions.find(orphanHash);
+ if (orphan_it == mapOrphanTransactions.end()) continue;
+
+ const CTransactionRef porphanTx = orphan_it->second.tx;
+ const CTransaction& orphanTx = *porphanTx;
+ NodeId fromPeer = orphan_it->second.fromPeer;
+ bool fMissingInputs2 = false;
+ // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
+ // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
+ // anyone relaying LegitTxX banned)
+ CValidationState stateDummy;
+
+ if (setMisbehaving.count(fromPeer)) continue;
+ if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
+ LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
+ RelayTransaction(orphanTx, connman);
+ for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
+ auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(orphanHash, i));
+ if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
+ for (const auto& elem : it_by_prev->second) {
+ orphan_work_set.insert(elem->first);
+ }
+ }
+ }
+ EraseOrphanTx(orphanHash);
+ done = true;
+ } else if (!fMissingInputs2) {
+ int nDos = 0;
+ if (stateDummy.IsInvalid(nDos) && nDos > 0) {
+ // Punish peer that gave us an invalid orphan tx
+ Misbehaving(fromPeer, nDos);
+ setMisbehaving.insert(fromPeer);
+ LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
+ }
+ // Has inputs but not accepted to mempool
+ // Probably non-standard or insufficient fee
+ LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
+ if (!orphanTx.HasWitness() && !stateDummy.CorruptionPossible()) {
+ // Do not use rejection cache for witness transactions or
+ // witness-stripped transactions, as they can have been malleated.
+ // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
+ assert(recentRejects);
+ recentRejects->insert(orphanHash);
+ }
+ EraseOrphanTx(orphanHash);
+ done = true;
+ }
+ mempool.check(pcoinsTip.get());
+ }
+}
+
bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc, bool enable_bip61)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
@@ -1782,7 +1840,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
ServiceFlags nServices;
int nVersion;
int nSendVersion;
- std::string strSubVer;
std::string cleanSubVer;
int nStartingHeight = -1;
bool fRelay = true;
@@ -1819,6 +1876,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!vRecv.empty())
vRecv >> addrFrom >> nNonce;
if (!vRecv.empty()) {
+ std::string strSubVer;
vRecv >> LIMITED_STRING(strSubVer, MAX_SUBVERSION_LENGTH);
cleanSubVer = SanitizeString(strSubVer);
}
@@ -1850,7 +1908,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->SetAddrLocal(addrMe);
{
LOCK(pfrom->cs_SubVer);
- pfrom->strSubVer = strSubVer;
pfrom->cleanSubVer = cleanSubVer;
}
pfrom->nStartingHeight = nStartingHeight;
@@ -2022,6 +2079,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom->AddAddressKnown(addr);
+ if (g_banman->IsBanned(addr)) continue; // Do not process banned addresses beyond remembering we received them
bool fReachable = IsReachable(addr);
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
@@ -2341,8 +2399,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
- std::deque<COutPoint> vWorkQueue;
- std::vector<uint256> vEraseQueue;
CTransactionRef ptx;
vRecv >> ptx;
const CTransaction& tx = *ptx;
@@ -2367,7 +2423,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
mempool.check(pcoinsTip.get());
RelayTransaction(tx, connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
- vWorkQueue.emplace_back(inv.hash, i);
+ auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));
+ if (it_by_prev != mapOrphanTransactionsByPrev.end()) {
+ for (const auto& elem : it_by_prev->second) {
+ pfrom->orphan_work_set.insert(elem->first);
+ }
+ }
}
pfrom->nLastTXTime = GetTime();
@@ -2378,65 +2439,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
mempool.size(), mempool.DynamicMemoryUsage() / 1000);
// Recursively process any orphan transactions that depended on this one
- std::set<NodeId> setMisbehaving;
- while (!vWorkQueue.empty()) {
- auto itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue.front());
- vWorkQueue.pop_front();
- if (itByPrev == mapOrphanTransactionsByPrev.end())
- continue;
- for (auto mi = itByPrev->second.begin();
- mi != itByPrev->second.end();
- ++mi)
- {
- const CTransactionRef& porphanTx = (*mi)->second.tx;
- const CTransaction& orphanTx = *porphanTx;
- const uint256& orphanHash = orphanTx.GetHash();
- NodeId fromPeer = (*mi)->second.fromPeer;
- bool fMissingInputs2 = false;
- // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
- // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
- // anyone relaying LegitTxX banned)
- CValidationState stateDummy;
-
-
- if (setMisbehaving.count(fromPeer))
- continue;
- if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
- LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanTx, connman);
- for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
- vWorkQueue.emplace_back(orphanHash, i);
- }
- vEraseQueue.push_back(orphanHash);
- }
- else if (!fMissingInputs2)
- {
- int nDos = 0;
- if (stateDummy.IsInvalid(nDos) && nDos > 0)
- {
- // Punish peer that gave us an invalid orphan tx
- Misbehaving(fromPeer, nDos);
- setMisbehaving.insert(fromPeer);
- LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
- }
- // Has inputs but not accepted to mempool
- // Probably non-standard or insufficient fee
- LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
- vEraseQueue.push_back(orphanHash);
- if (!orphanTx.HasWitness() && !stateDummy.CorruptionPossible()) {
- // Do not use rejection cache for witness transactions or
- // witness-stripped transactions, as they can have been malleated.
- // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
- assert(recentRejects);
- recentRejects->insert(orphanHash);
- }
- }
- mempool.check(pcoinsTip.get());
- }
- }
-
- for (const uint256& hash : vEraseQueue)
- EraseOrphanTx(hash);
+ ProcessOrphanTx(connman, pfrom->orphan_work_set, lRemovedTxn);
}
else if (fMissingInputs)
{
@@ -2540,8 +2543,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
- if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
+ if (strCommand == NetMsgType::CMPCTBLOCK)
{
+ // Ignore cmpctblock received while importing
+ if (fImporting || fReindex) {
+ LogPrint(BCLog::NET, "Unexpected cmpctblock message received from peer %d\n", pfrom->GetId());
+ return true;
+ }
+
CBlockHeaderAndShortTxIDs cmpctblock;
vRecv >> cmpctblock;
@@ -2761,8 +2770,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
- if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing
+ if (strCommand == NetMsgType::BLOCKTXN)
{
+ // Ignore blocktxn received while importing
+ if (fImporting || fReindex) {
+ LogPrint(BCLog::NET, "Unexpected blocktxn message received from peer %d\n", pfrom->GetId());
+ return true;
+ }
+
BlockTransactions resp;
vRecv >> resp;
@@ -2836,8 +2851,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return true;
}
- if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing
+ if (strCommand == NetMsgType::HEADERS)
{
+ // Ignore headers received while importing
+ if (fImporting || fReindex) {
+ LogPrint(BCLog::NET, "Unexpected headers message received from peer %d\n", pfrom->GetId());
+ return true;
+ }
+
std::vector<CBlockHeader> headers;
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
@@ -2861,8 +2882,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
return ProcessHeadersMessage(pfrom, connman, headers, chainparams, should_punish);
}
- if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
+ if (strCommand == NetMsgType::BLOCK)
{
+ // Ignore block received while importing
+ if (fImporting || fReindex) {
+ LogPrint(BCLog::NET, "Unexpected block message received from peer %d\n", pfrom->GetId());
+ return true;
+ }
+
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
vRecv >> *pblock;
@@ -2912,8 +2939,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->vAddrToSend.clear();
std::vector<CAddress> vAddr = connman->GetAddresses();
FastRandomContext insecure_rand;
- for (const CAddress &addr : vAddr)
- pfrom->PushAddress(addr, insecure_rand);
+ for (const CAddress &addr : vAddr) {
+ if (!g_banman->IsBanned(addr)) {
+ pfrom->PushAddress(addr, insecure_rand);
+ }
+ }
return true;
}
@@ -3141,11 +3171,21 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
if (!pfrom->vRecvGetData.empty())
ProcessGetData(pfrom, chainparams, connman, interruptMsgProc);
+ if (!pfrom->orphan_work_set.empty()) {
+ std::list<CTransactionRef> removed_txn;
+ LOCK2(cs_main, g_cs_orphans);
+ ProcessOrphanTx(connman, pfrom->orphan_work_set, removed_txn);
+ for (const CTransactionRef& removedTx : removed_txn) {
+ AddToCompactExtraTransactions(removedTx);
+ }
+ }
+
if (pfrom->fDisconnect)
return false;
// this maintains the order of responses
if (!pfrom->vRecvGetData.empty()) return true;
+ if (!pfrom->orphan_work_set.empty()) return true;
// Don't bother if send buffer is too full to respond anyway
if (pfrom->fPauseSend)
@@ -3507,14 +3547,6 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
}
}
- // Resend wallet transactions that haven't gotten in a block yet
- // Except during reindex, importing and IBD, when old wallet
- // transactions become unconfirmed and spams other nodes.
- if (!fReindex && !fImporting && !IsInitialBlockDownload())
- {
- GetMainSignals().Broadcast(nTimeBestReceived, connman);
- }
-
//
// Try sending block announcements via headers
//
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 58e45c2c02..6ee2d8a4b3 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -14,6 +14,11 @@ static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
// 0xFD + sha256("bitcoin")[0:5]
static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 };
+/**
+ * Construct an unspecified IPv6 network address (::/128).
+ *
+ * @note This address is considered invalid by CNetAddr::IsValid()
+ */
CNetAddr::CNetAddr()
{
memset(ip, 0, sizeof(ip));
@@ -40,6 +45,20 @@ void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
}
}
+/**
+ * Try to make this a dummy address that maps the specified name into IPv6 like
+ * so: (0xFD + %sha256("bitcoin")[0:5]) + %sha256(name)[0:10]. Such dummy
+ * addresses have a prefix of fd6b:88c0:8724::/48 and are guaranteed to not be
+ * publicly routable as it falls under RFC4193's fc00::/7 subnet allocated to
+ * unique-local addresses.
+ *
+ * CAddrMan uses these fake addresses to keep track of which DNS seeds were
+ * used.
+ *
+ * @returns Whether or not the operation was successful.
+ *
+ * @see CNetAddr::IsInternal(), CNetAddr::IsRFC4193()
+ */
bool CNetAddr::SetInternal(const std::string &name)
{
if (name.empty()) {
@@ -52,6 +71,16 @@ bool CNetAddr::SetInternal(const std::string &name)
return true;
}
+/**
+ * Try to make this a dummy address that maps the specified onion address into
+ * IPv6 using OnionCat's range and encoding. Such dummy addresses have a prefix
+ * of fd87:d87e:eb43::/48 and are guaranteed to not be publicly routable as they
+ * fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
+ *
+ * @returns Whether or not the operation was successful.
+ *
+ * @see CNetAddr::IsTor(), CNetAddr::IsRFC4193()
+ */
bool CNetAddr::SetSpecial(const std::string &strName)
{
if (strName.size()>6 && strName.substr(strName.size() - 6, 6) == ".onion") {
@@ -175,6 +204,12 @@ bool CNetAddr::IsRFC4843() const
return (GetByte(15) == 0x20 && GetByte(14) == 0x01 && GetByte(13) == 0x00 && (GetByte(12) & 0xF0) == 0x10);
}
+/**
+ * @returns Whether or not this is a dummy address that maps an onion address
+ * into IPv6.
+ *
+ * @see CNetAddr::SetSpecial(const std::string &)
+ */
bool CNetAddr::IsTor() const
{
return (memcmp(ip, pchOnionCat, sizeof(pchOnionCat)) == 0);
@@ -194,6 +229,16 @@ bool CNetAddr::IsLocal() const
return false;
}
+/**
+ * @returns Whether or not this network address is a valid address that @a could
+ * be used to refer to an actual host.
+ *
+ * @note A valid address may or may not be publicly routable on the global
+ * internet. As in, the set of valid addreses is a superset of the set of
+ * publicly routable addresses.
+ *
+ * @see CNetAddr::IsRoutable()
+ */
bool CNetAddr::IsValid() const
{
// Cleanup 3-byte shifted addresses caused by garbage in size field
@@ -233,11 +278,25 @@ bool CNetAddr::IsValid() const
return true;
}
+/**
+ * @returns Whether or not this network address is publicly routable on the
+ * global internet.
+ *
+ * @note A routable address is always valid. As in, the set of routable addreses
+ * is a subset of the set of valid addresses.
+ *
+ * @see CNetAddr::IsValid()
+ */
bool CNetAddr::IsRoutable() const
{
return IsValid() && !(IsRFC1918() || IsRFC2544() || IsRFC3927() || IsRFC4862() || IsRFC6598() || IsRFC5737() || (IsRFC4193() && !IsTor()) || IsRFC4843() || IsLocal() || IsInternal());
}
+/**
+ * @returns Whether or not this is a dummy address that maps a name into IPv6.
+ *
+ * @see CNetAddr::SetInternal(const std::string &)
+ */
bool CNetAddr::IsInternal() const
{
return memcmp(ip, g_internal_prefix, sizeof(g_internal_prefix)) == 0;
@@ -299,6 +358,16 @@ bool operator<(const CNetAddr& a, const CNetAddr& b)
return (memcmp(a.ip, b.ip, 16) < 0);
}
+/**
+ * Try to get our IPv4 address.
+ *
+ * @param[out] pipv4Addr The in_addr struct to which to copy.
+ *
+ * @returns Whether or not the operation was successful, in particular, whether
+ * or not our address was an IPv4 address.
+ *
+ * @see CNetAddr::IsIPv4()
+ */
bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
{
if (!IsIPv4())
@@ -307,6 +376,16 @@ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
return true;
}
+/**
+ * Try to get our IPv6 address.
+ *
+ * @param[out] pipv6Addr The in6_addr struct to which to copy.
+ *
+ * @returns Whether or not the operation was successful, in particular, whether
+ * or not our address was an IPv6 address.
+ *
+ * @see CNetAddr::IsIPv6()
+ */
bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
{
if (!IsIPv6()) {
@@ -316,8 +395,16 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
return true;
}
-// get canonical identifier of an address' group
-// no two connections will be attempted to addresses with the same group
+/**
+ * Get the canonical identifier of our network group
+ *
+ * The groups are assigned in a way where it should be costly for an attacker to
+ * obtain addresses with many different group identifiers, even if it is cheap
+ * to obtain addresses with the same identifier.
+ *
+ * @note No two connections will be attempted to addresses with the same network
+ * group.
+ */
std::vector<unsigned char> CNetAddr::GetGroup() const
{
std::vector<unsigned char> vchRet;
@@ -379,12 +466,15 @@ std::vector<unsigned char> CNetAddr::GetGroup() const
nBits = 32;
vchRet.push_back(nClass);
+
+ // push our ip onto vchRet byte by byte...
while (nBits >= 8)
{
vchRet.push_back(GetByte(15 - nStartByte));
nStartByte++;
nBits -= 8;
}
+ // ...for the last byte, push nBits and for the rest of the byte push 1's
if (nBits > 0)
vchRet.push_back(GetByte(15 - nStartByte) | ((1 << (8 - nBits)) - 1));
@@ -526,6 +616,18 @@ bool operator<(const CService& a, const CService& b)
return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) || (static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port < b.port);
}
+/**
+ * Obtain the IPv4/6 socket address this represents.
+ *
+ * @param[out] paddr The obtained socket address.
+ * @param[in,out] addrlen The size, in bytes, of the address structure pointed
+ * to by paddr. The value that's pointed to by this
+ * parameter might change after calling this function if
+ * the size of the corresponding address structure
+ * changed.
+ *
+ * @returns Whether or not the operation was successful.
+ */
bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
{
if (IsIPv4()) {
@@ -556,13 +658,16 @@ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
return false;
}
+/**
+ * @returns An identifier unique to this service's address and port number.
+ */
std::vector<unsigned char> CService::GetKey() const
{
std::vector<unsigned char> vKey;
vKey.resize(18);
memcpy(vKey.data(), ip, 16);
- vKey[16] = port / 0x100;
- vKey[17] = port & 0x0FF;
+ vKey[16] = port / 0x100; // most significant byte of our port
+ vKey[17] = port & 0x0FF; // least significant byte of our port
return vKey;
}
@@ -641,6 +746,10 @@ CSubNet::CSubNet(const CNetAddr &addr):
network = addr;
}
+/**
+ * @returns True if this subnet is valid, the specified address is valid, and
+ * the specified address belongs in this subnet.
+ */
bool CSubNet::Match(const CNetAddr &addr) const
{
if (!valid || !addr.IsValid())
@@ -651,6 +760,10 @@ bool CSubNet::Match(const CNetAddr &addr) const
return true;
}
+/**
+ * @returns The number of 1-bits in the prefix of the specified subnet mask. If
+ * the specified subnet mask is not a valid one, -1.
+ */
static inline int NetmaskBits(uint8_t x)
{
switch(x) {
diff --git a/src/netaddress.h b/src/netaddress.h
index ca435d17dc..8230e40606 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -48,10 +48,6 @@ class CNetAddr
void SetRaw(Network network, const uint8_t *data);
public:
- /**
- * Transform an arbitrary string into a non-routable ipv6 address.
- * Useful for mapping resolved addresses back to their source.
- */
bool SetInternal(const std::string& name);
bool SetSpecial(const std::string &strName); // for Tor addresses
@@ -69,8 +65,8 @@ class CNetAddr
bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32)
bool IsRFC4843() const; // IPv6 ORCHID (2001:10::/28)
bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
- bool IsRFC6052() const; // IPv6 well-known prefix (64:FF9B::/96)
- bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96)
+ bool IsRFC6052() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96)
+ bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
bool IsTor() const;
bool IsLocal() const;
bool IsRoutable() const;
diff --git a/src/node/README.md b/src/node/README.md
new file mode 100644
index 0000000000..e99a717534
--- /dev/null
+++ b/src/node/README.md
@@ -0,0 +1,22 @@
+# src/node/
+
+The [`src/node/`](./) directory contains code that needs to access node state
+(state in `CChain`, `CBlockIndex`, `CCoinsView`, `CTxMemPool`, and similar
+classes).
+
+Code in [`src/node/`](./) is meant to be segregated from code in
+[`src/wallet/`](../wallet/) and [`src/qt/`](../qt/), to ensure wallet and GUI
+code changes don't interfere with node operation, to allow wallet and GUI code
+to run in separate processes, and to perhaps eventually allow wallet and GUI
+code to be maintained in separate source repositories.
+
+As a rule of thumb, code in one of the [`src/node/`](./),
+[`src/wallet/`](../wallet/), or [`src/qt/`](../qt/) directories should avoid
+calling code in the other directories directly, and only invoke it indirectly
+through the more limited [`src/interfaces/`](../interfaces/) classes.
+
+The [`src/node/`](./) directory is a new directory introduced in
+[#14978](https://github.com/bitcoin/bitcoin/pull/14978) and at the moment is
+sparsely populated. Eventually more substantial files like
+[`src/validation.cpp`](../validation.cpp) and
+[`src/txmempool.cpp`](../txmempool.cpp) might be moved there.
diff --git a/src/node/coin.cpp b/src/node/coin.cpp
new file mode 100644
index 0000000000..bb98e63f3a
--- /dev/null
+++ b/src/node/coin.cpp
@@ -0,0 +1,22 @@
+// 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 <node/coin.h>
+
+#include <txmempool.h>
+#include <validation.h>
+
+void FindCoins(std::map<COutPoint, Coin>& coins)
+{
+ LOCK2(cs_main, ::mempool.cs);
+ assert(pcoinsTip);
+ CCoinsViewCache& chain_view = *::pcoinsTip;
+ CCoinsViewMemPool mempool_view(&chain_view, ::mempool);
+ for (auto& coin : coins) {
+ if (!mempool_view.GetCoin(coin.first, coin.second)) {
+ // Either the coin is not in the CCoinsViewCache or is spent. Clear it.
+ coin.second.Clear();
+ }
+ }
+}
diff --git a/src/node/coin.h b/src/node/coin.h
new file mode 100644
index 0000000000..eb95b75cfb
--- /dev/null
+++ b/src/node/coin.h
@@ -0,0 +1,22 @@
+// 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_NODE_COIN_H
+#define BITCOIN_NODE_COIN_H
+
+#include <map>
+
+class COutPoint;
+class Coin;
+
+/**
+ * Look up unspent output information. Returns coins in the mempool and in the
+ * current chain UTXO set. Iterates through all the keys in the map and
+ * populates the values.
+ *
+ * @param[in,out] coins map to fill
+ */
+void FindCoins(std::map<COutPoint, Coin>& coins);
+
+#endif // BITCOIN_NODE_COIN_H
diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp
new file mode 100644
index 0000000000..12559c5a5f
--- /dev/null
+++ b/src/node/psbt.cpp
@@ -0,0 +1,134 @@
+// 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.
+
+#include <coins.h>
+#include <consensus/tx_verify.h>
+#include <node/psbt.h>
+#include <policy/policy.h>
+#include <policy/settings.h>
+
+#include <numeric>
+
+PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
+{
+ // Go through each input and build status
+ PSBTAnalysis result;
+
+ bool calc_fee = true;
+ bool all_final = true;
+ bool only_missing_sigs = true;
+ bool only_missing_final = false;
+ CAmount in_amt = 0;
+
+ result.inputs.resize(psbtx.tx->vin.size());
+
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs[i];
+ PSBTInputAnalysis& input_analysis = result.inputs[i];
+
+ // Check for a UTXO
+ CTxOut utxo;
+ if (psbtx.GetInputUTXO(utxo, i)) {
+ in_amt += utxo.nValue;
+ input_analysis.has_utxo = true;
+ } else {
+ input_analysis.has_utxo = false;
+ input_analysis.is_final = false;
+ input_analysis.next = PSBTRole::UPDATER;
+ calc_fee = false;
+ }
+
+ // Check if it is final
+ if (!utxo.IsNull() && !PSBTInputSigned(input)) {
+ input_analysis.is_final = false;
+ all_final = false;
+
+ // Figure out what is missing
+ SignatureData outdata;
+ bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
+
+ // Things are missing
+ if (!complete) {
+ input_analysis.missing_pubkeys = outdata.missing_pubkeys;
+ input_analysis.missing_redeem_script = outdata.missing_redeem_script;
+ input_analysis.missing_witness_script = outdata.missing_witness_script;
+ input_analysis.missing_sigs = outdata.missing_sigs;
+
+ // If we are only missing signatures and nothing else, then next is signer
+ if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
+ input_analysis.next = PSBTRole::SIGNER;
+ } else {
+ only_missing_sigs = false;
+ input_analysis.next = PSBTRole::UPDATER;
+ }
+ } else {
+ only_missing_final = true;
+ input_analysis.next = PSBTRole::FINALIZER;
+ }
+ } else if (!utxo.IsNull()){
+ input_analysis.is_final = true;
+ }
+ }
+
+ if (all_final) {
+ only_missing_sigs = false;
+ result.next = PSBTRole::EXTRACTOR;
+ }
+ if (calc_fee) {
+ // Get the output amount
+ CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0),
+ [](CAmount a, const CTxOut& b) {
+ return a += b.nValue;
+ }
+ );
+
+ // Get the fee
+ CAmount fee = in_amt - out_amt;
+ result.fee = fee;
+
+ // Estimate the size
+ CMutableTransaction mtx(*psbtx.tx);
+ CCoinsView view_dummy;
+ CCoinsViewCache view(&view_dummy);
+ bool success = true;
+
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ PSBTInput& input = psbtx.inputs[i];
+ Coin newcoin;
+
+ if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) {
+ success = false;
+ break;
+ } else {
+ mtx.vin[i].scriptSig = input.final_script_sig;
+ mtx.vin[i].scriptWitness = input.final_script_witness;
+ newcoin.nHeight = 1;
+ view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
+ }
+ }
+
+ if (success) {
+ CTransaction ctx = CTransaction(mtx);
+ size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
+ result.estimated_vsize = size;
+ // Estimate fee rate
+ CFeeRate feerate(fee, size);
+ result.estimated_feerate = feerate;
+ }
+
+ if (only_missing_sigs) {
+ result.next = PSBTRole::SIGNER;
+ } else if (only_missing_final) {
+ result.next = PSBTRole::FINALIZER;
+ } else if (all_final) {
+ result.next = PSBTRole::EXTRACTOR;
+ } else {
+ result.next = PSBTRole::UPDATER;
+ }
+ } else {
+ result.next = PSBTRole::UPDATER;
+ }
+
+ return result;
+}
diff --git a/src/node/psbt.h b/src/node/psbt.h
new file mode 100644
index 0000000000..e04366a20f
--- /dev/null
+++ b/src/node/psbt.h
@@ -0,0 +1,43 @@
+// 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_PSBT_H
+#define BITCOIN_NODE_PSBT_H
+
+#include <psbt.h>
+
+/**
+ * Holds an analysis of one input from a PSBT
+ */
+struct PSBTInputAnalysis {
+ bool has_utxo; //!< Whether we have UTXO information for this input
+ bool is_final; //!< Whether the input has all required information including signatures
+ PSBTRole next; //!< Which of the BIP 174 roles needs to handle this input next
+
+ std::vector<CKeyID> missing_pubkeys; //!< Pubkeys whose BIP32 derivation path is missing
+ std::vector<CKeyID> missing_sigs; //!< Pubkeys whose signatures are missing
+ uint160 missing_redeem_script; //!< Hash160 of redeem script, if missing
+ uint256 missing_witness_script; //!< SHA256 of witness script, if missing
+};
+
+/**
+ * Holds the results of AnalyzePSBT (miscellaneous information about a PSBT)
+ */
+struct PSBTAnalysis {
+ Optional<size_t> estimated_vsize; //!< Estimated weight of the transaction
+ Optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction
+ Optional<CAmount> fee; //!< Amount of fee being paid by the transaction
+ std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction
+ PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next
+};
+
+/**
+ * Provides helpful miscellaneous information about where a PSBT is in the signing workflow.
+ *
+ * @param[in] psbtx the PSBT to analyze
+ * @return A PSBTAnalysis with information about the provided PSBT.
+ */
+PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx);
+
+#endif // BITCOIN_NODE_PSBT_H
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 7b9b4310e7..5ffb15ed3c 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -6,38 +6,13 @@
#include <consensus/validation.h>
#include <net.h>
#include <txmempool.h>
+#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
#include <node/transaction.h>
#include <future>
-std::string TransactionErrorString(const TransactionError err)
-{
- switch (err) {
- case TransactionError::OK:
- return "No error";
- case TransactionError::MISSING_INPUTS:
- return "Missing inputs";
- case TransactionError::ALREADY_IN_CHAIN:
- return "Transaction already in block chain";
- case TransactionError::P2P_DISABLED:
- return "Peer-to-peer functionality missing or disabled";
- case TransactionError::MEMPOOL_REJECTED:
- return "Transaction rejected by AcceptToMemoryPool";
- case TransactionError::MEMPOOL_ERROR:
- return "AcceptToMemoryPool failed";
- case TransactionError::INVALID_PSBT:
- return "PSBT is not sane";
- case TransactionError::PSBT_MISMATCH:
- return "PSBTs not compatible (different transactions)";
- case TransactionError::SIGHASH_MISMATCH:
- return "Specified sighash value does not match existing value";
- // no default case, so the compiler can warn about missing cases
- }
- assert(false);
-}
-
TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, std::string& err_string, const CAmount& highfee)
{
std::promise<void> promise;
diff --git a/src/node/transaction.h b/src/node/transaction.h
index 3457ececa4..51033f94e5 100644
--- a/src/node/transaction.h
+++ b/src/node/transaction.h
@@ -8,20 +8,7 @@
#include <attributes.h>
#include <primitives/transaction.h>
#include <uint256.h>
-
-enum class TransactionError {
- OK, //!< No error
- MISSING_INPUTS,
- ALREADY_IN_CHAIN,
- P2P_DISABLED,
- MEMPOOL_REJECTED,
- MEMPOOL_ERROR,
- INVALID_PSBT,
- PSBT_MISMATCH,
- SIGHASH_MISMATCH,
-};
-
-std::string TransactionErrorString(const TransactionError error);
+#include <util/error.h>
/**
* Broadcast a transaction
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index c49b9fa36b..524afd014e 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -27,40 +27,6 @@ std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) {
return horizon_string->second;
}
-std::string StringForFeeReason(FeeReason reason) {
- static const std::map<FeeReason, std::string> fee_reason_strings = {
- {FeeReason::NONE, "None"},
- {FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"},
- {FeeReason::FULL_ESTIMATE, "Target 85% Threshold"},
- {FeeReason::DOUBLE_ESTIMATE, "Double Target 95% Threshold"},
- {FeeReason::CONSERVATIVE, "Conservative Double Target longer horizon"},
- {FeeReason::MEMPOOL_MIN, "Mempool Min Fee"},
- {FeeReason::PAYTXFEE, "PayTxFee set"},
- {FeeReason::FALLBACK, "Fallback fee"},
- {FeeReason::REQUIRED, "Minimum Required Fee"},
- {FeeReason::MAXTXFEE, "MaxTxFee limit"}
- };
- auto reason_string = fee_reason_strings.find(reason);
-
- if (reason_string == fee_reason_strings.end()) return "Unknown";
-
- return reason_string->second;
-}
-
-bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
- static const std::map<std::string, FeeEstimateMode> fee_modes = {
- {"UNSET", FeeEstimateMode::UNSET},
- {"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
- {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
- };
- auto mode = fee_modes.find(mode_string);
-
- if (mode == fee_modes.end()) return false;
-
- fee_estimate_mode = mode->second;
- return true;
-}
-
/**
* We will instantiate an instance of this class to track transactions that were
* included in a block. We will lump transactions into a bucket according to their
diff --git a/src/policy/fees.h b/src/policy/fees.h
index c8472a12f5..6e61f76178 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -46,8 +46,6 @@ enum class FeeReason {
MAXTXFEE,
};
-std::string StringForFeeReason(FeeReason reason);
-
/* Used to determine type of fee estimation requested */
enum class FeeEstimateMode {
UNSET, //!< Use default settings based on other criteria
@@ -55,8 +53,6 @@ enum class FeeEstimateMode {
CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
};
-bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
-
/* Used to return detailed information about a feerate bucket */
struct EstimatorBucket
{
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index d4cc538492..63a3d06267 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -8,8 +8,8 @@
#include <policy/policy.h>
#include <consensus/validation.h>
-#include <validation.h>
#include <coins.h>
+#include <policy/settings.h>
#include <tinyformat.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -59,7 +59,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
std::vector<std::vector<unsigned char> > vSolutions;
whichType = Solver(scriptPubKey, vSolutions);
- if (whichType == TX_NONSTANDARD || whichType == TX_WITNESS_UNKNOWN) {
+ if (whichType == TX_NONSTANDARD) {
return false;
} else if (whichType == TX_MULTISIG) {
unsigned char m = vSolutions.front()[0];
@@ -77,7 +77,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
return true;
}
-bool IsStandardTx(const CTransaction& tx, std::string& reason)
+bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason)
{
if (tx.nVersion > CTransaction::MAX_STANDARD_VERSION || tx.nVersion < 1) {
reason = "version";
@@ -123,10 +123,10 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason)
if (whichType == TX_NULL_DATA)
nDataOut++;
- else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
+ else if ((whichType == TX_MULTISIG) && (!permit_bare_multisig)) {
reason = "bare-multisig";
return false;
- } else if (IsDust(txout, ::dustRelayFee)) {
+ } else if (IsDust(txout, dust_relay_fee)) {
reason = "dust";
return false;
}
@@ -239,21 +239,17 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
return true;
}
-CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
-CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
-unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
-
-int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost)
+int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop)
{
- return (std::max(nWeight, nSigOpCost * nBytesPerSigOp) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
+ return (std::max(nWeight, nSigOpCost * bytes_per_sigop) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
}
-int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost)
+int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost, unsigned int bytes_per_sigop)
{
- return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost);
+ return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost, bytes_per_sigop);
}
-int64_t GetVirtualTransactionInputSize(const CTxIn& txin, int64_t nSigOpCost)
+int64_t GetVirtualTransactionInputSize(const CTxIn& txin, int64_t nSigOpCost, unsigned int bytes_per_sigop)
{
- return GetVirtualTransactionSize(GetTransactionInputWeight(txin), nSigOpCost);
+ return GetVirtualTransactionSize(GetTransactionInputWeight(txin), nSigOpCost, bytes_per_sigop);
}
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 3d47ac1267..ebe040f0ea 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -34,6 +34,8 @@ static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
static const unsigned int DEFAULT_INCREMENTAL_RELAY_FEE = 1000;
/** Default for -bytespersigop */
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
+/** Default for -permitbaremultisig */
+static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
/** The maximum number of witness stack items in a standard P2WSH script */
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
/** The maximum size of each witness stack item in a standard P2WSH script */
@@ -84,7 +86,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
* Check for standard transaction types
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
-bool IsStandardTx(const CTransaction& tx, std::string& reason);
+bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason);
/**
* Check for standard transaction types
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
@@ -98,13 +100,19 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
*/
bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
-extern CFeeRate incrementalRelayFee;
-extern CFeeRate dustRelayFee;
-extern unsigned int nBytesPerSigOp;
-
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
-int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
-int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0);
-int64_t GetVirtualTransactionInputSize(const CTxIn& tx, int64_t nSigOpCost = 0);
+int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop);
+int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost, unsigned int bytes_per_sigop);
+int64_t GetVirtualTransactionInputSize(const CTxIn& tx, int64_t nSigOpCost, unsigned int bytes_per_sigop);
+
+static inline int64_t GetVirtualTransactionSize(const CTransaction& tx)
+{
+ return GetVirtualTransactionSize(tx, 0, 0);
+}
+
+static inline int64_t GetVirtualTransactionInputSize(const CTxIn& tx)
+{
+ return GetVirtualTransactionInputSize(tx, 0, 0);
+}
#endif // BITCOIN_POLICY_POLICY_H
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index c73a97fd7d..b4b8341d77 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -3,16 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <policy/rbf.h>
-
-bool SignalsOptInRBF(const CTransaction &tx)
-{
- for (const CTxIn &txin : tx.vin) {
- if (txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) {
- return true;
- }
- }
- return false;
-}
+#include <util/rbf.h>
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
{
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index a4f8777310..0707b0044f 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -7,18 +7,12 @@
#include <txmempool.h>
-static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
-
enum class RBFTransactionState {
UNKNOWN,
REPLACEABLE_BIP125,
FINAL
};
-// Check whether the sequence numbers on this transaction are signaling
-// opt-in to replace-by-fee, according to BIP 125
-bool SignalsOptInRBF(const CTransaction &tx);
-
// Determine whether an in-mempool transaction is signaling opt-in to RBF
// according to BIP 125
// This involves checking sequence numbers of the transaction, as well
diff --git a/src/policy/settings.cpp b/src/policy/settings.cpp
new file mode 100644
index 0000000000..e8e1559407
--- /dev/null
+++ b/src/policy/settings.cpp
@@ -0,0 +1,14 @@
+// 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.
+
+#include <policy/settings.h>
+
+#include <policy/feerate.h>
+#include <policy/policy.h>
+
+bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
+CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
+CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
+unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
diff --git a/src/policy/settings.h b/src/policy/settings.h
new file mode 100644
index 0000000000..30a7189c93
--- /dev/null
+++ b/src/policy/settings.h
@@ -0,0 +1,35 @@
+// 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_POLICY_SETTINGS_H
+#define BITCOIN_POLICY_SETTINGS_H
+
+#include <policy/policy.h>
+
+class CFeeRate;
+class CTransaction;
+
+// Policy settings which are configurable at runtime.
+extern CFeeRate incrementalRelayFee;
+extern CFeeRate dustRelayFee;
+extern unsigned int nBytesPerSigOp;
+extern bool fIsBareMultisigStd;
+
+static inline bool IsStandardTx(const CTransaction& tx, std::string& reason)
+{
+ return IsStandardTx(tx, ::fIsBareMultisigStd, ::dustRelayFee, reason);
+}
+
+static inline int64_t GetVirtualTransactionSize(int64_t weight, int64_t sigop_cost)
+{
+ return GetVirtualTransactionSize(weight, sigop_cost, ::nBytesPerSigOp);
+}
+
+static inline int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t sigop_cost)
+{
+ return GetVirtualTransactionSize(tx, sigop_cost, ::nBytesPerSigOp);
+}
+
+#endif // BITCOIN_POLICY_SETTINGS_H
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index f6f8e31363..aad991e2f1 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -222,6 +222,10 @@ inline void UnserializeTransaction(TxType& tx, Stream& s) {
for (size_t i = 0; i < tx.vin.size(); i++) {
s >> tx.vin[i].scriptWitness.stack;
}
+ if (!tx.HasWitness()) {
+ /* It's illegal to encode witnesses when all witness stacks are empty. */
+ throw std::ios_base::failure("Superfluous witness record");
+ }
}
if (flags) {
/* Unknown flag in the serialization */
diff --git a/src/psbt.cpp b/src/psbt.cpp
index 0fb7d49d7d..f31f2af0d1 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -2,9 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <coins.h>
+#include <consensus/tx_verify.h>
+#include <policy/policy.h>
#include <psbt.h>
#include <util/strencodings.h>
+#include <numeric>
+
PartiallySignedTransaction::PartiallySignedTransaction(const CMutableTransaction& tx) : tx(tx)
{
inputs.resize(tx.vin.size());
@@ -205,7 +210,7 @@ void PSBTOutput::Merge(const PSBTOutput& output)
if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
}
-bool PSBTInputSigned(PSBTInput& input)
+bool PSBTInputSigned(const PSBTInput& input)
{
return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
}
@@ -325,3 +330,39 @@ TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector
return TransactionError::OK;
}
+
+std::string PSBTRoleName(PSBTRole role) {
+ switch (role) {
+ case PSBTRole::UPDATER: return "updater";
+ case PSBTRole::SIGNER: return "signer";
+ case PSBTRole::FINALIZER: return "finalizer";
+ case PSBTRole::EXTRACTOR: return "extractor";
+ }
+}
+
+bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
+{
+ bool invalid;
+ std::string tx_data = DecodeBase64(base64_tx, &invalid);
+ if (invalid) {
+ error = "invalid base64";
+ return false;
+ }
+ return DecodeRawPSBT(psbt, tx_data, error);
+}
+
+bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data, std::string& error)
+{
+ CDataStream ss_data(tx_data.data(), tx_data.data() + tx_data.size(), SER_NETWORK, PROTOCOL_VERSION);
+ try {
+ ss_data >> psbt;
+ if (!ss_data.empty()) {
+ error = "extra data after PSBT";
+ return false;
+ }
+ } catch (const std::exception& e) {
+ error = e.what();
+ return false;
+ }
+ return true;
+}
diff --git a/src/psbt.h b/src/psbt.h
index c889dad361..1bc1e91a84 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -7,6 +7,8 @@
#include <attributes.h>
#include <node/transaction.h>
+#include <optional.h>
+#include <policy/feerate.h>
#include <primitives/transaction.h>
#include <pubkey.h>
#include <script/sign.h>
@@ -548,8 +550,17 @@ struct PartiallySignedTransaction
}
};
+enum class PSBTRole {
+ UPDATER,
+ SIGNER,
+ FINALIZER,
+ EXTRACTOR
+};
+
+std::string PSBTRoleName(PSBTRole role);
+
/** Checks whether a PSBTInput is already signed. */
-bool PSBTInputSigned(PSBTInput& input);
+bool PSBTInputSigned(const PSBTInput& input);
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool use_dummy = false);
@@ -580,4 +591,9 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti
*/
NODISCARD TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs);
+//! Decode a base64ed PSBT into a PartiallySignedTransaction
+NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
+//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
+NODISCARD bool DecodeRawPSBT(PartiallySignedTransaction& decoded_psbt, const std::string& raw_psbt, std::string& error);
+
#endif // BITCOIN_PSBT_H
diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index 726dafccb0..50d6afabcd 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -107,7 +107,7 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
ui->newAddress->setVisible(true);
break;
case ReceivingTab:
- ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. It is recommended to use a new receiving address for each transaction."));
+ ui->labelExplanation->setText(tr("These are your Bitcoin addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses."));
ui->deleteAddress->setVisible(false);
ui->newAddress->setVisible(false);
break;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 1b063771ef..81255aaae9 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -30,6 +30,7 @@
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <noui.h>
+#include <util/threadnames.h>
#include <rpc/server.h>
#include <ui_interface.h>
#include <uint256.h>
@@ -149,6 +150,7 @@ void BitcoinCore::initialize()
try
{
qDebug() << __func__ << ": Running initialization in thread";
+ util::ThreadRename("qt-init");
bool rv = m_node.appInitMain();
Q_EMIT initializeResult(rv);
} catch (const std::exception& e) {
@@ -423,6 +425,7 @@ int GuiMain(int argc, char* argv[])
std::tie(argc, argv) = winArgs.get();
#endif
SetupEnvironment();
+ util::ThreadRename("main");
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 27b4c182f9..88a35534c2 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -11,7 +11,6 @@
#include <chain.h>
#include <chainparams.h>
-#include <checkpoints.h>
#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 2f916d0b44..0d280f2993 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -108,7 +108,7 @@
</size>
</property>
<property name="text">
- <string>&amp;Request payment</string>
+ <string>&amp;Create new receiving address</string>
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
@@ -189,7 +189,7 @@
</widget>
</item>
<item>
- <widget class="QCheckBox" name="useBech32">
+ <widget class="QCheckBox" name="useLegacyAddress">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
@@ -206,10 +206,10 @@
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
- <string>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</string>
+ <string>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When checked, an address compatible with older wallets will be created instead.</string>
</property>
<property name="text">
- <string>Generate native segwit (Bech32) address</string>
+ <string>Generate legacy address</string>
</property>
</widget>
</item>
@@ -360,7 +360,7 @@
<tabstops>
<tabstop>reqLabel</tabstop>
<tabstop>reqAmount</tabstop>
- <tabstop>useBech32</tabstop>
+ <tabstop>useLegacyAddress</tabstop>
<tabstop>reqMessage</tabstop>
<tabstop>receiveButton</tabstop>
<tabstop>clearButton</tabstop>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index ba0a5abdf3..45f21d50fc 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -175,7 +175,9 @@ bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
QString formatBitcoinURI(const SendCoinsRecipient &info)
{
- QString ret = QString("bitcoin:%1").arg(info.address);
+ bool bech_32 = info.address.startsWith(QString::fromStdString(Params().Bech32HRP() + "1"));
+
+ QString ret = QString("bitcoin:%1").arg(bech_32 ? info.address.toUpper() : info.address);
int paramCount = 0;
if (info.amount)
@@ -244,6 +246,11 @@ QList<QModelIndex> getEntryData(QAbstractItemView *view, int column)
return view->selectionModel()->selectedRows(column);
}
+QString getDefaultDataDirectory()
+{
+ return boostPathToQString(GetDefaultDataDir());
+}
+
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
const QString &filter,
QString *selectedSuffixOut)
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index cbec73a882..bea4a83494 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -79,6 +79,11 @@ namespace GUIUtil
void setClipboard(const QString& str);
+ /**
+ * Determine default data directory for operating system.
+ */
+ QString getDefaultDataDirectory();
+
/** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix
when no suffix is provided by the user.
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 499af9fa07..c595361934 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -168,7 +168,7 @@ QString Intro::getDataDirectory()
void Intro::setDataDirectory(const QString &dataDir)
{
ui->dataDirectory->setText(dataDir);
- if(dataDir == getDefaultDataDirectory())
+ if(dataDir == GUIUtil::getDefaultDataDirectory())
{
ui->dataDirDefault->setChecked(true);
ui->dataDirectory->setEnabled(false);
@@ -180,11 +180,6 @@ void Intro::setDataDirectory(const QString &dataDir)
}
}
-QString Intro::getDefaultDataDirectory()
-{
- return GUIUtil::boostPathToQString(GetDefaultDataDir());
-}
-
bool Intro::pickDataDirectory(interfaces::Node& node)
{
QSettings settings;
@@ -193,7 +188,7 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
if(!gArgs.GetArg("-datadir", "").empty())
return true;
/* 1) Default data directory for operating system */
- QString dataDir = getDefaultDataDirectory();
+ QString dataDir = GUIUtil::getDefaultDataDirectory();
/* 2) Allow QSettings to override default dir */
dataDir = settings.value("strDataDir", dataDir).toString();
@@ -239,7 +234,7 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
* override -datadir in the bitcoin.conf file in the default data directory
* (to be consistent with bitcoind behavior)
*/
- if(dataDir != getDefaultDataDirectory()) {
+ if(dataDir != GUIUtil::getDefaultDataDirectory()) {
node.softSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
}
return true;
@@ -293,7 +288,7 @@ void Intro::on_ellipsisButton_clicked()
void Intro::on_dataDirDefault_clicked()
{
- setDataDirectory(getDefaultDataDirectory());
+ setDataDirectory(GUIUtil::getDefaultDataDirectory());
}
void Intro::on_dataDirCustom_clicked()
diff --git a/src/qt/intro.h b/src/qt/intro.h
index b537c94f7d..c3b26808d4 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -48,11 +48,6 @@ public:
*/
static bool pickDataDirectory(interfaces::Node& node);
- /**
- * Determine default data directory for operating system.
- */
- static QString getDefaultDataDirectory();
-
Q_SIGNALS:
void requestCheck();
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 9094aeff56..40dc7bf400 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -154,6 +154,10 @@ void OptionsDialog::setModel(OptionsModel *_model)
if (_model->isRestartRequired())
showRestartWarning(true);
+ // Prune values are in GB to be consistent with intro.cpp
+ static constexpr uint64_t nMinDiskSpace = (MIN_DISK_SPACE_FOR_BLOCK_FILES / GB_BYTES) + (MIN_DISK_SPACE_FOR_BLOCK_FILES % GB_BYTES) ? 1 : 0;
+ ui->pruneSize->setRange(nMinDiskSpace, std::numeric_limits<int>::max());
+
QString strLabel = _model->getOverriddenByCommandLine();
if (strLabel.isEmpty())
strLabel = tr("none");
@@ -164,10 +168,6 @@ void OptionsDialog::setModel(OptionsModel *_model)
mapper->toFirst();
updateDefaultProxyNets();
-
- // Prune values are in GB to be consistent with intro.cpp
- static constexpr uint64_t nMinDiskSpace = (MIN_DISK_SPACE_FOR_BLOCK_FILES / GB_BYTES) + (MIN_DISK_SPACE_FOR_BLOCK_FILES % GB_BYTES) ? 1 : 0;
- ui->pruneSize->setRange(nMinDiskSpace, _model->node().getAssumedBlockchainSize());
}
/* warn when one of the following settings changes by user action (placed here so init via mapper doesn't trigger them) */
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 62496a57f4..5b4fb4cc18 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -17,7 +17,6 @@
#include <net.h>
#include <netbase.h>
#include <txdb.h> // for -dbcache defaults
-#include <qt/intro.h>
#include <QNetworkProxy>
#include <QSettings>
@@ -110,7 +109,7 @@ void OptionsModel::Init(bool resetSettings)
addOverriddenOption("-par");
if (!settings.contains("strDataDir"))
- settings.setValue("strDataDir", Intro::getDefaultDataDirectory());
+ settings.setValue("strDataDir", GUIUtil::getDefaultDataDirectory());
// Wallet
#ifdef ENABLE_WALLET
@@ -187,7 +186,7 @@ void OptionsModel::Reset()
BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings);
// Save the strDataDir setting
- QString dataDir = Intro::getDefaultDataDirectory();
+ QString dataDir = GUIUtil::getDefaultDataDirectory();
dataDir = settings.value("strDataDir", dataDir).toString();
// Remove all entries from our QSettings object
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 22a79a12bb..fc58090dcd 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -95,9 +95,9 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this);
if (model->wallet().getDefaultAddressType() == OutputType::BECH32) {
- ui->useBech32->setCheckState(Qt::Checked);
+ ui->useLegacyAddress->setCheckState(Qt::Unchecked);
} else {
- ui->useBech32->setCheckState(Qt::Unchecked);
+ ui->useLegacyAddress->setCheckState(Qt::Checked);
}
// Set the button to be enabled or disabled based on whether the wallet can give out new addresses.
@@ -150,7 +150,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked()
QString label = ui->reqLabel->text();
/* Generate new receiving address */
OutputType address_type;
- if (ui->useBech32->isChecked()) {
+ if (!ui->useLegacyAddress->isChecked()) {
address_type = OutputType::BECH32;
} else {
address_type = model->wallet().getDefaultAddressType();
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index fc1e14b031..8b6dcf0445 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -21,8 +21,6 @@
#include <util/strencodings.h>
#include <util/system.h>
-#include <openssl/crypto.h>
-
#include <univalue.h>
#ifdef ENABLE_WALLET
@@ -69,7 +67,6 @@ const QStringList historyFilter = QStringList()
<< "importmulti"
<< "sethdseed"
<< "signmessagewithprivkey"
- << "signrawtransaction"
<< "signrawtransactionwithkey"
<< "walletpassphrase"
<< "walletpassphrasechange"
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 6e00ab755c..8a0b265834 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -578,7 +578,7 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
case WalletModel::AbsurdFee:
- msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->node().getMaxTxFee()));
+ msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));
break;
case WalletModel::PaymentRequestExpired:
msgParams.first = tr("Payment request expired.");
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index d37a78fa8c..64cc85d623 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -11,7 +11,7 @@
#include <qt/walletmodel.h>
#include <key_io.h>
-#include <validation.h> // For strMessageMagic
+#include <util/validation.h> // For strMessageMagic
#include <wallet/wallet.h>
#include <string>
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 7f5e92ea9f..2ba1c2604c 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/test_bitcoin.h>
+#include <test/setup_common.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
@@ -58,7 +58,7 @@ void TestAddAddressesToSendBook()
{
TestChain100Setup test;
auto chain = interfaces::MakeChain();
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateMock());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index f8a9c25303..b0bd89b290 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -12,7 +12,7 @@
#include <rpc/register.h>
#include <rpc/server.h>
#include <qt/rpcconsole.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <univalue.h>
#include <util/system.h>
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 2b50a2ba81..9e3518fd53 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -15,7 +15,7 @@
#include <qt/transactionview.h>
#include <qt/walletmodel.h>
#include <key_io.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <validation.h>
#include <wallet/wallet.h>
#include <qt/overviewpage.h>
@@ -135,7 +135,7 @@ void TestGUI()
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
auto chain = interfaces::MakeChain();
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateMock());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
{
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index fab86a7912..019bd65823 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -9,9 +9,11 @@
#include <algorithm>
+#include <QApplication>
#include <QMessageBox>
#include <QMutexLocker>
#include <QThread>
+#include <QWindow>
WalletController::WalletController(interfaces::Node& node, const PlatformStyle* platform_style, OptionsModel* options_model, QObject* parent)
: QObject(parent)
@@ -97,7 +99,17 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal
m_wallets.push_back(wallet_model);
connect(wallet_model, &WalletModel::unload, [this, wallet_model] {
- removeAndDeleteWallet(wallet_model);
+ // Defer removeAndDeleteWallet when no modal widget is active.
+ // TODO: remove this workaround by removing usage of QDiallog::exec.
+ if (QApplication::activeModalWidget()) {
+ connect(qApp, &QApplication::focusWindowChanged, wallet_model, [this, wallet_model]() {
+ if (!QApplication::activeModalWidget()) {
+ removeAndDeleteWallet(wallet_model);
+ }
+ }, Qt::QueuedConnection);
+ } else {
+ removeAndDeleteWallet(wallet_model);
+ }
});
// Re-emit coinsSent signal from wallet model.
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 466f2278eb..94413547d4 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -40,15 +40,11 @@ void WalletFrame::setClientModel(ClientModel *_clientModel)
this->clientModel = _clientModel;
}
-bool WalletFrame::addWallet(WalletModel *walletModel)
+void WalletFrame::addWallet(WalletModel *walletModel)
{
- if (!gui || !clientModel || !walletModel) {
- return false;
- }
+ if (!gui || !clientModel || !walletModel) return;
- if (mapWalletViews.count(walletModel) > 0) {
- return false;
- }
+ if (mapWalletViews.count(walletModel) > 0) return;
WalletView *walletView = new WalletView(platformStyle, this);
walletView->setBitcoinGUI(gui);
@@ -72,31 +68,25 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
});
connect(walletView, &WalletView::outOfSyncWarningClicked, this, &WalletFrame::outOfSyncWarningClicked);
-
- return true;
}
-bool WalletFrame::setCurrentWallet(WalletModel* wallet_model)
+void WalletFrame::setCurrentWallet(WalletModel* wallet_model)
{
- if (mapWalletViews.count(wallet_model) == 0)
- return false;
+ if (mapWalletViews.count(wallet_model) == 0) return;
WalletView *walletView = mapWalletViews.value(wallet_model);
walletStack->setCurrentWidget(walletView);
assert(walletView);
walletView->updateEncryptionStatus();
- return true;
}
-bool WalletFrame::removeWallet(WalletModel* wallet_model)
+void WalletFrame::removeWallet(WalletModel* wallet_model)
{
- if (mapWalletViews.count(wallet_model) == 0)
- return false;
+ if (mapWalletViews.count(wallet_model) == 0) return;
WalletView *walletView = mapWalletViews.take(wallet_model);
walletStack->removeWidget(walletView);
delete walletView;
- return true;
}
void WalletFrame::removeAllWallets()
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index 6a74fde9fd..156653f47d 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -36,9 +36,9 @@ public:
void setClientModel(ClientModel *clientModel);
- bool addWallet(WalletModel *walletModel);
- bool setCurrentWallet(WalletModel* wallet_model);
- bool removeWallet(WalletModel* wallet_model);
+ void addWallet(WalletModel *walletModel);
+ void setCurrentWallet(WalletModel* wallet_model);
+ void removeWallet(WalletModel* wallet_model);
void removeAllWallets();
bool handlePaymentRequest(const SendCoinsRecipient& recipient);
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index f4f3be8f43..fd392b7cf7 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -222,9 +222,9 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
}
// reject absurdly high fee. (This can never happen because the
- // wallet caps the fee at maxTxFee. This merely serves as a
+ // wallet caps the fee at m_default_max_tx_fee. This merely serves as a
// belt-and-suspenders check)
- if (nFeeRequired > m_node.getMaxTxFee())
+ if (nFeeRequired > m_wallet->getDefaultMaxTxFee())
return AbsurdFee;
}
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index eb3b0baf08..2694d67800 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -29,7 +29,7 @@ std::unique_ptr<interfaces::PendingWalletTx>& WalletModelTransaction::getWtx()
unsigned int WalletModelTransaction::getTransactionSize()
{
- return wtx ? wtx->getVirtualSize() : 0;
+ return wtx ? GetVirtualTransactionSize(wtx->get()) : 0;
}
CAmount WalletModelTransaction::getTransactionFee() const
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 1c20feba12..e3fab44719 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -7,13 +7,14 @@
#include <amount.h>
#include <base58.h>
+#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
-#include <checkpoints.h>
#include <coins.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <hash.h>
+#include <index/blockfilterindex.h>
#include <index/txindex.h>
#include <key_io.h>
#include <policy/feerate.h>
@@ -29,6 +30,7 @@
#include <txmempool.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -378,7 +380,9 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
static std::string EntryDescriptionString()
{
- return " \"size\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n"
+ return " \"vsize\" : n, (numeric) virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted.\n"
+ " \"size\" : n, (numeric) (DEPRECATED) same as vsize. Only returned if bitcoind is started with -deprecatedrpc=size\n"
+ " size will be completely removed in v0.20.\n"
" \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)\n"
" \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority (DEPRECATED)\n"
" \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n"
@@ -416,7 +420,8 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
info.pushKV("fees", fees);
- info.pushKV("size", (int)e.GetTxSize());
+ info.pushKV("vsize", (int)e.GetTxSize());
+ if (IsDeprecatedRPCEnabled("size")) info.pushKV("size", (int)e.GetTxSize());
info.pushKV("fee", ValueFromAmount(e.GetFee()));
info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee()));
info.pushKV("time", e.GetTime());
@@ -1007,7 +1012,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
// too low to be a block time (corresponds to timestamp from Sep 2001).
if (heightParam > 1000000000) {
// Add a 2 hour buffer to include blocks which might have had old timestamps
- CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW);
+ CBlockIndex* pindex = chainActive.FindEarliestAtLeast(heightParam - TIMESTAMP_WINDOW, 0);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find block with at least the specified timestamp.");
}
@@ -1483,6 +1488,8 @@ static UniValue getchaintips(const JSONRPCRequest& request)
UniValue MempoolInfoToJSON(const CTxMemPool& pool)
{
+ // Make sure this call is atomic in the pool.
+ LOCK(pool.cs);
UniValue ret(UniValue::VOBJ);
ret.pushKV("loaded", pool.IsLoaded());
ret.pushKV("size", (int64_t)pool.size());
@@ -1581,15 +1588,15 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
uint256 hash(ParseHashV(request.params[0], "blockhash"));
CValidationState state;
+ CBlockIndex* pblockindex;
{
LOCK(cs_main);
- CBlockIndex* pblockindex = LookupBlockIndex(hash);
+ pblockindex = LookupBlockIndex(hash);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
-
- InvalidateBlock(state, Params(), pblockindex);
}
+ InvalidateBlock(state, Params(), pblockindex);
if (state.IsValid()) {
ActivateBestChain(state, Params());
@@ -2295,6 +2302,85 @@ UniValue scantxoutset(const JSONRPCRequest& request)
return result;
}
+static UniValue getblockfilter(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
+ throw std::runtime_error(
+ RPCHelpMan{"getblockfilter",
+ "\nRetrieve a BIP 157 content filter for a particular block.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hash of the block"},
+ {"filtertype", RPCArg::Type::STR, /*default*/ "basic", "The type name of the filter"},
+ },
+ RPCResult{
+ "{\n"
+ " \"filter\" : (string) the hex-encoded filter data\n"
+ " \"header\" : (string) the hex-encoded filter header\n"
+ "}\n"
+ },
+ RPCExamples{
+ HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"")
+ }
+ }.ToString()
+ );
+ }
+
+ uint256 block_hash = ParseHashV(request.params[0], "blockhash");
+ std::string filtertype_name = "basic";
+ if (!request.params[1].isNull()) {
+ filtertype_name = request.params[1].get_str();
+ }
+
+ BlockFilterType filtertype;
+ if (!BlockFilterTypeByName(filtertype_name, filtertype)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype");
+ }
+
+ BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
+ if (!index) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Index is not enabled for filtertype " + filtertype_name);
+ }
+
+ const CBlockIndex* block_index;
+ bool block_was_connected;
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(block_hash);
+ if (!block_index) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ block_was_connected = block_index->IsValid(BLOCK_VALID_SCRIPTS);
+ }
+
+ bool index_ready = index->BlockUntilSyncedToCurrentChain();
+
+ BlockFilter filter;
+ uint256 filter_header;
+ if (!index->LookupFilter(block_index, filter) ||
+ !index->LookupFilterHeader(block_index, filter_header)) {
+ int err_code;
+ std::string errmsg = "Filter not found.";
+
+ if (!block_was_connected) {
+ err_code = RPC_INVALID_ADDRESS_OR_KEY;
+ errmsg += " Block was not connected to active chain.";
+ } else if (!index_ready) {
+ err_code = RPC_MISC_ERROR;
+ errmsg += " Block filters are still in the process of being indexed.";
+ } else {
+ err_code = RPC_INTERNAL_ERROR;
+ errmsg += " This error is unexpected and indicates index corruption.";
+ }
+
+ throw JSONRPCError(err_code, errmsg);
+ }
+
+ UniValue ret(UniValue::VOBJ);
+ ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
+ ret.pushKV("header", filter_header.GetHex());
+ return ret;
+}
+
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -2322,6 +2408,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
{ "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} },
+ { "blockchain", "getblockfilter", &getblockfilter, {"blockhash", "filtertype"} },
/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, {"blockhash"} },
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index a266580b3d..4144a17bc3 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -92,8 +92,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "signrawtransactionwithkey", 2, "prevtxs" },
{ "signrawtransactionwithwallet", 1, "prevtxs" },
{ "sendrawtransaction", 1, "allowhighfees" },
+ { "sendrawtransaction", 1, "maxfeerate" },
{ "testmempoolaccept", 0, "rawtxs" },
{ "testmempoolaccept", 1, "allowhighfees" },
+ { "testmempoolaccept", 1, "maxfeerate" },
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index f2acb8fbf5..4de738a756 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -16,13 +16,16 @@
#include <policy/fees.h>
#include <pow.h>
#include <rpc/blockchain.h>
-#include <rpc/mining.h>
#include <rpc/server.h>
#include <rpc/util.h>
+#include <script/script.h>
#include <shutdown.h>
#include <txmempool.h>
+#include <univalue.h>
+#include <util/fees.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/validation.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -98,7 +101,7 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1);
}
-UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript)
+static UniValue generateBlocks(const CScript& coinbase_script, int nGenerate, uint64_t nMaxTries)
{
static const int nInnerLoopCount = 0x10000;
int nHeightEnd = 0;
@@ -113,7 +116,7 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd && !ShutdownRequested())
{
- std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript));
+ std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbase_script));
if (!pblocktemplate.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
CBlock *pblock = &pblocktemplate->block;
@@ -136,12 +139,6 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex());
-
- //mark script as important because it was used at least for one coinbase output if the script came from the wallet
- if (keepScript)
- {
- coinbaseScript->KeepScript();
- }
}
return blockHashes;
}
@@ -179,10 +176,9 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
}
- std::shared_ptr<CReserveScript> coinbaseScript = std::make_shared<CReserveScript>();
- coinbaseScript->reserveScript = GetScriptForDestination(destination);
+ CScript coinbase_script = GetScriptForDestination(destination);
- return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false);
+ return generateBlocks(coinbase_script, nGenerate, nMaxTries);
}
static UniValue getmininginfo(const JSONRPCRequest& request)
@@ -444,10 +440,10 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
- throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Bitcoin is not connected!");
+ throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
if (IsInitialBlockDownload())
- throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks...");
+ throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks...");
static unsigned int nTransactionsUpdatedLast;
diff --git a/src/rpc/mining.h b/src/rpc/mining.h
deleted file mode 100644
index be9a973315..0000000000
--- a/src/rpc/mining.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2017 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_RPC_MINING_H
-#define BITCOIN_RPC_MINING_H
-
-#include <script/script.h>
-
-#include <univalue.h>
-
-/** Generate blocks (mine) */
-UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript);
-
-#endif
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 822a0beef9..bfb559f0db 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -20,6 +20,7 @@
#include <timedata.h>
#include <util/system.h>
#include <util/strencodings.h>
+#include <util/validation.h>
#include <warnings.h>
#include <stdint.h>
@@ -34,11 +35,7 @@ static UniValue validateaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
RPCHelpMan{"validateaddress",
- "\nReturn information about the given bitcoin address.\n"
- "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n"
- "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n"
- "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n"
- "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n",
+ "\nReturn information about the given bitcoin address.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"},
},
@@ -232,8 +229,8 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
range_end = range.second;
}
- FlatSigningProvider provider;
- auto desc = Parse(desc_str, provider, /* require_checksum = */ true);
+ FlatSigningProvider key_provider;
+ auto desc = Parse(desc_str, key_provider, /* require_checksum = */ true);
if (!desc) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor"));
}
@@ -249,8 +246,9 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
UniValue addresses(UniValue::VARR);
for (int i = range_begin; i <= range_end; ++i) {
+ FlatSigningProvider provider;
std::vector<CScript> scripts;
- if (!desc->Expand(i, provider, scripts, provider)) {
+ if (!desc->Expand(i, key_provider, scripts, provider)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys"));
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index c7b3478f44..e8cdce623c 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -12,6 +12,7 @@
#include <net_processing.h>
#include <netbase.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <rpc/protocol.h>
#include <rpc/util.h>
#include <sync.h>
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index d19afaa8a1..78d7bbc80c 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -11,15 +11,19 @@
#include <core_io.h>
#include <index/txindex.h>
#include <init.h>
+#include <interfaces/chain.h>
#include <key_io.h>
#include <keystore.h>
#include <merkleblock.h>
+#include <node/coin.h>
+#include <node/psbt.h>
#include <node/transaction.h>
#include <policy/policy.h>
#include <policy/rbf.h>
+#include <policy/settings.h>
#include <primitives/transaction.h>
#include <psbt.h>
-#include <rpc/rawtransaction.h>
+#include <rpc/rawtransaction_util.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/script.h>
@@ -28,6 +32,7 @@
#include <script/standard.h>
#include <uint256.h>
#include <util/bip32.h>
+#include <util/moneystr.h>
#include <util/strencodings.h>
#include <validation.h>
#include <validationinterface.h>
@@ -38,6 +43,11 @@
#include <univalue.h>
+/** High fee for sendrawtransaction and testmempoolaccept.
+ * By default, transaction with a fee higher than this will be rejected by the
+ * RPCs. This can be overriden with the maxfeerate argument.
+ */
+constexpr static CAmount DEFAULT_MAX_RAW_TX_FEE{COIN / 10};
static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
{
@@ -352,119 +362,6 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
return res;
}
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
-{
- if (inputs_in.isNull() || outputs_in.isNull())
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
-
- UniValue inputs = inputs_in.get_array();
- const bool outputs_is_obj = outputs_in.isObject();
- UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
-
- CMutableTransaction rawTx;
-
- if (!locktime.isNull()) {
- int64_t nLockTime = locktime.get_int64();
- if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
- rawTx.nLockTime = nLockTime;
- }
-
- bool rbfOptIn = rbf.isTrue();
-
- for (unsigned int idx = 0; idx < inputs.size(); idx++) {
- const UniValue& input = inputs[idx];
- const UniValue& o = input.get_obj();
-
- uint256 txid = ParseHashO(o, "txid");
-
- const UniValue& vout_v = find_value(o, "vout");
- if (!vout_v.isNum())
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
- int nOutput = vout_v.get_int();
- if (nOutput < 0)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
-
- uint32_t nSequence;
- if (rbfOptIn) {
- nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
- } else if (rawTx.nLockTime) {
- nSequence = CTxIn::SEQUENCE_FINAL - 1;
- } else {
- nSequence = CTxIn::SEQUENCE_FINAL;
- }
-
- // set the sequence number if passed in the parameters object
- const UniValue& sequenceObj = find_value(o, "sequence");
- if (sequenceObj.isNum()) {
- int64_t seqNr64 = sequenceObj.get_int64();
- if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
- } else {
- nSequence = (uint32_t)seqNr64;
- }
- }
-
- CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
-
- rawTx.vin.push_back(in);
- }
-
- if (!outputs_is_obj) {
- // Translate array of key-value pairs into dict
- UniValue outputs_dict = UniValue(UniValue::VOBJ);
- for (size_t i = 0; i < outputs.size(); ++i) {
- const UniValue& output = outputs[i];
- if (!output.isObject()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
- }
- if (output.size() != 1) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
- }
- outputs_dict.pushKVs(output);
- }
- outputs = std::move(outputs_dict);
- }
-
- // Duplicate checking
- std::set<CTxDestination> destinations;
- bool has_data{false};
-
- for (const std::string& name_ : outputs.getKeys()) {
- if (name_ == "data") {
- if (has_data) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
- }
- has_data = true;
- std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
-
- CTxOut out(0, CScript() << OP_RETURN << data);
- rawTx.vout.push_back(out);
- } else {
- CTxDestination destination = DecodeDestination(name_);
- if (!IsValidDestination(destination)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
- }
-
- if (!destinations.insert(destination).second) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
- }
-
- CScript scriptPubKey = GetScriptForDestination(destination);
- CAmount nAmount = AmountFromValue(outputs[name_]);
-
- CTxOut out(nAmount, scriptPubKey);
- rawTx.vout.push_back(out);
- }
- }
-
- if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
- }
-
- return rawTx;
-}
-
static UniValue createrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
@@ -608,33 +505,54 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
return result;
}
+static std::string GetAllOutputTypes()
+{
+ std::string ret;
+ for (int i = TX_NONSTANDARD; i <= TX_WITNESS_UNKNOWN; ++i) {
+ if (i != TX_NONSTANDARD) ret += ", ";
+ ret += GetTxnOutputType(static_cast<txnouttype>(i));
+ }
+ return ret;
+}
+
static UniValue decodescript(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() != 1)
- throw std::runtime_error(
- RPCHelpMan{"decodescript",
+ const RPCHelpMan help{"decodescript",
"\nDecode a hex-encoded script.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"},
},
RPCResult{
"{\n"
- " \"asm\":\"asm\", (string) Script public key\n"
- " \"hex\":\"hex\", (string) hex-encoded public key\n"
- " \"type\":\"type\", (string) The output type\n"
- " \"reqSigs\": n, (numeric) The required signatures\n"
- " \"addresses\": [ (json array of string)\n"
- " \"address\" (string) bitcoin address\n"
+ " \"asm\":\"asm\", (string) Script public key\n"
+ " \"type\":\"type\", (string) The output type (e.g. "+GetAllOutputTypes()+")\n"
+ " \"reqSigs\": n, (numeric) The required signatures\n"
+ " \"addresses\": [ (json array of string)\n"
+ " \"address\" (string) bitcoin address\n"
" ,...\n"
" ],\n"
- " \"p2sh\",\"address\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n"
+ " \"p2sh\":\"str\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n"
+ " \"segwit\": { (json object) Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness).\n"
+ " \"asm\":\"str\", (string) String representation of the script public key\n"
+ " \"hex\":\"hexstr\", (string) Hex string of the script public key\n"
+ " \"type\":\"str\", (string) The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)\n"
+ " \"reqSigs\": n, (numeric) The required signatures (always 1)\n"
+ " \"addresses\": [ (json array of string) (always length 1)\n"
+ " \"address\" (string) segwit address\n"
+ " ,...\n"
+ " ],\n"
+ " \"p2sh-segwit\":\"str\" (string) address of the P2SH script wrapping this witness redeem script.\n"
"}\n"
},
RPCExamples{
HelpExampleCli("decodescript", "\"hexstring\"")
+ HelpExampleRpc("decodescript", "\"hexstring\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
RPCTypeCheck(request.params, {UniValue::VSTR});
@@ -646,7 +564,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
} else {
// Empty scripts are valid
}
- ScriptPubKeyToUniv(script, r, false);
+ ScriptPubKeyToUniv(script, r, /* fIncludeHex */ false);
UniValue type;
type = find_value(r, "type");
@@ -680,7 +598,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
// Newer segwit program versions should be considered when then become available.
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
}
- ScriptPubKeyToUniv(segwitScr, sr, true);
+ ScriptPubKeyToUniv(segwitScr, sr, /* fIncludeHex */ true);
sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
r.pushKV("segwit", sr);
}
@@ -689,23 +607,6 @@ static UniValue decodescript(const JSONRPCRequest& request)
return r;
}
-/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
-static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
-{
- UniValue entry(UniValue::VOBJ);
- entry.pushKV("txid", txin.prevout.hash.ToString());
- entry.pushKV("vout", (uint64_t)txin.prevout.n);
- UniValue witness(UniValue::VARR);
- for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
- witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end()));
- }
- entry.pushKV("witness", witness);
- entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
- entry.pushKV("sequence", (uint64_t)txin.nSequence);
- entry.pushKV("error", strMessage);
- vErrorsRet.push_back(entry);
-}
-
static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
@@ -790,155 +691,6 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
return EncodeHexTx(CTransaction(mergedTx));
}
-UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType)
-{
- // Fetch previous transactions (inputs):
- CCoinsView viewDummy;
- CCoinsViewCache view(&viewDummy);
- {
- LOCK2(cs_main, mempool.cs);
- CCoinsViewCache &viewChain = *pcoinsTip;
- CCoinsViewMemPool viewMempool(&viewChain, mempool);
- view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
-
- for (const CTxIn& txin : mtx.vin) {
- view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
- }
-
- view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
- }
-
- // Add previous txouts given in the RPC call:
- if (!prevTxsUnival.isNull()) {
- UniValue prevTxs = prevTxsUnival.get_array();
- for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {
- const UniValue& p = prevTxs[idx];
- if (!p.isObject()) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
- }
-
- UniValue prevOut = p.get_obj();
-
- RPCTypeCheckObj(prevOut,
- {
- {"txid", UniValueType(UniValue::VSTR)},
- {"vout", UniValueType(UniValue::VNUM)},
- {"scriptPubKey", UniValueType(UniValue::VSTR)},
- });
-
- uint256 txid = ParseHashO(prevOut, "txid");
-
- int nOut = find_value(prevOut, "vout").get_int();
- if (nOut < 0) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
- }
-
- COutPoint out(txid, nOut);
- std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
- CScript scriptPubKey(pkData.begin(), pkData.end());
-
- {
- const Coin& coin = view.AccessCoin(out);
- if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) {
- std::string err("Previous output scriptPubKey mismatch:\n");
- err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
- ScriptToAsmStr(scriptPubKey);
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
- }
- Coin newcoin;
- newcoin.out.scriptPubKey = scriptPubKey;
- newcoin.out.nValue = MAX_MONEY;
- if (prevOut.exists("amount")) {
- newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
- }
- newcoin.nHeight = 1;
- view.AddCoin(out, std::move(newcoin), true);
- }
-
- // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
- if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
- RPCTypeCheckObj(prevOut,
- {
- {"redeemScript", UniValueType(UniValue::VSTR)},
- {"witnessScript", UniValueType(UniValue::VSTR)},
- }, true);
- UniValue rs = find_value(prevOut, "redeemScript");
- if (!rs.isNull()) {
- std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
- CScript redeemScript(rsData.begin(), rsData.end());
- keystore->AddCScript(redeemScript);
- // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
- // This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
- keystore->AddCScript(GetScriptForWitness(redeemScript));
- }
- UniValue ws = find_value(prevOut, "witnessScript");
- if (!ws.isNull()) {
- std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
- CScript witnessScript(wsData.begin(), wsData.end());
- keystore->AddCScript(witnessScript);
- // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
- keystore->AddCScript(GetScriptForWitness(witnessScript));
- }
- }
- }
- }
-
- int nHashType = ParseSighashString(hashType);
-
- bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
-
- // Script verification errors
- UniValue vErrors(UniValue::VARR);
-
- // Use CTransaction for the constant parts of the
- // transaction to avoid rehashing.
- const CTransaction txConst(mtx);
- // Sign what we can:
- for (unsigned int i = 0; i < mtx.vin.size(); i++) {
- CTxIn& txin = mtx.vin[i];
- const Coin& coin = view.AccessCoin(txin.prevout);
- if (coin.IsSpent()) {
- TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
- continue;
- }
- const CScript& prevPubKey = coin.out.scriptPubKey;
- const CAmount& amount = coin.out.nValue;
-
- SignatureData sigdata = DataFromTransaction(mtx, i, coin.out);
- // Only sign SIGHASH_SINGLE if there's a corresponding output:
- if (!fHashSingle || (i < mtx.vout.size())) {
- ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
- }
-
- UpdateInput(txin, sigdata);
-
- // amount must be specified for valid segwit signature
- if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin.out.ToString()));
- }
-
- ScriptError serror = SCRIPT_ERR_OK;
- if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
- if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
- // Unable to sign input and verification failed (possible attempt to partially sign).
- TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
- } else {
- TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
- }
- }
- }
- bool fComplete = vErrors.empty();
-
- UniValue result(UniValue::VOBJ);
- result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
- result.pushKV("complete", fComplete);
- if (!vErrors.empty()) {
- result.pushKV("errors", vErrors);
- }
-
- return result;
-}
-
static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
@@ -965,7 +717,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
{"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
- {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"},
+ {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "(required for Segwit inputs) the amount spent"},
},
},
},
@@ -1019,27 +771,26 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
keystore.AddKey(key);
}
- return SignTransaction(*g_rpc_interfaces->chain, mtx, request.params[2], &keystore, true, request.params[3]);
-}
+ // Fetch previous transactions (inputs):
+ std::map<COutPoint, Coin> coins;
+ for (const CTxIn& txin : mtx.vin) {
+ coins[txin.prevout]; // Create empty map entry keyed by prevout.
+ }
+ FindCoins(coins);
-UniValue signrawtransaction(const JSONRPCRequest& request)
-{
- // This method should be removed entirely in V0.19, along with the entries in the
- // CRPCCommand table and rpc/client.cpp.
- throw JSONRPCError(RPC_METHOD_DEPRECATED, "signrawtransaction was removed in v0.18.\n"
- "Clients should transition to using signrawtransactionwithkey and signrawtransactionwithwallet");
+ return SignTransaction(mtx, request.params[2], &keystore, coins, true, request.params[3]);
}
static UniValue sendrawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- throw std::runtime_error(
- RPCHelpMan{"sendrawtransaction",
+ const RPCHelpMan help{"sendrawtransaction",
"\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
"\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
- {"allowhighfees", RPCArg::Type::BOOL, /* default */ "false", "Allow high fees"},
+ {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE),
+ "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
+ "/kB.\nSet to 0 to accept any fee rate.\n"},
},
RPCResult{
"\"hex\" (string) The transaction hash in hex\n"
@@ -1054,9 +805,16 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
},
- }.ToString());
+ };
- RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
+
+ RPCTypeCheck(request.params, {
+ UniValue::VSTR,
+ UniValueType(), // NUM or BOOL, checked later
+ });
// parse hex string from parameter
CMutableTransaction mtx;
@@ -1064,12 +822,22 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
- bool allowhighfees = false;
- if (!request.params[1].isNull()) allowhighfees = request.params[1].get_bool();
- const CAmount highfee{allowhighfees ? 0 : ::maxTxFee};
+ CAmount max_raw_tx_fee = DEFAULT_MAX_RAW_TX_FEE;
+ // TODO: temporary migration code for old clients. Remove in v0.20
+ if (request.params[1].isBool()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0.");
+ } else if (!request.params[1].isNull()) {
+ size_t weight = GetTransactionWeight(*tx);
+ CFeeRate fr(AmountFromValue(request.params[1]));
+ // the +3/4 part rounds the value up, and is the same formula used when
+ // calculating the fee for a transaction
+ // (see GetVirtualTransactionSize)
+ max_raw_tx_fee = fr.GetFee((weight+3)/4);
+ }
+
uint256 txid;
std::string err_string;
- const TransactionError err = BroadcastTransaction(tx, txid, err_string, highfee);
+ const TransactionError err = BroadcastTransaction(tx, txid, err_string, max_raw_tx_fee);
if (TransactionError::OK != err) {
throw JSONRPCTransactionError(err, err_string);
}
@@ -1079,9 +847,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
static UniValue testmempoolaccept(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
- RPCHelpMan{"testmempoolaccept",
+ const RPCHelpMan help{"testmempoolaccept",
"\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
"\nThis checks if the transaction violates the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n",
@@ -1092,7 +858,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
},
},
- {"allowhighfees", RPCArg::Type::BOOL, /* default */ "false", "Allow high fees"},
+ {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
},
RPCResult{
"[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n"
@@ -1114,10 +880,17 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
}
- RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL});
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // NUM or BOOL, checked later
+ });
+
if (request.params[0].get_array().size() != 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now");
}
@@ -1129,9 +902,17 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
const uint256& tx_hash = tx->GetHash();
- CAmount max_raw_tx_fee = ::maxTxFee;
- if (!request.params[1].isNull() && request.params[1].get_bool()) {
- max_raw_tx_fee = 0;
+ CAmount max_raw_tx_fee = DEFAULT_MAX_RAW_TX_FEE;
+ // TODO: temporary migration code for old clients. Remove in v0.20
+ if (request.params[1].isBool()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0.");
+ } else if (!request.params[1].isNull()) {
+ size_t weight = GetTransactionWeight(*tx);
+ CFeeRate fr(AmountFromValue(request.params[1]));
+ // the +3/4 part rounds the value up, and is the same formula used when
+ // calculating the fee for a transaction
+ // (see GetVirtualTransactionSize)
+ max_raw_tx_fee = fr.GetFee((weight+3)/4);
}
UniValue result(UniValue::VARR);
@@ -1500,7 +1281,6 @@ UniValue combinepsbt(const JSONRPCRequest& request)
throw JSONRPCTransactionError(error);
}
- UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << merged_psbt;
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
@@ -1862,21 +1642,21 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
" \"has_utxo\" : true|false (boolean) Whether a UTXO is provided\n"
" \"is_final\" : true|false (boolean) Whether the input is finalized\n"
" \"missing\" : { (json object, optional) Things that are missing that are required to complete this input\n"
- " \"pubkeys\" : [ (array)\n"
+ " \"pubkeys\" : [ (array, optional)\n"
" \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing\n"
" ]\n"
- " \"signatures\" : [ (array)\n"
+ " \"signatures\" : [ (array, optional)\n"
" \"keyid\" (string) Public key ID, hash160 of the public key, of a public key whose signature is missing\n"
" ]\n"
- " \"redeemscript\" : \"hash\" (string) Hash160 of the redeemScript that is missing\n"
- " \"witnessscript\" : \"hash\" (string) SHA256 of the witnessScript that is missing\n"
+ " \"redeemscript\" : \"hash\" (string, optional) Hash160 of the redeemScript that is missing\n"
+ " \"witnessscript\" : \"hash\" (string, optional) SHA256 of the witnessScript that is missing\n"
" }\n"
- " \"next\" : \"role\" (string) Role of the next person that this input needs to go to\n"
+ " \"next\" : \"role\" (string, optional) Role of the next person that this input needs to go to\n"
" }\n"
" ,...\n"
" ]\n"
- " \"estimated_vsize\" : vsize (numeric) Estimated vsize of the final signed transaction\n"
- " \"estimated_feerate\" : feerate (numeric, optional) Estimated feerate of the final signed transaction. Shown only if all UTXO slots in the PSBT have been filled.\n"
+ " \"estimated_vsize\" : vsize (numeric, optional) Estimated vsize of the final signed transaction\n"
+ " \"estimated_feerate\" : feerate (numeric, optional) Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled.\n"
" \"fee\" : fee (numeric, optional) The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled.\n"
" \"next\" : \"role\" (string) Role of the next person that this psbt needs to go to\n"
"}\n"
@@ -1895,148 +1675,56 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
- // Go through each input and build status
+ PSBTAnalysis psbta = AnalyzePSBT(psbtx);
+
UniValue result(UniValue::VOBJ);
UniValue inputs_result(UniValue::VARR);
- bool calc_fee = true;
- bool all_final = true;
- bool only_missing_sigs = true;
- bool only_missing_final = false;
- CAmount in_amt = 0;
- for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- PSBTInput& input = psbtx.inputs[i];
+ for (const auto& input : psbta.inputs) {
UniValue input_univ(UniValue::VOBJ);
UniValue missing(UniValue::VOBJ);
- // Check for a UTXO
- CTxOut utxo;
- if (psbtx.GetInputUTXO(utxo, i)) {
- in_amt += utxo.nValue;
- input_univ.pushKV("has_utxo", true);
- } else {
- input_univ.pushKV("has_utxo", false);
- input_univ.pushKV("is_final", false);
- input_univ.pushKV("next", "updater");
- calc_fee = false;
- }
+ input_univ.pushKV("has_utxo", input.has_utxo);
+ input_univ.pushKV("is_final", input.is_final);
+ input_univ.pushKV("next", PSBTRoleName(input.next));
- // Check if it is final
- if (!utxo.IsNull() && !PSBTInputSigned(input)) {
- input_univ.pushKV("is_final", false);
- all_final = false;
-
- // Figure out what is missing
- SignatureData outdata;
- bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
-
- // Things are missing
- if (!complete) {
- if (!outdata.missing_pubkeys.empty()) {
- // Missing pubkeys
- UniValue missing_pubkeys_univ(UniValue::VARR);
- for (const CKeyID& pubkey : outdata.missing_pubkeys) {
- missing_pubkeys_univ.push_back(HexStr(pubkey));
- }
- missing.pushKV("pubkeys", missing_pubkeys_univ);
- }
- if (!outdata.missing_redeem_script.IsNull()) {
- // Missing redeemScript
- missing.pushKV("redeemscript", HexStr(outdata.missing_redeem_script));
- }
- if (!outdata.missing_witness_script.IsNull()) {
- // Missing witnessScript
- missing.pushKV("witnessscript", HexStr(outdata.missing_witness_script));
- }
- if (!outdata.missing_sigs.empty()) {
- // Missing sigs
- UniValue missing_sigs_univ(UniValue::VARR);
- for (const CKeyID& pubkey : outdata.missing_sigs) {
- missing_sigs_univ.push_back(HexStr(pubkey));
- }
- missing.pushKV("signatures", missing_sigs_univ);
- }
- input_univ.pushKV("missing", missing);
-
- // If we are only missing signatures and nothing else, then next is signer
- if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) {
- input_univ.pushKV("next", "signer");
- } else {
- only_missing_sigs = false;
- input_univ.pushKV("next", "updater");
- }
- } else {
- only_missing_final = true;
- input_univ.pushKV("next", "finalizer");
+ if (!input.missing_pubkeys.empty()) {
+ UniValue missing_pubkeys_univ(UniValue::VARR);
+ for (const CKeyID& pubkey : input.missing_pubkeys) {
+ missing_pubkeys_univ.push_back(HexStr(pubkey));
+ }
+ missing.pushKV("pubkeys", missing_pubkeys_univ);
+ }
+ if (!input.missing_redeem_script.IsNull()) {
+ missing.pushKV("redeemscript", HexStr(input.missing_redeem_script));
+ }
+ if (!input.missing_witness_script.IsNull()) {
+ missing.pushKV("witnessscript", HexStr(input.missing_witness_script));
+ }
+ if (!input.missing_sigs.empty()) {
+ UniValue missing_sigs_univ(UniValue::VARR);
+ for (const CKeyID& pubkey : input.missing_sigs) {
+ missing_sigs_univ.push_back(HexStr(pubkey));
}
- } else if (!utxo.IsNull()){
- input_univ.pushKV("is_final", true);
+ missing.pushKV("signatures", missing_sigs_univ);
+ }
+ if (!missing.getKeys().empty()) {
+ input_univ.pushKV("missing", missing);
}
inputs_result.push_back(input_univ);
}
result.pushKV("inputs", inputs_result);
- if (all_final) {
- only_missing_sigs = false;
- result.pushKV("next", "extractor");
+ if (psbta.estimated_vsize != nullopt) {
+ result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize);
}
- if (calc_fee) {
- // Get the output amount
- CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), 0,
- [](int a, const CTxOut& b) {
- return a += b.nValue;
- }
- );
-
- // Get the fee
- CAmount fee = in_amt - out_amt;
-
- // Estimate the size
- CMutableTransaction mtx(*psbtx.tx);
- CCoinsView view_dummy;
- CCoinsViewCache view(&view_dummy);
- bool success = true;
-
- for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- PSBTInput& input = psbtx.inputs[i];
- if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true)) {
- mtx.vin[i].scriptSig = input.final_script_sig;
- mtx.vin[i].scriptWitness = input.final_script_witness;
-
- Coin newcoin;
- if (!psbtx.GetInputUTXO(newcoin.out, i)) {
- success = false;
- break;
- }
- newcoin.nHeight = 1;
- view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
- } else {
- success = false;
- break;
- }
- }
-
- if (success) {
- CTransaction ctx = CTransaction(mtx);
- size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
- result.pushKV("estimated_vsize", (int)size);
- // Estimate fee rate
- CFeeRate feerate(fee, size);
- result.pushKV("estimated_feerate", feerate.ToString());
- }
- result.pushKV("fee", ValueFromAmount(fee));
-
- if (only_missing_sigs) {
- result.pushKV("next", "signer");
- } else if (only_missing_final) {
- result.pushKV("next", "finalizer");
- } else if (all_final) {
- result.pushKV("next", "extractor");
- } else {
- result.pushKV("next", "updater");
- }
- } else {
- result.pushKV("next", "updater");
+ if (psbta.estimated_feerate != nullopt) {
+ result.pushKV("estimated_feerate", ValueFromAmount(psbta.estimated_feerate->GetFeePerK()));
}
+ if (psbta.fee != nullopt) {
+ result.pushKV("fee", ValueFromAmount(*psbta.fee));
+ }
+ result.pushKV("next", PSBTRoleName(psbta.next));
+
return result;
}
@@ -2048,11 +1736,10 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
- { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
+ { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees|maxfeerate"} },
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
- { "hidden", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} },
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
- { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} },
+ { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees|maxfeerate"} },
{ "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} },
{ "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} },
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h
deleted file mode 100644
index 52d701d1c3..0000000000
--- a/src/rpc/rawtransaction.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2017-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_RPC_RAWTRANSACTION_H
-#define BITCOIN_RPC_RAWTRANSACTION_H
-
-class CBasicKeyStore;
-struct CMutableTransaction;
-class UniValue;
-
-namespace interfaces {
-class Chain;
-} // namespace interfaces
-
-/** Sign a transaction with the given keystore and previous transactions */
-UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
-
-/** Create a transaction from univalue parameters */
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
-
-#endif // BITCOIN_RPC_RAWTRANSACTION_H
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
new file mode 100644
index 0000000000..8af491977c
--- /dev/null
+++ b/src/rpc/rawtransaction_util.cpp
@@ -0,0 +1,283 @@
+// Copyright (c) 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.
+
+#include <rpc/rawtransaction_util.h>
+
+#include <coins.h>
+#include <core_io.h>
+#include <interfaces/chain.h>
+#include <key_io.h>
+#include <keystore.h>
+#include <policy/policy.h>
+#include <primitives/transaction.h>
+#include <rpc/protocol.h>
+#include <rpc/util.h>
+#include <tinyformat.h>
+#include <univalue.h>
+#include <util/rbf.h>
+#include <util/strencodings.h>
+
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
+{
+ if (inputs_in.isNull() || outputs_in.isNull())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
+
+ UniValue inputs = inputs_in.get_array();
+ const bool outputs_is_obj = outputs_in.isObject();
+ UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
+
+ CMutableTransaction rawTx;
+
+ if (!locktime.isNull()) {
+ int64_t nLockTime = locktime.get_int64();
+ if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
+ rawTx.nLockTime = nLockTime;
+ }
+
+ bool rbfOptIn = rbf.isTrue();
+
+ for (unsigned int idx = 0; idx < inputs.size(); idx++) {
+ const UniValue& input = inputs[idx];
+ const UniValue& o = input.get_obj();
+
+ uint256 txid = ParseHashO(o, "txid");
+
+ const UniValue& vout_v = find_value(o, "vout");
+ if (!vout_v.isNum())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
+ int nOutput = vout_v.get_int();
+ if (nOutput < 0)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
+
+ uint32_t nSequence;
+ if (rbfOptIn) {
+ nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
+ } else if (rawTx.nLockTime) {
+ nSequence = CTxIn::SEQUENCE_FINAL - 1;
+ } else {
+ nSequence = CTxIn::SEQUENCE_FINAL;
+ }
+
+ // set the sequence number if passed in the parameters object
+ const UniValue& sequenceObj = find_value(o, "sequence");
+ if (sequenceObj.isNum()) {
+ int64_t seqNr64 = sequenceObj.get_int64();
+ if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
+ } else {
+ nSequence = (uint32_t)seqNr64;
+ }
+ }
+
+ CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
+
+ rawTx.vin.push_back(in);
+ }
+
+ if (!outputs_is_obj) {
+ // Translate array of key-value pairs into dict
+ UniValue outputs_dict = UniValue(UniValue::VOBJ);
+ for (size_t i = 0; i < outputs.size(); ++i) {
+ const UniValue& output = outputs[i];
+ if (!output.isObject()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
+ }
+ if (output.size() != 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
+ }
+ outputs_dict.pushKVs(output);
+ }
+ outputs = std::move(outputs_dict);
+ }
+
+ // Duplicate checking
+ std::set<CTxDestination> destinations;
+ bool has_data{false};
+
+ for (const std::string& name_ : outputs.getKeys()) {
+ if (name_ == "data") {
+ if (has_data) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
+ }
+ has_data = true;
+ std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
+
+ CTxOut out(0, CScript() << OP_RETURN << data);
+ rawTx.vout.push_back(out);
+ } else {
+ CTxDestination destination = DecodeDestination(name_);
+ if (!IsValidDestination(destination)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
+ }
+
+ if (!destinations.insert(destination).second) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
+ }
+
+ CScript scriptPubKey = GetScriptForDestination(destination);
+ CAmount nAmount = AmountFromValue(outputs[name_]);
+
+ CTxOut out(nAmount, scriptPubKey);
+ rawTx.vout.push_back(out);
+ }
+ }
+
+ if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
+ }
+
+ return rawTx;
+}
+
+/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
+static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
+{
+ UniValue entry(UniValue::VOBJ);
+ entry.pushKV("txid", txin.prevout.hash.ToString());
+ entry.pushKV("vout", (uint64_t)txin.prevout.n);
+ UniValue witness(UniValue::VARR);
+ for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
+ witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end()));
+ }
+ entry.pushKV("witness", witness);
+ entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
+ entry.pushKV("sequence", (uint64_t)txin.nSequence);
+ entry.pushKV("error", strMessage);
+ vErrorsRet.push_back(entry);
+}
+
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool is_temp_keystore, const UniValue& hashType)
+{
+ // Add previous txouts given in the RPC call:
+ if (!prevTxsUnival.isNull()) {
+ UniValue prevTxs = prevTxsUnival.get_array();
+ for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {
+ const UniValue& p = prevTxs[idx];
+ if (!p.isObject()) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
+ }
+
+ UniValue prevOut = p.get_obj();
+
+ RPCTypeCheckObj(prevOut,
+ {
+ {"txid", UniValueType(UniValue::VSTR)},
+ {"vout", UniValueType(UniValue::VNUM)},
+ {"scriptPubKey", UniValueType(UniValue::VSTR)},
+ });
+
+ uint256 txid = ParseHashO(prevOut, "txid");
+
+ int nOut = find_value(prevOut, "vout").get_int();
+ if (nOut < 0) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
+ }
+
+ COutPoint out(txid, nOut);
+ std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
+ CScript scriptPubKey(pkData.begin(), pkData.end());
+
+ {
+ auto coin = coins.find(out);
+ if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) {
+ std::string err("Previous output scriptPubKey mismatch:\n");
+ err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+
+ ScriptToAsmStr(scriptPubKey);
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
+ }
+ Coin newcoin;
+ newcoin.out.scriptPubKey = scriptPubKey;
+ newcoin.out.nValue = MAX_MONEY;
+ if (prevOut.exists("amount")) {
+ newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
+ }
+ newcoin.nHeight = 1;
+ coins[out] = std::move(newcoin);
+ }
+
+ // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
+ if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
+ RPCTypeCheckObj(prevOut,
+ {
+ {"redeemScript", UniValueType(UniValue::VSTR)},
+ {"witnessScript", UniValueType(UniValue::VSTR)},
+ }, true);
+ UniValue rs = find_value(prevOut, "redeemScript");
+ if (!rs.isNull()) {
+ std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
+ CScript redeemScript(rsData.begin(), rsData.end());
+ keystore->AddCScript(redeemScript);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ // This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
+ keystore->AddCScript(GetScriptForWitness(redeemScript));
+ }
+ UniValue ws = find_value(prevOut, "witnessScript");
+ if (!ws.isNull()) {
+ std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
+ CScript witnessScript(wsData.begin(), wsData.end());
+ keystore->AddCScript(witnessScript);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ keystore->AddCScript(GetScriptForWitness(witnessScript));
+ }
+ }
+ }
+ }
+
+ int nHashType = ParseSighashString(hashType);
+
+ bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
+
+ // Script verification errors
+ UniValue vErrors(UniValue::VARR);
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(mtx);
+ // Sign what we can:
+ for (unsigned int i = 0; i < mtx.vin.size(); i++) {
+ CTxIn& txin = mtx.vin[i];
+ auto coin = coins.find(txin.prevout);
+ if (coin == coins.end() || coin->second.IsSpent()) {
+ TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
+ continue;
+ }
+ const CScript& prevPubKey = coin->second.out.scriptPubKey;
+ const CAmount& amount = coin->second.out.nValue;
+
+ SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out);
+ // Only sign SIGHASH_SINGLE if there's a corresponding output:
+ if (!fHashSingle || (i < mtx.vout.size())) {
+ ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata);
+ }
+
+ UpdateInput(txin, sigdata);
+
+ // amount must be specified for valid segwit signature
+ if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString()));
+ }
+
+ ScriptError serror = SCRIPT_ERR_OK;
+ if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
+ if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
+ // Unable to sign input and verification failed (possible attempt to partially sign).
+ TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
+ } else {
+ TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
+ }
+ }
+ }
+ bool fComplete = vErrors.empty();
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
+ result.pushKV("complete", fComplete);
+ if (!vErrors.empty()) {
+ result.pushKV("errors", vErrors);
+ }
+
+ return result;
+}
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
new file mode 100644
index 0000000000..c115d33a77
--- /dev/null
+++ b/src/rpc/rawtransaction_util.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2017-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_RPC_RAWTRANSACTION_UTIL_H
+#define BITCOIN_RPC_RAWTRANSACTION_UTIL_H
+
+#include <map>
+
+class CBasicKeyStore;
+class UniValue;
+struct CMutableTransaction;
+class Coin;
+class COutPoint;
+
+/**
+ * Sign a transaction with the given keystore and previous transactions
+ *
+ * @param mtx The transaction to-be-signed
+ * @param prevTxs Array of previous txns outputs that tx depends on but may not yet be in the block chain
+ * @param keystore Temporary keystore containing signing keys
+ * @param coins Map of unspent outputs - coins in mempool and current chain UTXO set, may be extended by previous txns outputs after call
+ * @param tempKeystore Whether to use temporary keystore
+ * @param hashType The signature hash type
+ * @returns JSON object with details of signed transaction
+ */
+UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore* keystore, std::map<COutPoint, Coin>& coins, bool tempKeystore, const UniValue& hashType);
+
+/** Create a transaction from univalue parameters */
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
+
+#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index cd90573da0..9df4070cbb 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -30,6 +30,7 @@ static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server starte
static RPCTimerInterface* timerInterface = nullptr;
/* Map of name to timer. */
static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
+static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler);
struct RPCCommandExecutionInfo
{
@@ -76,108 +77,15 @@ void RPCServer::OnStopped(std::function<void ()> slot)
g_rpcSignals.Stopped.connect(slot);
}
-void RPCTypeCheck(const UniValue& params,
- const std::list<UniValueType>& typesExpected,
- bool fAllowNull)
-{
- unsigned int i = 0;
- for (const UniValueType& t : typesExpected) {
- if (params.size() <= i)
- break;
-
- const UniValue& v = params[i];
- if (!(fAllowNull && v.isNull())) {
- RPCTypeCheckArgument(v, t);
- }
- i++;
- }
-}
-
-void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
-{
- if (!typeExpected.typeAny && value.type() != typeExpected.type) {
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
- }
-}
-
-void RPCTypeCheckObj(const UniValue& o,
- const std::map<std::string, UniValueType>& typesExpected,
- bool fAllowNull,
- bool fStrict)
-{
- for (const auto& t : typesExpected) {
- const UniValue& v = find_value(o, t.first);
- if (!fAllowNull && v.isNull())
- throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
-
- if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
- std::string err = strprintf("Expected type %s for %s, got %s",
- uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
- throw JSONRPCError(RPC_TYPE_ERROR, err);
- }
- }
-
- if (fStrict)
- {
- for (const std::string& k : o.getKeys())
- {
- if (typesExpected.count(k) == 0)
- {
- std::string err = strprintf("Unexpected key %s", k);
- throw JSONRPCError(RPC_TYPE_ERROR, err);
- }
- }
- }
-}
-
-CAmount AmountFromValue(const UniValue& value)
-{
- if (!value.isNum() && !value.isStr())
- throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
- CAmount amount;
- if (!ParseFixedPoint(value.getValStr(), 8, &amount))
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
- if (!MoneyRange(amount))
- throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
- return amount;
-}
-
-uint256 ParseHashV(const UniValue& v, std::string strName)
-{
- std::string strHex(v.get_str());
- if (64 != strHex.length())
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
- if (!IsHex(strHex)) // Note: IsHex("") is false
- throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
- return uint256S(strHex);
-}
-uint256 ParseHashO(const UniValue& o, std::string strKey)
-{
- return ParseHashV(find_value(o, strKey), strKey);
-}
-std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
-{
- std::string strHex;
- if (v.isStr())
- strHex = v.get_str();
- if (!IsHex(strHex))
- throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
- return ParseHex(strHex);
-}
-std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
-{
- return ParseHexV(find_value(o, strKey), strKey);
-}
-
std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
{
std::string strRet;
std::string category;
- std::set<rpcfn_type> setDone;
+ std::set<intptr_t> setDone;
std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
for (const auto& entry : mapCommands)
- vCommands.push_back(make_pair(entry.second->category + entry.first, entry.second));
+ vCommands.push_back(make_pair(entry.second.front()->category + entry.first, entry.second.front()));
sort(vCommands.begin(), vCommands.end());
JSONRPCRequest jreq(helpreq);
@@ -193,9 +101,9 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest&
jreq.strMethod = strMethod;
try
{
- rpcfn_type pfn = pcmd->actor;
- if (setDone.insert(pfn).second)
- (*pfn)(jreq);
+ UniValue unused_result;
+ if (setDone.insert(pcmd->unique_id).second)
+ pcmd->actor(jreq, unused_result, true /* last_handler */);
}
catch (const std::exception& e)
{
@@ -296,8 +204,20 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
RPCHelpMan{"getrpcinfo",
"\nReturns details of the RPC server.\n",
{},
- RPCResults{},
- RPCExamples{""},
+ RPCResult{
+ "{\n"
+ " \"active_commands\" (array) All active commands\n"
+ " [\n"
+ " { (object) Information about an active command\n"
+ " \"method\" (string) The name of the RPC command \n"
+ " \"duration\" (numeric) The running time in microseconds\n"
+ " },...\n"
+ " ]\n"
+ "}\n"
+ },
+ RPCExamples{
+ HelpExampleCli("getrpcinfo", "")
+ + HelpExampleRpc("getrpcinfo", "")},
}.ToString()
);
}
@@ -337,32 +257,32 @@ CRPCTable::CRPCTable()
const CRPCCommand *pcmd;
pcmd = &vRPCCommands[vcidx];
- mapCommands[pcmd->name] = pcmd;
+ mapCommands[pcmd->name].push_back(pcmd);
}
}
-const CRPCCommand *CRPCTable::operator[](const std::string &name) const
-{
- std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
- if (it == mapCommands.end())
- return nullptr;
- return (*it).second;
-}
-
bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
{
if (IsRPCRunning())
return false;
- // don't allow overwriting for now
- std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
- if (it != mapCommands.end())
- return false;
-
- mapCommands[name] = pcmd;
+ mapCommands[name].push_back(pcmd);
return true;
}
+bool CRPCTable::removeCommand(const std::string& name, const CRPCCommand* pcmd)
+{
+ auto it = mapCommands.find(name);
+ if (it != mapCommands.end()) {
+ auto new_end = std::remove(it->second.begin(), it->second.end(), pcmd);
+ if (it->second.end() != new_end) {
+ it->second.erase(new_end, it->second.end());
+ return true;
+ }
+ }
+ return false;
+}
+
void StartRPC()
{
LogPrint(BCLog::RPC, "Starting RPC\n");
@@ -543,18 +463,28 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const
}
// Find method
- const CRPCCommand *pcmd = tableRPC[request.strMethod];
- if (!pcmd)
- throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
+ auto it = mapCommands.find(request.strMethod);
+ if (it != mapCommands.end()) {
+ UniValue result;
+ for (const auto& command : it->second) {
+ if (ExecuteCommand(*command, request, result, &command == &it->second.back())) {
+ return result;
+ }
+ }
+ }
+ throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
+}
+static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler)
+{
try
{
RPCCommandExecution execution(request.strMethod);
// Execute, convert arguments to array if necessary
if (request.params.isObject()) {
- return pcmd->actor(transformNamedArguments(request, pcmd->argNames));
+ return command.actor(transformNamedArguments(request, command.argNames), result, last_handler);
} else {
- return pcmd->actor(request);
+ return command.actor(request, result, last_handler);
}
}
catch (const std::exception& e)
@@ -570,17 +500,6 @@ std::vector<std::string> CRPCTable::listCommands() const
return commandList;
}
-std::string HelpExampleCli(const std::string& methodname, const std::string& args)
-{
- return "> bitcoin-cli " + methodname + " " + args + "\n";
-}
-
-std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
-{
- return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
- "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
-}
-
void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
{
if (!timerInterface)
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 2d62a76f3c..431ff0bb7c 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -27,15 +27,6 @@ namespace RPCServer
void OnStopped(std::function<void ()> slot);
}
-/** Wrapper for UniValue::VType, which includes typeAny:
- * Used to denote don't care type. */
-struct UniValueType {
- UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
- UniValueType() : typeAny(true) {}
- bool typeAny;
- UniValue::VType type;
-};
-
class JSONRPCRequest
{
public:
@@ -65,26 +56,6 @@ void SetRPCWarmupFinished();
/* returns the current warmup state. */
bool RPCIsInWarmup(std::string *outStatus);
-/**
- * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
- * the right number of arguments are passed, just that any passed are the correct type.
- */
-void RPCTypeCheck(const UniValue& params,
- const std::list<UniValueType>& typesExpected, bool fAllowNull=false);
-
-/**
- * Type-check one argument; throws JSONRPCError if wrong type given.
- */
-void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected);
-
-/*
- Check for expected keys/value types in an Object.
-*/
-void RPCTypeCheckObj(const UniValue& o,
- const std::map<std::string, UniValueType>& typesExpected,
- bool fAllowNull = false,
- bool fStrict = false);
-
/** Opaque base class for timers returned by NewTimerFunc.
* This provides no methods at the moment, but makes sure that delete
* cleans up the whole state.
@@ -131,10 +102,31 @@ typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest);
class CRPCCommand
{
public:
+ //! RPC method handler reading request and assigning result. Should return
+ //! true if request is fully handled, false if it should be passed on to
+ //! subsequent handlers.
+ using Actor = std::function<bool(const JSONRPCRequest& request, UniValue& result, bool last_handler)>;
+
+ //! Constructor taking Actor callback supporting multiple handlers.
+ CRPCCommand(std::string category, std::string name, Actor actor, std::vector<std::string> args, intptr_t unique_id)
+ : category(std::move(category)), name(std::move(name)), actor(std::move(actor)), argNames(std::move(args)),
+ unique_id(unique_id)
+ {
+ }
+
+ //! Simplified constructor taking plain rpcfn_type function pointer.
+ CRPCCommand(const char* category, const char* name, rpcfn_type fn, std::initializer_list<const char*> args)
+ : CRPCCommand(category, name,
+ [fn](const JSONRPCRequest& request, UniValue& result, bool) { result = fn(request); return true; },
+ {args.begin(), args.end()}, intptr_t(fn))
+ {
+ }
+
std::string category;
std::string name;
- rpcfn_type actor;
+ Actor actor;
std::vector<std::string> argNames;
+ intptr_t unique_id;
};
/**
@@ -143,10 +135,9 @@ public:
class CRPCTable
{
private:
- std::map<std::string, const CRPCCommand*> mapCommands;
+ std::map<std::string, std::vector<const CRPCCommand*>> mapCommands;
public:
CRPCTable();
- const CRPCCommand* operator[](const std::string& name) const;
std::string help(const std::string& name, const JSONRPCRequest& helpreq) const;
/**
@@ -169,9 +160,7 @@ public:
*
* Returns false if RPC server is already running (dump concurrency protection).
*
- * Commands cannot be overwritten (returns false).
- *
- * Commands with different method names but the same callback function will
+ * Commands with different method names but the same unique_id will
* be considered aliases, and only the first registered method name will
* show up in the help text command listing. Aliased commands do not have
* to have the same behavior. Server and client code can distinguish
@@ -179,25 +168,13 @@ public:
* register different names, types, and numbers of parameters.
*/
bool appendCommand(const std::string& name, const CRPCCommand* pcmd);
+ bool removeCommand(const std::string& name, const CRPCCommand* pcmd);
};
bool IsDeprecatedRPCEnabled(const std::string& method);
extern CRPCTable tableRPC;
-/**
- * Utilities: convert hex-encoded Values
- * (throws error if not hex).
- */
-extern uint256 ParseHashV(const UniValue& v, std::string strName);
-extern uint256 ParseHashO(const UniValue& o, std::string strKey);
-extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
-extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
-
-extern CAmount AmountFromValue(const UniValue& value);
-extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
-extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
-
void StartRPC();
void InterruptRPC();
void StopRPC();
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 10979b43b0..1a87c9f935 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -10,6 +10,110 @@
InitInterfaces* g_rpc_interfaces = nullptr;
+void RPCTypeCheck(const UniValue& params,
+ const std::list<UniValueType>& typesExpected,
+ bool fAllowNull)
+{
+ unsigned int i = 0;
+ for (const UniValueType& t : typesExpected) {
+ if (params.size() <= i)
+ break;
+
+ const UniValue& v = params[i];
+ if (!(fAllowNull && v.isNull())) {
+ RPCTypeCheckArgument(v, t);
+ }
+ i++;
+ }
+}
+
+void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
+{
+ if (!typeExpected.typeAny && value.type() != typeExpected.type) {
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
+ }
+}
+
+void RPCTypeCheckObj(const UniValue& o,
+ const std::map<std::string, UniValueType>& typesExpected,
+ bool fAllowNull,
+ bool fStrict)
+{
+ for (const auto& t : typesExpected) {
+ const UniValue& v = find_value(o, t.first);
+ if (!fAllowNull && v.isNull())
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
+
+ if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
+ std::string err = strprintf("Expected type %s for %s, got %s",
+ uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
+ throw JSONRPCError(RPC_TYPE_ERROR, err);
+ }
+ }
+
+ if (fStrict)
+ {
+ for (const std::string& k : o.getKeys())
+ {
+ if (typesExpected.count(k) == 0)
+ {
+ std::string err = strprintf("Unexpected key %s", k);
+ throw JSONRPCError(RPC_TYPE_ERROR, err);
+ }
+ }
+ }
+}
+
+CAmount AmountFromValue(const UniValue& value)
+{
+ if (!value.isNum() && !value.isStr())
+ throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
+ CAmount amount;
+ if (!ParseFixedPoint(value.getValStr(), 8, &amount))
+ throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
+ if (!MoneyRange(amount))
+ throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
+ return amount;
+}
+
+uint256 ParseHashV(const UniValue& v, std::string strName)
+{
+ std::string strHex(v.get_str());
+ if (64 != strHex.length())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
+ if (!IsHex(strHex)) // Note: IsHex("") is false
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
+ return uint256S(strHex);
+}
+uint256 ParseHashO(const UniValue& o, std::string strKey)
+{
+ return ParseHashV(find_value(o, strKey), strKey);
+}
+std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
+{
+ std::string strHex;
+ if (v.isStr())
+ strHex = v.get_str();
+ if (!IsHex(strHex))
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
+ return ParseHex(strHex);
+}
+std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
+{
+ return ParseHexV(find_value(o, strKey), strKey);
+}
+
+std::string HelpExampleCli(const std::string& methodname, const std::string& args)
+{
+ return "> bitcoin-cli " + methodname + " " + args + "\n";
+}
+
+std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
+{
+ return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
+ "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
+}
+
// Converts a hex string to a public key if possible
CPubKey HexToPubKey(const std::string& hex_in)
{
@@ -165,6 +269,10 @@ UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_s
}
}
+/**
+ * A pair of strings that can be aligned (through padding) with other Sections
+ * later on
+ */
struct Section {
Section(const std::string& left, const std::string& right)
: m_left{left}, m_right{right} {}
@@ -172,6 +280,10 @@ struct Section {
const std::string m_right;
};
+/**
+ * Keeps track of RPCArgs by transforming them into sections for the purpose
+ * of serializing everything to a single string
+ */
struct Sections {
std::vector<Section> m_sections;
size_t m_max_pad{0};
@@ -182,16 +294,26 @@ struct Sections {
m_sections.push_back(s);
}
+ /**
+ * Serializing RPCArgs depends on the outer type. Only arrays and
+ * dictionaries can be nested in json. The top-level outer type is "named
+ * arguments", a mix between a dictionary and arrays.
+ */
enum class OuterType {
ARR,
OBJ,
NAMED_ARG, // Only set on first recursion
};
+ /**
+ * Recursive helper to translate an RPCArg into sections
+ */
void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NAMED_ARG)
{
const auto indent = std::string(current_indent, ' ');
const auto indent_next = std::string(current_indent + 2, ' ');
+ const bool push_name{outer_type == OuterType::OBJ}; // Dictionary keys must have a name
+
switch (arg.m_type) {
case RPCArg::Type::STR_HEX:
case RPCArg::Type::STR:
@@ -201,10 +323,10 @@ struct Sections {
case RPCArg::Type::BOOL: {
if (outer_type == OuterType::NAMED_ARG) return; // Nothing more to do for non-recursive types on first recursion
auto left = indent;
- if (arg.m_type_str.size() != 0 && outer_type == OuterType::OBJ) {
+ if (arg.m_type_str.size() != 0 && push_name) {
left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0);
} else {
- left += outer_type == OuterType::OBJ ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false);
+ left += push_name ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false);
}
left += ",";
PushSection({left, arg.ToDescriptionString()});
@@ -213,7 +335,7 @@ struct Sections {
case RPCArg::Type::OBJ:
case RPCArg::Type::OBJ_USER_KEYS: {
const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString();
- PushSection({indent + "{", right});
+ PushSection({indent + (push_name ? "\"" + arg.m_name + "\": " : "") + "{", right});
for (const auto& arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::OBJ);
}
@@ -225,7 +347,7 @@ struct Sections {
}
case RPCArg::Type::ARR: {
auto left = indent;
- left += outer_type == OuterType::OBJ ? "\"" + arg.m_name + "\": " : "";
+ left += push_name ? "\"" + arg.m_name + "\": " : "";
left += "[";
const auto right = outer_type == OuterType::NAMED_ARG ? "" : arg.ToDescriptionString();
PushSection({left, right});
@@ -241,6 +363,9 @@ struct Sections {
}
}
+ /**
+ * Concatenate all sections with proper padding
+ */
std::string ToString() const
{
std::string ret;
diff --git a/src/rpc/util.h b/src/rpc/util.h
index e4cc1fde44..b5b5789253 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -26,6 +26,48 @@ struct InitInterfaces;
//! state to RPC method implementations.
extern InitInterfaces* g_rpc_interfaces;
+/** Wrapper for UniValue::VType, which includes typeAny:
+ * Used to denote don't care type. */
+struct UniValueType {
+ UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
+ UniValueType() : typeAny(true) {}
+ bool typeAny;
+ UniValue::VType type;
+};
+
+/**
+ * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
+ * the right number of arguments are passed, just that any passed are the correct type.
+ */
+void RPCTypeCheck(const UniValue& params,
+ const std::list<UniValueType>& typesExpected, bool fAllowNull=false);
+
+/**
+ * Type-check one argument; throws JSONRPCError if wrong type given.
+ */
+void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected);
+
+/*
+ Check for expected keys/value types in an Object.
+*/
+void RPCTypeCheckObj(const UniValue& o,
+ const std::map<std::string, UniValueType>& typesExpected,
+ bool fAllowNull = false,
+ bool fStrict = false);
+
+/**
+ * Utilities: convert hex-encoded Values
+ * (throws error if not hex).
+ */
+extern uint256 ParseHashV(const UniValue& v, std::string strName);
+extern uint256 ParseHashO(const UniValue& o, std::string strKey);
+extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
+extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
+
+extern CAmount AmountFromValue(const UniValue& value);
+extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
+extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
+
CPubKey HexToPubKey(const std::string& hex_in);
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 43448d7222..a333d4d4ac 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -436,7 +436,7 @@ public:
pubkeys.reserve(entries.size());
for (auto& entry : entries) {
pubkeys.push_back(entry.first);
- out.origins.emplace(entry.first.GetID(), std::move(entry.second));
+ out.origins.emplace(entry.first.GetID(), std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second)));
}
if (m_script_arg) {
for (const auto& subscript : subscripts) {
diff --git a/src/script/ismine.h b/src/script/ismine.h
index 601e70f709..55e28e225a 100644
--- a/src/script/ismine.h
+++ b/src/script/ismine.h
@@ -9,6 +9,7 @@
#include <script/standard.h>
#include <stdint.h>
+#include <bitset>
class CKeyStore;
class CScript;
@@ -16,10 +17,11 @@ class CScript;
/** IsMine() return codes */
enum isminetype
{
- ISMINE_NO = 0,
- ISMINE_WATCH_ONLY = 1,
- ISMINE_SPENDABLE = 2,
- ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE
+ ISMINE_NO = 0,
+ ISMINE_WATCH_ONLY = 1 << 0,
+ ISMINE_SPENDABLE = 1 << 1,
+ ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE,
+ ISMINE_ENUM_ELEMENTS,
};
/** used for bitflags of isminetype */
typedef uint8_t isminefilter;
@@ -27,4 +29,23 @@ typedef uint8_t isminefilter;
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
+/**
+ * Cachable amount subdivided into watchonly and spendable parts.
+ */
+struct CachableAmount
+{
+ // NO and ALL are never (supposed to be) cached
+ std::bitset<ISMINE_ENUM_ELEMENTS> m_cached;
+ CAmount m_value[ISMINE_ENUM_ELEMENTS];
+ inline void Reset()
+ {
+ m_cached.reset();
+ }
+ void Set(isminefilter filter, CAmount value)
+ {
+ m_cached.set(filter);
+ m_value[filter] = value;
+ }
+};
+
#endif // BITCOIN_SCRIPT_ISMINE_H
diff --git a/src/script/script.h b/src/script/script.h
index 1d8ddba2f2..11e8661a5b 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -581,13 +581,4 @@ struct CScriptWitness
std::string ToString() const;
};
-class CReserveScript
-{
-public:
- CScript reserveScript;
- virtual void KeepScript() {}
- CReserveScript() {}
- virtual ~CReserveScript() {}
-};
-
#endif // BITCOIN_SCRIPT_SCRIPT_H
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 320956d0c4..36dd68a3d8 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -483,7 +483,13 @@ bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& inf
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
-bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return LookupHelper(origins, keyid, info); }
+bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
+{
+ std::pair<CPubKey, KeyOriginInfo> out;
+ bool ret = LookupHelper(origins, keyid, out);
+ if (ret) info = std::move(out.second);
+ return ret;
+}
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
diff --git a/src/script/sign.h b/src/script/sign.h
index 491fb54c45..f746325b90 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -77,7 +77,7 @@ struct FlatSigningProvider final : public SigningProvider
{
std::map<CScriptID, CScript> scripts;
std::map<CKeyID, CPubKey> pubkeys;
- std::map<CKeyID, KeyOriginInfo> origins;
+ std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
std::map<CKeyID, CKey> keys;
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
diff --git a/src/script/standard.h b/src/script/standard.h
index fc20fb6a08..f16068c413 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -153,8 +153,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
* multisig scripts, this populates the addressRet vector with the pubkey IDs
* and nRequiredRet with the n required to spend. For other destinations,
* addressRet is populated with a single value and nRequiredRet is set to 1.
- * Returns true if successful. Currently does not extract address from
- * pay-to-witness scripts.
+ * Returns true if successful.
*
* Note: this function confuses destinations (a subset of CScripts that are
* encodable as an address) with key identifiers (of keys involved in a
diff --git a/src/serialize.h b/src/serialize.h
index 2d0cfbbbf0..b001ee1324 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -89,6 +89,11 @@ template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj)
obj = htole32(obj);
s.write((char*)&obj, 4);
}
+template<typename Stream> inline void ser_writedata32be(Stream &s, uint32_t obj)
+{
+ obj = htobe32(obj);
+ s.write((char*)&obj, 4);
+}
template<typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj)
{
obj = htole64(obj);
@@ -118,6 +123,12 @@ template<typename Stream> inline uint32_t ser_readdata32(Stream &s)
s.read((char*)&obj, 4);
return le32toh(obj);
}
+template<typename Stream> inline uint32_t ser_readdata32be(Stream &s)
+{
+ uint32_t obj;
+ s.read((char*)&obj, 4);
+ return be32toh(obj);
+}
template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
{
uint64_t obj;
diff --git a/src/sync.cpp b/src/sync.cpp
index 23ca866e53..e7c0a6f9bc 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -3,9 +3,11 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <sync.h>
+#include <tinyformat.h>
#include <logging.h>
#include <util/strencodings.h>
+#include <util/threadnames.h>
#include <stdio.h>
@@ -37,23 +39,30 @@ void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
//
struct CLockLocation {
- CLockLocation(const char* pszName, const char* pszFile, int nLine, bool fTryIn)
- {
- mutexName = pszName;
- sourceFile = pszFile;
- sourceLine = nLine;
- fTry = fTryIn;
- }
+ CLockLocation(
+ const char* pszName,
+ const char* pszFile,
+ int nLine,
+ bool fTryIn,
+ const std::string& thread_name)
+ : fTry(fTryIn),
+ mutexName(pszName),
+ sourceFile(pszFile),
+ m_thread_name(thread_name),
+ sourceLine(nLine) {}
std::string ToString() const
{
- return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : "");
+ return tfm::format(
+ "%s %s:%s%s (in thread %s)",
+ mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name);
}
private:
bool fTry;
std::string mutexName;
std::string sourceFile;
+ const std::string& m_thread_name;
int sourceLine;
};
@@ -125,7 +134,7 @@ static void push_lock(void* c, const CLockLocation& locklocation)
std::pair<void*, void*> p1 = std::make_pair(i.first, c);
if (lockdata.lockorders.count(p1))
continue;
- lockdata.lockorders[p1] = g_lockstack;
+ lockdata.lockorders.emplace(p1, g_lockstack);
std::pair<void*, void*> p2 = std::make_pair(c, i.first);
lockdata.invlockorders.insert(p2);
@@ -141,7 +150,7 @@ static void pop_lock()
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
{
- push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry));
+ push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));
}
void LeaveCritical()
diff --git a/src/test/README.md b/src/test/README.md
index f2a4cb1818..0017e3de26 100644
--- a/src/test/README.md
+++ b/src/test/README.md
@@ -42,7 +42,7 @@ unit tests as possible).
The build system is setup to compile an executable called `test_bitcoin`
that runs all of the unit tests. The main source file is called
-test_bitcoin.cpp. To add a new unit test file to our test suite you need
+setup_common.cpp. To add a new unit test file to our test suite you need
to add the file to `src/Makefile.test.include`. The pattern is to create
one test file for each class or source file for which you want to create
unit tests. The file naming convention is `<source_filename>_tests.cpp`
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 22347fbc57..eeb54b4cf0 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -1,8 +1,8 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
-#include <test/test_bitcoin.h>
+#include <test/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 9eded4f5b2..f255691704 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/system.h>
#include <support/allocators/secure.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <memory>
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index 1ff040b077..378fe285d5 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-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 <amount.h>
#include <policy/feerate.h>
-#include <test/test_bitcoin.h>
+#include <test/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 77b6008fd0..809c627d27 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -12,7 +12,7 @@
#include <arith_uint256.h>
#include <string>
#include <version.h>
-#include <test/test_bitcoin.h>
+#include <test/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 32af843bf6..b3bed2434c 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index f8f9b3c1a7..cb376cddb6 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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 <test/data/base58_encode_decode.json.h>
#include <base58.h>
-#include <test/test_bitcoin.h>
+#include <test/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 0abbb682a7..9ffffb0b7d 100644
--- a/src/test/base64_tests.cpp
+++ b/src/test/base64_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp
index 6ecc9ac705..0ba492c24e 100644
--- a/src/test/bech32_tests.cpp
+++ b/src/test/bech32_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bech32.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index c9951f4b7e..0c0423c0db 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2018 The Bitcoin Core developers
+// 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.
@@ -9,7 +9,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <string>
#include <vector>
diff --git a/src/test/blockchain_tests.cpp b/src/test/blockchain_tests.cpp
index b61152985f..13afcca375 100644
--- a/src/test/blockchain_tests.cpp
+++ b/src/test/blockchain_tests.cpp
@@ -3,7 +3,7 @@
#include <stdlib.h>
#include <rpc/blockchain.h>
-#include <test/test_bitcoin.h>
+#include <test/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 607af8a32a..f57e1a0ebd 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -8,7 +8,7 @@
#include <pow.h>
#include <random.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
@@ -386,6 +386,7 @@ BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
BOOST_CHECK(0);
} catch(std::ios_base::failure &) {
// deserialize should fail
+ BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
}
}
diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp
new file mode 100644
index 0000000000..db0b973463
--- /dev/null
+++ b/src/test/blockfilter_index_tests.cpp
@@ -0,0 +1,307 @@
+// 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.
+
+#include <blockfilter.h>
+#include <chainparams.h>
+#include <consensus/validation.h>
+#include <index/blockfilterindex.h>
+#include <miner.h>
+#include <pow.h>
+#include <test/setup_common.h>
+#include <script/standard.h>
+#include <validation.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(blockfilter_index_tests)
+
+static bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex* block_index,
+ BlockFilter& filter)
+{
+ CBlock block;
+ if (!ReadBlockFromDisk(block, block_index->GetBlockPos(), Params().GetConsensus())) {
+ return false;
+ }
+
+ CBlockUndo block_undo;
+ if (block_index->nHeight > 0 && !UndoReadFromDisk(block_undo, block_index)) {
+ return false;
+ }
+
+ filter = BlockFilter(filter_type, block, block_undo);
+ return true;
+}
+
+static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex* block_index,
+ uint256& last_header)
+{
+ BlockFilter expected_filter;
+ if (!ComputeFilter(filter_index.GetFilterType(), block_index, expected_filter)) {
+ BOOST_ERROR("ComputeFilter failed on block " << block_index->nHeight);
+ return false;
+ }
+
+ BlockFilter filter;
+ uint256 filter_header;
+ std::vector<BlockFilter> filters;
+ std::vector<uint256> filter_hashes;
+
+ BOOST_CHECK(filter_index.LookupFilter(block_index, filter));
+ BOOST_CHECK(filter_index.LookupFilterHeader(block_index, filter_header));
+ BOOST_CHECK(filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
+ BOOST_CHECK(filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
+ filter_hashes));
+
+ BOOST_CHECK_EQUAL(filters.size(), 1);
+ BOOST_CHECK_EQUAL(filter_hashes.size(), 1);
+
+ BOOST_CHECK_EQUAL(filter.GetHash(), expected_filter.GetHash());
+ BOOST_CHECK_EQUAL(filter_header, expected_filter.ComputeHeader(last_header));
+ BOOST_CHECK_EQUAL(filters[0].GetHash(), expected_filter.GetHash());
+ BOOST_CHECK_EQUAL(filter_hashes[0], expected_filter.GetHash());
+
+ filters.clear();
+ filter_hashes.clear();
+ last_header = filter_header;
+ return true;
+}
+
+static CBlock CreateBlock(const CBlockIndex* prev,
+ const std::vector<CMutableTransaction>& txns,
+ const CScript& scriptPubKey)
+{
+ const CChainParams& chainparams = Params();
+ std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
+ CBlock& block = pblocktemplate->block;
+ block.hashPrevBlock = prev->GetBlockHash();
+ block.nTime = prev->nTime + 1;
+
+ // Replace mempool-selected txns with just coinbase plus passed-in txns:
+ block.vtx.resize(1);
+ for (const CMutableTransaction& tx : txns) {
+ block.vtx.push_back(MakeTransactionRef(tx));
+ }
+ // IncrementExtraNonce creates a valid coinbase and merkleRoot
+ unsigned int extraNonce = 0;
+ IncrementExtraNonce(&block, prev, extraNonce);
+
+ while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
+
+ return block;
+}
+
+static bool BuildChain(const CBlockIndex* pindex, const CScript& coinbase_script_pub_key,
+ size_t length, std::vector<std::shared_ptr<CBlock>>& chain)
+{
+ std::vector<CMutableTransaction> no_txns;
+
+ chain.resize(length);
+ for (auto& block : chain) {
+ block = std::make_shared<CBlock>(CreateBlock(pindex, no_txns, coinbase_script_pub_key));
+ CBlockHeader header = block->GetBlockHeader();
+
+ CValidationState state;
+ if (!ProcessNewBlockHeaders({header}, state, Params(), &pindex, nullptr)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
+{
+ BlockFilterIndex filter_index(BlockFilterType::BASIC, 1 << 20, true);
+
+ uint256 last_header;
+
+ // Filter should not be found in the index before it is started.
+ {
+ LOCK(cs_main);
+
+ BlockFilter filter;
+ uint256 filter_header;
+ std::vector<BlockFilter> filters;
+ std::vector<uint256> filter_hashes;
+
+ for (const CBlockIndex* block_index = chainActive.Genesis();
+ block_index != nullptr;
+ block_index = chainActive.Next(block_index)) {
+ BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
+ BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
+ BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
+ BOOST_CHECK(!filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
+ filter_hashes));
+ }
+ }
+
+ // BlockUntilSyncedToCurrentChain should return false before index is started.
+ BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
+
+ filter_index.Start();
+
+ // Allow filter index to catch up with the block index.
+ constexpr int64_t timeout_ms = 10 * 1000;
+ int64_t time_start = GetTimeMillis();
+ while (!filter_index.BlockUntilSyncedToCurrentChain()) {
+ BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
+ MilliSleep(100);
+ }
+
+ // Check that filter index has all blocks that were in the chain before it started.
+ {
+ LOCK(cs_main);
+ const CBlockIndex* block_index;
+ for (block_index = chainActive.Genesis();
+ block_index != nullptr;
+ block_index = chainActive.Next(block_index)) {
+ CheckFilterLookups(filter_index, block_index, last_header);
+ }
+ }
+
+ // Create two forks.
+ const CBlockIndex* tip;
+ {
+ LOCK(cs_main);
+ tip = chainActive.Tip();
+ }
+ CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
+ std::vector<std::shared_ptr<CBlock>> chainA, chainB;
+ BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key, 10, chainA));
+ BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key, 10, chainB));
+
+ // Check that new blocks on chain A get indexed.
+ uint256 chainA_last_header = last_header;
+ for (size_t i = 0; i < 2; i++) {
+ const auto& block = chainA[i];
+ BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
+
+ const CBlockIndex* block_index;
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(block->GetHash());
+ }
+
+ BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ CheckFilterLookups(filter_index, block_index, chainA_last_header);
+ }
+
+ // Reorg to chain B.
+ uint256 chainB_last_header = last_header;
+ for (size_t i = 0; i < 3; i++) {
+ const auto& block = chainB[i];
+ BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
+
+ const CBlockIndex* block_index;
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(block->GetHash());
+ }
+
+ BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ CheckFilterLookups(filter_index, block_index, chainB_last_header);
+ }
+
+ // Check that filters for stale blocks on A can be retrieved.
+ chainA_last_header = last_header;
+ for (size_t i = 0; i < 2; i++) {
+ const auto& block = chainA[i];
+ const CBlockIndex* block_index;
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(block->GetHash());
+ }
+
+ BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ CheckFilterLookups(filter_index, block_index, chainA_last_header);
+ }
+
+ // Reorg back to chain A.
+ for (size_t i = 2; i < 4; i++) {
+ const auto& block = chainA[i];
+ BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
+ }
+
+ // Check that chain A and B blocks can be retrieved.
+ chainA_last_header = last_header;
+ chainB_last_header = last_header;
+ for (size_t i = 0; i < 3; i++) {
+ const CBlockIndex* block_index;
+
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(chainA[i]->GetHash());
+ }
+ BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ CheckFilterLookups(filter_index, block_index, chainA_last_header);
+
+ {
+ LOCK(cs_main);
+ block_index = LookupBlockIndex(chainB[i]->GetHash());
+ }
+ BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ CheckFilterLookups(filter_index, block_index, chainB_last_header);
+ }
+
+ // Test lookups for a range of filters/hashes.
+ std::vector<BlockFilter> filters;
+ std::vector<uint256> filter_hashes;
+
+ {
+ LOCK(cs_main);
+ tip = chainActive.Tip();
+ }
+ BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
+ BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
+
+ BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1);
+ BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1);
+
+ filters.clear();
+ filter_hashes.clear();
+
+ filter_index.Interrupt();
+ filter_index.Stop();
+}
+
+BOOST_FIXTURE_TEST_CASE(blockfilter_index_init_destroy, BasicTestingSetup)
+{
+ SetDataDir("tempdir");
+
+ BlockFilterIndex* filter_index;
+
+ filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
+ BOOST_CHECK(filter_index == nullptr);
+
+ BOOST_CHECK(InitBlockFilterIndex(BlockFilterType::BASIC, 1 << 20, true, false));
+
+ filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
+ BOOST_CHECK(filter_index != nullptr);
+ BOOST_CHECK(filter_index->GetFilterType() == BlockFilterType::BASIC);
+
+ // Initialize returns false if index already exists.
+ BOOST_CHECK(!InitBlockFilterIndex(BlockFilterType::BASIC, 1 << 20, true, false));
+
+ int iter_count = 0;
+ ForEachBlockFilterIndex([&iter_count](BlockFilterIndex& _index) { iter_count++; });
+ BOOST_CHECK_EQUAL(iter_count, 1);
+
+ BOOST_CHECK(DestroyBlockFilterIndex(BlockFilterType::BASIC));
+
+ // Destroy returns false because index was already destroyed.
+ BOOST_CHECK(!DestroyBlockFilterIndex(BlockFilterType::BASIC));
+
+ filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
+ BOOST_CHECK(filter_index == nullptr);
+
+ // Reinitialize index.
+ BOOST_CHECK(InitBlockFilterIndex(BlockFilterType::BASIC, 1 << 20, true, false));
+
+ DestroyAllBlockFilterIndexes();
+
+ filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
+ BOOST_CHECK(filter_index == nullptr);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp
index 625362f446..df0a041e0e 100644
--- a/src/test/blockfilter_tests.cpp
+++ b/src/test/blockfilter_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-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/data/blockfilters.json.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <blockfilter.h>
#include <core_io.h>
@@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(gcsfilter_default_constructor)
BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
{
- CScript included_scripts[5], excluded_scripts[3];
+ CScript included_scripts[5], excluded_scripts[4];
// First two are outputs on a single transaction.
included_scripts[0] << std::vector<unsigned char>(0, 65) << OP_CHECKSIG;
@@ -73,14 +73,19 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
// This script is not related to the block at all.
excluded_scripts[1] << std::vector<unsigned char>(5, 33) << OP_CHECKSIG;
+ // OP_RETURN is non-standard since it's not followed by a data push, but is still excluded from
+ // filter.
+ excluded_scripts[2] << OP_RETURN << OP_4 << OP_ADD << OP_8 << OP_EQUAL;
+
CMutableTransaction tx_1;
tx_1.vout.emplace_back(100, included_scripts[0]);
tx_1.vout.emplace_back(200, included_scripts[1]);
+ tx_1.vout.emplace_back(0, excluded_scripts[0]);
CMutableTransaction tx_2;
tx_2.vout.emplace_back(300, included_scripts[2]);
- tx_2.vout.emplace_back(0, excluded_scripts[0]);
- tx_2.vout.emplace_back(400, excluded_scripts[2]); // Script is empty
+ tx_2.vout.emplace_back(0, excluded_scripts[2]);
+ tx_2.vout.emplace_back(400, excluded_scripts[3]); // Script is empty
CBlock block;
block.vtx.push_back(MakeTransactionRef(tx_1));
@@ -90,7 +95,7 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
block_undo.vtxundo.emplace_back();
block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(500, included_scripts[3]), 1000, true);
block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(600, included_scripts[4]), 10000, false);
- block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(700, excluded_scripts[2]), 100000, false);
+ block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(700, excluded_scripts[3]), 100000, false);
BlockFilter block_filter(BlockFilterType::BASIC, block, block_undo);
const GCSFilter& filter = block_filter.GetFilter();
@@ -112,6 +117,12 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
BOOST_CHECK_EQUAL(block_filter.GetFilterType(), block_filter2.GetFilterType());
BOOST_CHECK_EQUAL(block_filter.GetBlockHash(), block_filter2.GetBlockHash());
BOOST_CHECK(block_filter.GetEncodedFilter() == block_filter2.GetEncodedFilter());
+
+ BlockFilter default_ctor_block_filter_1;
+ BlockFilter default_ctor_block_filter_2;
+ BOOST_CHECK_EQUAL(default_ctor_block_filter_1.GetFilterType(), default_ctor_block_filter_2.GetFilterType());
+ BOOST_CHECK_EQUAL(default_ctor_block_filter_1.GetBlockHash(), default_ctor_block_filter_2.GetBlockHash());
+ BOOST_CHECK(default_ctor_block_filter_1.GetEncodedFilter() == default_ctor_block_filter_2.GetEncodedFilter());
}
BOOST_AUTO_TEST_CASE(blockfilters_json_test)
@@ -168,4 +179,16 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test)
}
}
+BOOST_AUTO_TEST_CASE(blockfilter_type_names)
+{
+ BOOST_CHECK_EQUAL(BlockFilterTypeName(BlockFilterType::BASIC), "basic");
+ BOOST_CHECK_EQUAL(BlockFilterTypeName(static_cast<BlockFilterType>(255)), "");
+
+ BlockFilterType filter_type;
+ BOOST_CHECK(BlockFilterTypeByName("basic", filter_type));
+ BOOST_CHECK_EQUAL(filter_type, BlockFilterType::BASIC);
+
+ BOOST_CHECK(!BlockFilterTypeByName("unknown", filter_type));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index f58dd20efc..4421494007 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -15,7 +15,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp
index 8572926193..8fd4e5d5d6 100644
--- a/src/test/bswap_tests.cpp
+++ b/src/test/bswap_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-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 <compat/byteswap.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 3469c6dfba..408a7fbda4 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -6,7 +6,7 @@
#include <util/time.h>
#include <validation.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <checkqueue.h>
#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>
@@ -167,7 +167,6 @@ static void Correct_Queue_range(std::vector<size_t> range)
BOOST_REQUIRE(control.Wait());
if (FakeCheckCheckCompletion::n_calls != i) {
BOOST_REQUIRE_EQUAL(FakeCheckCheckCompletion::n_calls, i);
- BOOST_TEST_MESSAGE("Failure on trial " << i << " expected, got " << FakeCheckCheckCompletion::n_calls);
}
}
tg.interrupt_all();
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index f6b97a6868..232c077c68 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-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.
@@ -6,7 +6,7 @@
#include <coins.h>
#include <consensus/validation.h>
#include <script/standard.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <uint256.h>
#include <undo.h>
#include <util/strencodings.h>
diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp
index e686c05165..6cef8cd8a8 100644
--- a/src/test/compress_tests.cpp
+++ b/src/test/compress_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <compressor.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <stdint.h>
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 86cb00a78f..0d05b6514f 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -1,9 +1,10 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-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 <crypto/aes.h>
#include <crypto/chacha20.h>
+#include <crypto/poly1305.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
@@ -12,13 +13,11 @@
#include <crypto/hmac_sha512.h>
#include <random.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
#include <boost/test/unit_test.hpp>
-#include <openssl/aes.h>
-#include <openssl/evp.h>
BOOST_FIXTURE_TEST_SUITE(crypto_tests, BasicTestingSetup)
@@ -66,26 +65,6 @@ static void TestHMACSHA512(const std::string &hexkey, const std::string &hexin,
TestVector(CHMAC_SHA512(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout));
}
-static void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
-{
- std::vector<unsigned char> key = ParseHex(hexkey);
- std::vector<unsigned char> in = ParseHex(hexin);
- std::vector<unsigned char> correctout = ParseHex(hexout);
- std::vector<unsigned char> buf, buf2;
-
- assert(key.size() == 16);
- assert(in.size() == 16);
- assert(correctout.size() == 16);
- AES128Encrypt enc(key.data());
- buf.resize(correctout.size());
- buf2.resize(correctout.size());
- enc.Encrypt(buf.data(), in.data());
- BOOST_CHECK_EQUAL(HexStr(buf), HexStr(correctout));
- AES128Decrypt dec(key.data());
- dec.Decrypt(buf2.data(), buf.data());
- BOOST_CHECK_EQUAL(HexStr(buf2), HexStr(in));
-}
-
static void TestAES256(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
@@ -105,47 +84,6 @@ static void TestAES256(const std::string &hexkey, const std::string &hexin, cons
BOOST_CHECK(buf == in);
}
-static void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)
-{
- std::vector<unsigned char> key = ParseHex(hexkey);
- std::vector<unsigned char> iv = ParseHex(hexiv);
- std::vector<unsigned char> in = ParseHex(hexin);
- std::vector<unsigned char> correctout = ParseHex(hexout);
- std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE);
-
- // Encrypt the plaintext and verify that it equals the cipher
- AES128CBCEncrypt enc(key.data(), iv.data(), pad);
- int size = enc.Encrypt(in.data(), in.size(), realout.data());
- realout.resize(size);
- BOOST_CHECK(realout.size() == correctout.size());
- BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout);
-
- // Decrypt the cipher and verify that it equals the plaintext
- std::vector<unsigned char> decrypted(correctout.size());
- AES128CBCDecrypt dec(key.data(), iv.data(), pad);
- size = dec.Decrypt(correctout.data(), correctout.size(), decrypted.data());
- decrypted.resize(size);
- BOOST_CHECK(decrypted.size() == in.size());
- BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin);
-
- // Encrypt and re-decrypt substrings of the plaintext and verify that they equal each-other
- for(std::vector<unsigned char>::iterator i(in.begin()); i != in.end(); ++i)
- {
- std::vector<unsigned char> sub(i, in.end());
- std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE);
- int _size = enc.Encrypt(sub.data(), sub.size(), subout.data());
- if (_size != 0)
- {
- subout.resize(_size);
- std::vector<unsigned char> subdecrypted(subout.size());
- _size = dec.Decrypt(subout.data(), subout.size(), subdecrypted.data());
- subdecrypted.resize(_size);
- BOOST_CHECK(decrypted.size() == in.size());
- BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub));
- }
- }
-}
-
static void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)
{
std::vector<unsigned char> key = ParseHex(hexkey);
@@ -200,6 +138,17 @@ static void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t see
BOOST_CHECK(out == outres);
}
+static void TestPoly1305(const std::string &hexmessage, const std::string &hexkey, const std::string& hextag)
+{
+ std::vector<unsigned char> key = ParseHex(hexkey);
+ std::vector<unsigned char> m = ParseHex(hexmessage);
+ std::vector<unsigned char> tag = ParseHex(hextag);
+ std::vector<unsigned char> tagres;
+ tagres.resize(POLY1305_TAGLEN);
+ poly1305_auth(tagres.data(), m.data(), m.size(), key.data());
+ BOOST_CHECK(tag == tagres);
+}
+
static std::string LongTestString() {
std::string ret;
for (int i=0; i<200000; i++) {
@@ -428,14 +377,9 @@ BOOST_AUTO_TEST_CASE(hmac_sha512_testvectors) {
BOOST_AUTO_TEST_CASE(aes_testvectors) {
// AES test vectors from FIPS 197.
- TestAES128("000102030405060708090a0b0c0d0e0f", "00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a");
TestAES256("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00112233445566778899aabbccddeeff", "8ea2b7ca516745bfeafc49904b496089");
// AES-ECB test vectors from NIST sp800-38a.
- TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172a", "3ad77bb40d7a3660a89ecaf32466ef97");
- TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "ae2d8a571e03ac9c9eb76fac45af8e51", "f5d3d58503b9699de785895a96fdbaaf");
- TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "30c81c46a35ce411e5fbc1191a0a52ef", "43b1cd7f598ece23881b00e3ed030688");
- TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "f69f2445df4f9b17ad2b417be66c3710", "7b0c785e27e8ad3f8223207104725dd4");
TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "6bc1bee22e409f96e93d7e117393172a", "f3eed1bdb5d2a03c064b5a7e3db181f8");
TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "ae2d8a571e03ac9c9eb76fac45af8e51", "591ccb10d410ed26dc5ba74a31362870");
TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "30c81c46a35ce411e5fbc1191a0a52ef", "b6ed21b99ca6f4f9f153e7b1beafed1d");
@@ -443,27 +387,6 @@ BOOST_AUTO_TEST_CASE(aes_testvectors) {
}
BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) {
-
- // NIST AES CBC 128-bit encryption test-vectors
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", false, \
- "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", false, \
- "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b2");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", false, \
- "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", false, \
- "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a7");
-
- // The same vectors with padding enabled
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", true, \
- "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d8964e0b149c10b7b682e6e39aaeb731c");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", true, \
- "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b255e21d7100b988ffec32feeafaf23538");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", true, \
- "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516f6eccda327bf8e5ec43718b0039adceb");
- TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", true, \
- "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a78cb82807230e1321d3fae00d18cc2012");
-
// NIST AES CBC 256-bit encryption test-vectors
TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \
"000102030405060708090A0B0C0D0E0F", false, "6bc1bee22e409f96e93d7e117393172a", \
@@ -524,6 +447,76 @@ BOOST_AUTO_TEST_CASE(chacha20_testvector)
"fab78c9");
}
+BOOST_AUTO_TEST_CASE(poly1305_testvector)
+{
+ // RFC 7539, section 2.5.2.
+ TestPoly1305("43727970746f6772617068696320466f72756d2052657365617263682047726f7570",
+ "85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b",
+ "a8061dc1305136c6c22b8baf0c0127a9");
+
+ // RFC 7539, section A.3.
+ TestPoly1305("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000");
+
+ TestPoly1305("416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e747269627"
+ "5746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465"
+ "726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686"
+ "520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e2022494554"
+ "4620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c20737461746"
+ "56d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c65"
+ "6374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207"
+ "768696368206172652061646472657373656420746f",
+ "0000000000000000000000000000000036e5f6b5c5e06070f0efca96227a863e",
+ "36e5f6b5c5e06070f0efca96227a863e");
+
+ TestPoly1305("416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e747269627"
+ "5746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465"
+ "726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686"
+ "520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e2022494554"
+ "4620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c20737461746"
+ "56d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c65"
+ "6374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207"
+ "768696368206172652061646472657373656420746f",
+ "36e5f6b5c5e06070f0efca96227a863e00000000000000000000000000000000",
+ "f3477e7cd95417af89a6b8794c310cf0");
+
+ TestPoly1305("2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e6420676"
+ "96d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e"
+ "6420746865206d6f6d65207261746873206f757467726162652e",
+ "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0",
+ "4541669a7eaaee61e708dc7cbcc5eb62");
+
+ TestPoly1305("ffffffffffffffffffffffffffffffff",
+ "0200000000000000000000000000000000000000000000000000000000000000",
+ "03000000000000000000000000000000");
+
+ TestPoly1305("02000000000000000000000000000000",
+ "02000000000000000000000000000000ffffffffffffffffffffffffffffffff",
+ "03000000000000000000000000000000");
+
+ TestPoly1305("fffffffffffffffffffffffffffffffff0ffffffffffffffffffffffffffffff11000000000000000000000000000000",
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "05000000000000000000000000000000");
+
+ TestPoly1305("fffffffffffffffffffffffffffffffffbfefefefefefefefefefefefefefefe01010101010101010101010101010101",
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000");
+
+ TestPoly1305("fdffffffffffffffffffffffffffffff",
+ "0200000000000000000000000000000000000000000000000000000000000000",
+ "faffffffffffffffffffffffffffffff");
+
+ TestPoly1305("e33594d7505e43b900000000000000003394d7505e4379cd01000000000000000000000000000000000000000000000001000000000000000000000000000000",
+ "0100000000000000040000000000000000000000000000000000000000000000",
+ "14000000000000005500000000000000");
+
+ TestPoly1305("e33594d7505e43b900000000000000003394d7505e4379cd010000000000000000000000000000000000000000000000",
+ "0100000000000000040000000000000000000000000000000000000000000000",
+ "13000000000000000000000000000000");
+}
+
BOOST_AUTO_TEST_CASE(countbits_tests)
{
FastRandomContext ctx;
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
index d8286520ec..d38ede691a 100644
--- a/src/test/cuckoocache_tests.cpp
+++ b/src/test/cuckoocache_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/test/unit_test.hpp>
#include <cuckoocache.h>
#include <script/sigcache.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <random.h>
#include <thread>
diff --git a/src/test/data/blockfilters.json b/src/test/data/blockfilters.json
index 134b788eed..8945296a07 100644
--- a/src/test/data/blockfilters.json
+++ b/src/test/data/blockfilters.json
@@ -3,9 +3,11 @@
[0,"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943","0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000",[],"0000000000000000000000000000000000000000000000000000000000000000","019dfca8","21584579b7eb08997773e5aeff3a7f932700042d0ed2a6129012b7d7ae81b750","Genesis block"],
[2,"000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820","0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d235340101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0432e7494d010e062f503253482fffffffff0100f2052a010000002321038a7f6ef1c8ca0c588aa53fa860128077c9e6c11e6830f4d7ee4e763a56b7718fac00000000",[],"d7bdac13a59d745b1add0d2ce852f1a0442e8945fc1bf3848d3cbffd88c24fe1","0174a170","186afd11ef2b5e7e3504f2e8cbf8df28a1fd251fe53d60dff8b1467d1b386cf0",""],
[3,"000000008b896e272758da5297bcd98fdc6d97c9b765ecec401e286dc1fdbe10","0100000020782a005255b657696ea057d5b98f34defcf75196f64f6eeac8026c0000000041ba5afc532aae03151b8aa87b65e1594f97504a768e010c98c0add79216247186e7494dffff001d058dc2b60101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0486e7494d0151062f503253482fffffffff0100f2052a01000000232103f6d9ff4c12959445ca5549c811683bf9c88e637b222dd2e0311154c4c85cf423ac00000000",[],"186afd11ef2b5e7e3504f2e8cbf8df28a1fd251fe53d60dff8b1467d1b386cf0","016cf7a0","8d63aadf5ab7257cb6d2316a57b16f517bff1c6388f124ec4c04af1212729d2a",""],
+[15007,"0000000038c44c703bae0f98cdd6bf30922326340a5996cc692aaae8bacf47ad","0100000002394092aa378fe35d7e9ac79c869b975c4de4374cd75eb5484b0e1e00000000eb9b8670abd44ad6c55cee18e3020fb0c6519e7004b01a16e9164867531b67afc33bc94fffff001d123f10050101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e04c33bc94f0115062f503253482fffffffff0100f2052a01000000232103f268e9ae07e0f8cb2f6e901d87c510d650b97230c0365b021df8f467363cafb1ac00000000",[],"18b5c2b0146d2d09d24fb00ff5b52bd0742f36c9e65527abdb9de30c027a4748","013c3710","07384b01311867949e0c046607c66b7a766d338474bb67f66c8ae9dbd454b20e","Tx has non-standard OP_RETURN output followed by opcodes"],
[49291,"0000000018b07dca1b28b4b5a119f6d6e71698ce1ed96f143f54179ce177a19c","02000000abfaf47274223ca2fea22797e44498240e482cb4c2f2baea088962f800000000604b5b52c32305b15d7542071d8b04e750a547500005d4010727694b6e72a776e55d0d51ffff001d211806480201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d038bc0000102062f503253482fffffffff01a078072a01000000232102971dd6034ed0cf52450b608d196c07d6345184fcb14deb277a6b82d526a6163dac0000000001000000081cefd96060ecb1c4fbe675ad8a4f8bdc61d634c52b3a1c4116dee23749fe80ff000000009300493046022100866859c21f306538152e83f115bcfbf59ab4bb34887a88c03483a5dff9895f96022100a6dfd83caa609bf0516debc2bf65c3df91813a4842650a1858b3f61cfa8af249014730440220296d4b818bb037d0f83f9f7111665f49532dfdcbec1e6b784526e9ac4046eaa602204acf3a5cb2695e8404d80bf49ab04828bcbe6fc31d25a2844ced7a8d24afbdff01ffffffff1cefd96060ecb1c4fbe675ad8a4f8bdc61d634c52b3a1c4116dee23749fe80ff020000009400483045022100e87899175991aa008176cb553c6f2badbb5b741f328c9845fcab89f8b18cae2302200acce689896dc82933015e7230e5230d5cff8a1ffe82d334d60162ac2c5b0c9601493046022100994ad29d1e7b03e41731a4316e5f4992f0d9b6e2efc40a1ccd2c949b461175c502210099b69fdc2db00fbba214f16e286f6a49e2d8a0d5ffc6409d87796add475478d601ffffffff1e4a6d2d280ea06680d6cf8788ac90344a9c67cca9b06005bbd6d3f6945c8272010000009500493046022100a27400ba52fd842ce07398a1de102f710a10c5599545e6c95798934352c2e4df022100f6383b0b14c9f64b6718139f55b6b9494374755b86bae7d63f5d3e583b57255a01493046022100fdf543292f34e1eeb1703b264965339ec4a450ec47585009c606b3edbc5b617b022100a5fbb1c8de8aaaa582988cdb23622838e38de90bebcaab3928d949aa502a65d401ffffffff1e4a6d2d280ea06680d6cf8788ac90344a9c67cca9b06005bbd6d3f6945c8272020000009400493046022100ac626ac3051f875145b4fe4cfe089ea895aac73f65ab837b1ac30f5d875874fa022100bc03e79fa4b7eb707fb735b95ff6613ca33adeaf3a0607cdcead4cfd3b51729801483045022100b720b04a5c5e2f61b7df0fcf334ab6fea167b7aaede5695d3f7c6973496adbf1022043328c4cc1cdc3e5db7bb895ccc37133e960b2fd3ece98350f774596badb387201ffffffff23a8733e349c97d6cd90f520fdd084ba15ce0a395aad03cd51370602bb9e5db3010000004a00483045022100e8556b72c5e9c0da7371913a45861a61c5df434dfd962de7b23848e1a28c86ca02205d41ceda00136267281be0974be132ac4cda1459fe2090ce455619d8b91045e901ffffffff6856d609b881e875a5ee141c235e2a82f6b039f2b9babe82333677a5570285a6000000006a473044022040a1c631554b8b210fbdf2a73f191b2851afb51d5171fb53502a3a040a38d2c0022040d11cf6e7b41fe1b66c3d08f6ada1aee07a047cb77f242b8ecc63812c832c9a012102bcfad931b502761e452962a5976c79158a0f6d307ad31b739611dac6a297c256ffffffff6856d609b881e875a5ee141c235e2a82f6b039f2b9babe82333677a5570285a601000000930048304502205b109df098f7e932fbf71a45869c3f80323974a826ee2770789eae178a21bfc8022100c0e75615e53ee4b6e32b9bb5faa36ac539e9c05fa2ae6b6de5d09c08455c8b9601483045022009fb7d27375c47bea23b24818634df6a54ecf72d52e0c1268fb2a2c84f1885de022100e0ed4f15d62e7f537da0d0f1863498f9c7c0c0a4e00e4679588c8d1a9eb20bb801ffffffffa563c3722b7b39481836d5edfc1461f97335d5d1e9a23ade13680d0e2c1c371f030000006c493046022100ecc38ae2b1565643dc3c0dad5e961a5f0ea09cab28d024f92fa05c922924157e022100ebc166edf6fbe4004c72bfe8cf40130263f98ddff728c8e67b113dbd621906a601210211a4ed241174708c07206601b44a4c1c29e5ad8b1f731c50ca7e1d4b2a06dc1fffffffff02d0223a00000000001976a91445db0b779c0b9fa207f12a8218c94fc77aff504588ac80f0fa02000000000000000000",["5221033423007d8f263819a2e42becaaf5b06f34cb09919e06304349d950668209eaed21021d69e2b68c3960903b702af7829fadcd80bd89b158150c85c4a75b2c8cb9c39452ae","52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821021d69e2b68c3960903b702af7829fadcd80bd89b158150c85c4a75b2c8cb9c39452ae","522102a7ae1e0971fc1689bd66d2a7296da3a1662fd21a53c9e38979e0f090a375c12d21022adb62335f41eb4e27056ac37d462cda5ad783fa8e0e526ed79c752475db285d52ae","52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821022adb62335f41eb4e27056ac37d462cda5ad783fa8e0e526ed79c752475db285d52ae","512103b9d1d0e2b4355ec3cdef7c11a5c0beff9e8b8d8372ab4b4e0aaf30e80173001951ae","76a9149144761ebaccd5b4bbdc2a35453585b5637b2f8588ac","522103f1848b40621c5d48471d9784c8174ca060555891ace6d2b03c58eece946b1a9121020ee5d32b54d429c152fdc7b1db84f2074b0564d35400d89d11870f9273ec140c52ae","76a914f4fa1cc7de742d135ea82c17adf0bb9cf5f4fb8388ac"],"ed47705334f4643892ca46396eb3f4196a5e30880589e4009ef38eae895d4a13","0afbc2920af1b027f31f87b592276eb4c32094bb4d3697021b4c6380","b6d98692cec5145f67585f3434ec3c2b3030182e1cb3ec58b855c5c164dfaaa3","Tx pays to empty output script"],
-[180480,"00000000fd3ceb2404ff07a785c7fdcc76619edc8ed61bd25134eaa22084366a","020000006058aa080a655aa991a444bd7d1f2defd9a3bbe68aabb69030cf3b4e00000000d2e826bfd7ef0beaa891a7eedbc92cd6a544a6cb61c7bdaa436762eb2123ef9790f5f552ffff001d0002c90f0501000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0300c102024608062f503253482fffffffff01c0c6072a01000000232102e769e60137a4df6b0df8ebd387cca44c4c57ae74cc0114a8e8317c8f3bfd85e9ac00000000010000000381a0802911a01ffb025c4dea0bc77963e8c1bb46313b71164c53f72f37fe5248010000000151ffffffffc904b267833d215e2128bd9575242232ac2bc311550c7fc1f0ef6f264b40d14c010000000151ffffffffdf0915666649dba81886519c531649b7b02180b4af67d6885e871299e9d5f775000000000151ffffffff0180817dcb00000000232103bb52138972c48a132fc1f637858c5189607dd0f7fe40c4f20f6ad65f2d389ba4ac0000000001000000018da38b434fba82d66052af74fc5e4e94301b114d9bc03f819dc876398404c8b4010000006c493046022100fe738b7580dc5fb5168e51fc61b5aed211125eb71068031009a22d9bbad752c5022100be5086baa384d40bcab0fa586e4f728397388d86e18b66cc417dc4f7fa4f9878012103f233299455134caa2687bdf15cb0becdfb03bd0ff2ff38e65ec6b7834295c34fffffffff022ebc1400000000001976a9147779b7fba1c1e06b717069b80ca170e8b04458a488ac9879c40f000000001976a9142a0307cd925dbb66b534c4db33003dd18c57015788ac0000000001000000026139a62e3422a602de36c873a225c1d3ca5aeee598539ceecb9f0dc8d1ad0f83010000006b483045022100ad9f32b4a0a2ddc19b5a74eba78123e57616f1b3cfd72ce68c03ea35a3dda1f002200dbd22aa6da17213df5e70dfc3b2611d40f70c98ed9626aa5e2cde9d97461f0a012103ddb295d2f1e8319187738fb4b230fdd9aa29d0e01647f69f6d770b9ab24eea90ffffffff983c82c87cf020040d671956525014d5c2b28c6d948c85e1a522362c0059eeae010000006b4830450221009ca544274c786d30a5d5d25e17759201ea16d3aedddf0b9e9721246f7ef6b32e02202cfa5564b6e87dfd9fd98957820e4d4e6238baeb0f65fe305d91506bb13f5f4f012103c99113deac0d5d044e3ac0346abc02501542af8c8d3759f1382c72ff84e704f7ffffffff02c0c62d00000000001976a914ae19d27efe12f5a886dc79af37ad6805db6f922d88ac70ce2000000000001976a9143b8d051d37a07ea1042067e93efe63dbf73920b988ac000000000100000002be566e8cd9933f0c75c4a82c027f7d0c544d5c101d0607ef6ae5d07b98e7f1dc000000006b483045022036a8cdfd5ea7ebc06c2bfb6e4f942bbf9a1caeded41680d11a3a9f5d8284abad022100cacb92a5be3f39e8bc14db1710910ef7b395fa1e18f45d41c28d914fcdde33be012102bf59abf110b5131fae0a3ce1ec379329b4c896a6ae5d443edb68529cc2bc7816ffffffff96cf67645b76ceb23fe922874847456a15feee1655082ff32d25a6bf2c0dfc90000000006a47304402203471ca2001784a5ac0abab583581f2613523da47ec5f53df833c117b5abd81500220618a2847723d57324f2984678db556dbca1a72230fc7e39df04c2239942ba942012102925c9794fd7bb9f8b29e207d5fc491b1150135a21f505041858889fa4edf436fffffffff026c840f00000000001976a914797fb8777d7991d8284d88bfd421ce520f0f843188ac00ca9a3b000000001976a9146d10f3f592699265d10b106eda37c3ce793f7a8588ac00000000",["","","","76a9142903b138c24be9e070b3e73ec495d77a204615e788ac","76a91433a1941fd9a37b9821d376f5a51bd4b52fa50e2888ac","76a914e4374e8155d0865742ca12b8d4d14d41b57d682f88ac","76a914001fa7459a6cfc64bdc178ba7e7a21603bb2568f88ac","76a914f6039952bc2b307aeec5371bfb96b66078ec17f688ac"],"b109139671dbedc2b6fcd499a5480a7461ae458af8ff9411d819aa64ba6995d1","0db414c859a07e8205876354a210a75042d0463404913d61a8e068e58a3ae2aa080026","a0af77e0a7ed20ea78d2def3200cc24f08217dcd51755c7c7feb0e2ba8316c2d","Tx spends from empty output script"],
-[926485,"000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313","0000002060bbab0edbf3ef8a49608ee326f8fd75c473b7e3982095e2d100000000000000c30134f8c9b6d2470488d7a67a888f6fa12f8692e0c3411fbfb92f0f68f67eedae03ca57ef13021acc22dc4105010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0315230e0004ae03ca57043e3d1e1d0c8796bf579aef0c0000000000122f4e696e6a61506f6f6c2f5345475749542fffffffff038427a112000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed5c748e121c0fe146d973a4ac26fa4a68b0549d46ee22d25f50a5e46fe1b377ee00000000000000002952534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f10909001200000000000000000000000000000000000000000000000000000000000000000000000000100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000010000000120925534261de4dcebb1ed5ab1b62bfe7a3ef968fb111dc2c910adfebc6e3bdf010000006b483045022100f50198f5ae66211a4f485190abe4dc7accdabe3bc214ebc9ea7069b97097d46e0220316a70a03014887086e335fc1b48358d46cd6bdc9af3b57c109c94af76fc915101210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415effffffff0220a10700000000001976a91450333046115eaa0ac9e0216565f945070e44573988ac2e7cd01a000000001976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac00000000010000000203a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322010000006a47304402204efc3d70e4ca3049c2a425025edf22d5ca355f9ec899dbfbbeeb2268533a0f2b02204780d3739653035af4814ea52e1396d021953f948c29754edd0ee537364603dc012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff03a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322000000006a47304402202d96defdc5b4af71d6ba28c9a6042c2d5ee7bc6de565d4db84ef517445626e03022022da80320e9e489c8f41b74833dfb6a54a4eb5087cdb46eb663eef0b25caa526012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a914b7e6f7ff8658b2d1fb107e3d7be7af4742e6b1b3876f88fc00000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac0000000001000000043ffd60d3818431c495b89be84afac205d5d1ed663009291c560758bbd0a66df5010000006b483045022100f344607de9df42049688dcae8ff1db34c0c7cd25ec05516e30d2bc8f12ac9b2f022060b648f6a21745ea6d9782e17bcc4277b5808326488a1f40d41e125879723d3a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffffa5379401cce30f84731ef1ba65ce27edf2cc7ce57704507ebe8714aa16a96b92010000006a473044022020c37a63bf4d7f564c2192528709b6a38ab8271bd96898c6c2e335e5208661580220435c6f1ad4d9305d2c0a818b2feb5e45d443f2f162c0f61953a14d097fd07064012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff70e731e193235ff12c3184510895731a099112ffca4b00246c60003c40f843ce000000006a473044022053760f74c29a879e30a17b5f03a5bb057a5751a39f86fa6ecdedc36a1b7db04c022041d41c9b95f00d2d10a0373322a9025dba66c942196bc9d8adeb0e12d3024728012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff66b7a71b3e50379c8e85fc18fe3f1a408fc985f257036c34702ba205cef09f6f000000006a4730440220499bf9e2db3db6e930228d0661395f65431acae466634d098612fd80b08459ee022040e069fc9e3c60009f521cef54c38aadbd1251aee37940e6018aadb10f194d6a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a9148fc37ad460fdfbd2b44fe446f6e3071a4f64faa6878f447f0b000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac00000000",["a914feb8a29635c56d9cd913122f90678756bf23887687","76a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac"],"da49977ba1ee0d620a2c4f8f646b03cd0d230f5c6c994722e3ba884889f0be1a","09027acea61b6cc3fb33f5d52f7d088a6b2f75d234e89ca800","4cd9dd007a325199102f1fc0b7d77ca25ee3c84d46018c4353ecfcb56c0d3e7a","Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"],
-[987876,"0000000000000c00901f2049055e2a437c819d79a3d54fd63e6af796cd7b8a79","000000202694f74969fdb542090e95a56bc8aa2d646e27033850e32f1c5f000000000000f7e53676b3f12d5beb524ed617f2d25f5a93b5f4f52c1ba2678260d72712f8dd0a6dfe5740257e1a4b1768960101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1603e4120ff9c30a1c216900002f424d4920546573742fffffff0001205fa012000000001e76a914c486de584a735ec2f22da7cd9681614681f92173d83d0aa68688ac00000000",[],"e9d729b72d533c29abe5276d5cf6c152f3723f10efe000b1e0c9ca5265a8beb6","010c0b40","e6137ae5a8424c40da1e5023c16975cc97b09300b4c050e6b1c713add3836c40","Coinbase tx has unparseable output script"],
-[1263442,"000000006f27ddfe1dd680044a34548f41bed47eba9e6f0b310da21423bc5f33","000000201c8d1a529c39a396db2db234d5ec152fa651a2872966daccbde028b400000000083f14492679151dbfaa1a825ef4c18518e780c1f91044180280a7d33f4a98ff5f45765aaddc001d38333b9a02010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff230352471300fe5f45765afe94690a000963676d696e6572343208000000000000000000ffffffff024423a804000000001976a914f2c25ac3d59f3d674b1d1d0a25c27339aaac0ba688ac0000000000000000266a24aa21a9edcb26cb3052426b9ebb4d19c819ef87c19677bbf3a7c46ef0855bd1b2abe83491012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101d20978463906ba4ff5e7192494b88dd5eb0de85d900ab253af909106faa22cc5010000000004000000014777ff000000000016001446c29eabe8208a33aa1023c741fa79aa92e881ff0347304402207d7ca96134f2bcfdd6b536536fdd39ad17793632016936f777ebb32c22943fda02206014d2fb8a6aa58279797f861042ba604ebd2f8f61e5bddbd9d3be5a245047b201004b632103eeaeba7ce5dc2470221e9517fb498e8d6bd4e73b85b8be655196972eb9ccd5566754b2752103a40b74d43df244799d041f32ce1ad515a6cd99501701540e38750d883ae21d3a68ac00000000",["002027a5000c7917f785d8fc6e5a55adfca8717ecb973ebb7743849ff956d896a7ed"],"a4a4d6c6034da8aa06f01fe71f1fffbd79e032006b07f6c7a2c60a66aa310c01","0385acb4f0fe889ef0","3588f34fbbc11640f9ed40b2a66a4e096215d50389691309c1dac74d4268aa81","Includes witness data"]
+[180480,"00000000fd3ceb2404ff07a785c7fdcc76619edc8ed61bd25134eaa22084366a","020000006058aa080a655aa991a444bd7d1f2defd9a3bbe68aabb69030cf3b4e00000000d2e826bfd7ef0beaa891a7eedbc92cd6a544a6cb61c7bdaa436762eb2123ef9790f5f552ffff001d0002c90f0501000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0300c102024608062f503253482fffffffff01c0c6072a01000000232102e769e60137a4df6b0df8ebd387cca44c4c57ae74cc0114a8e8317c8f3bfd85e9ac00000000010000000381a0802911a01ffb025c4dea0bc77963e8c1bb46313b71164c53f72f37fe5248010000000151ffffffffc904b267833d215e2128bd9575242232ac2bc311550c7fc1f0ef6f264b40d14c010000000151ffffffffdf0915666649dba81886519c531649b7b02180b4af67d6885e871299e9d5f775000000000151ffffffff0180817dcb00000000232103bb52138972c48a132fc1f637858c5189607dd0f7fe40c4f20f6ad65f2d389ba4ac0000000001000000018da38b434fba82d66052af74fc5e4e94301b114d9bc03f819dc876398404c8b4010000006c493046022100fe738b7580dc5fb5168e51fc61b5aed211125eb71068031009a22d9bbad752c5022100be5086baa384d40bcab0fa586e4f728397388d86e18b66cc417dc4f7fa4f9878012103f233299455134caa2687bdf15cb0becdfb03bd0ff2ff38e65ec6b7834295c34fffffffff022ebc1400000000001976a9147779b7fba1c1e06b717069b80ca170e8b04458a488ac9879c40f000000001976a9142a0307cd925dbb66b534c4db33003dd18c57015788ac0000000001000000026139a62e3422a602de36c873a225c1d3ca5aeee598539ceecb9f0dc8d1ad0f83010000006b483045022100ad9f32b4a0a2ddc19b5a74eba78123e57616f1b3cfd72ce68c03ea35a3dda1f002200dbd22aa6da17213df5e70dfc3b2611d40f70c98ed9626aa5e2cde9d97461f0a012103ddb295d2f1e8319187738fb4b230fdd9aa29d0e01647f69f6d770b9ab24eea90ffffffff983c82c87cf020040d671956525014d5c2b28c6d948c85e1a522362c0059eeae010000006b4830450221009ca544274c786d30a5d5d25e17759201ea16d3aedddf0b9e9721246f7ef6b32e02202cfa5564b6e87dfd9fd98957820e4d4e6238baeb0f65fe305d91506bb13f5f4f012103c99113deac0d5d044e3ac0346abc02501542af8c8d3759f1382c72ff84e704f7ffffffff02c0c62d00000000001976a914ae19d27efe12f5a886dc79af37ad6805db6f922d88ac70ce2000000000001976a9143b8d051d37a07ea1042067e93efe63dbf73920b988ac000000000100000002be566e8cd9933f0c75c4a82c027f7d0c544d5c101d0607ef6ae5d07b98e7f1dc000000006b483045022036a8cdfd5ea7ebc06c2bfb6e4f942bbf9a1caeded41680d11a3a9f5d8284abad022100cacb92a5be3f39e8bc14db1710910ef7b395fa1e18f45d41c28d914fcdde33be012102bf59abf110b5131fae0a3ce1ec379329b4c896a6ae5d443edb68529cc2bc7816ffffffff96cf67645b76ceb23fe922874847456a15feee1655082ff32d25a6bf2c0dfc90000000006a47304402203471ca2001784a5ac0abab583581f2613523da47ec5f53df833c117b5abd81500220618a2847723d57324f2984678db556dbca1a72230fc7e39df04c2239942ba942012102925c9794fd7bb9f8b29e207d5fc491b1150135a21f505041858889fa4edf436fffffffff026c840f00000000001976a914797fb8777d7991d8284d88bfd421ce520f0f843188ac00ca9a3b000000001976a9146d10f3f592699265d10b106eda37c3ce793f7a8588ac00000000",["","","","76a9142903b138c24be9e070b3e73ec495d77a204615e788ac","76a91433a1941fd9a37b9821d376f5a51bd4b52fa50e2888ac","76a914e4374e8155d0865742ca12b8d4d14d41b57d682f88ac","76a914001fa7459a6cfc64bdc178ba7e7a21603bb2568f88ac","76a914f6039952bc2b307aeec5371bfb96b66078ec17f688ac"],"d34ef98386f413769502808d4bac5f20f8dfd5bffc9eedafaa71de0eb1f01489","0db414c859a07e8205876354a210a75042d0463404913d61a8e068e58a3ae2aa080026","c582d51c0ca365e3fcf36c51cb646d7f83a67e867cb4743fd2128e3e022b700c","Tx spends from empty output script"],
+[926485,"000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313","0000002060bbab0edbf3ef8a49608ee326f8fd75c473b7e3982095e2d100000000000000c30134f8c9b6d2470488d7a67a888f6fa12f8692e0c3411fbfb92f0f68f67eedae03ca57ef13021acc22dc4105010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0315230e0004ae03ca57043e3d1e1d0c8796bf579aef0c0000000000122f4e696e6a61506f6f6c2f5345475749542fffffffff038427a112000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed5c748e121c0fe146d973a4ac26fa4a68b0549d46ee22d25f50a5e46fe1b377ee00000000000000002952534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f10909001200000000000000000000000000000000000000000000000000000000000000000000000000100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000010000000120925534261de4dcebb1ed5ab1b62bfe7a3ef968fb111dc2c910adfebc6e3bdf010000006b483045022100f50198f5ae66211a4f485190abe4dc7accdabe3bc214ebc9ea7069b97097d46e0220316a70a03014887086e335fc1b48358d46cd6bdc9af3b57c109c94af76fc915101210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415effffffff0220a10700000000001976a91450333046115eaa0ac9e0216565f945070e44573988ac2e7cd01a000000001976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac00000000010000000203a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322010000006a47304402204efc3d70e4ca3049c2a425025edf22d5ca355f9ec899dbfbbeeb2268533a0f2b02204780d3739653035af4814ea52e1396d021953f948c29754edd0ee537364603dc012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff03a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322000000006a47304402202d96defdc5b4af71d6ba28c9a6042c2d5ee7bc6de565d4db84ef517445626e03022022da80320e9e489c8f41b74833dfb6a54a4eb5087cdb46eb663eef0b25caa526012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a914b7e6f7ff8658b2d1fb107e3d7be7af4742e6b1b3876f88fc00000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac0000000001000000043ffd60d3818431c495b89be84afac205d5d1ed663009291c560758bbd0a66df5010000006b483045022100f344607de9df42049688dcae8ff1db34c0c7cd25ec05516e30d2bc8f12ac9b2f022060b648f6a21745ea6d9782e17bcc4277b5808326488a1f40d41e125879723d3a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffffa5379401cce30f84731ef1ba65ce27edf2cc7ce57704507ebe8714aa16a96b92010000006a473044022020c37a63bf4d7f564c2192528709b6a38ab8271bd96898c6c2e335e5208661580220435c6f1ad4d9305d2c0a818b2feb5e45d443f2f162c0f61953a14d097fd07064012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff70e731e193235ff12c3184510895731a099112ffca4b00246c60003c40f843ce000000006a473044022053760f74c29a879e30a17b5f03a5bb057a5751a39f86fa6ecdedc36a1b7db04c022041d41c9b95f00d2d10a0373322a9025dba66c942196bc9d8adeb0e12d3024728012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff66b7a71b3e50379c8e85fc18fe3f1a408fc985f257036c34702ba205cef09f6f000000006a4730440220499bf9e2db3db6e930228d0661395f65431acae466634d098612fd80b08459ee022040e069fc9e3c60009f521cef54c38aadbd1251aee37940e6018aadb10f194d6a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a9148fc37ad460fdfbd2b44fe446f6e3071a4f64faa6878f447f0b000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac00000000",["a914feb8a29635c56d9cd913122f90678756bf23887687","76a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac"],"8f13b9a9c85611635b47906c3053ac53cfcec7211455d4cb0d63dc9acc13d472","09027acea61b6cc3fb33f5d52f7d088a6b2f75d234e89ca800","546c574a0472144bcaf9b6aeabf26372ad87c7af7d1ee0dbfae5e099abeae49c","Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"],
+[987876,"0000000000000c00901f2049055e2a437c819d79a3d54fd63e6af796cd7b8a79","000000202694f74969fdb542090e95a56bc8aa2d646e27033850e32f1c5f000000000000f7e53676b3f12d5beb524ed617f2d25f5a93b5f4f52c1ba2678260d72712f8dd0a6dfe5740257e1a4b1768960101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1603e4120ff9c30a1c216900002f424d4920546573742fffffff0001205fa012000000001e76a914c486de584a735ec2f22da7cd9681614681f92173d83d0aa68688ac00000000",[],"fe4d230dbb0f4fec9bed23a5283e08baf996e3f32b93f52c7de1f641ddfd04ad","010c0b40","0965a544743bbfa36f254446e75630c09404b3d164a261892372977538928ed5","Coinbase tx has unparseable output script"],
+[1263442,"000000006f27ddfe1dd680044a34548f41bed47eba9e6f0b310da21423bc5f33","000000201c8d1a529c39a396db2db234d5ec152fa651a2872966daccbde028b400000000083f14492679151dbfaa1a825ef4c18518e780c1f91044180280a7d33f4a98ff5f45765aaddc001d38333b9a02010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff230352471300fe5f45765afe94690a000963676d696e6572343208000000000000000000ffffffff024423a804000000001976a914f2c25ac3d59f3d674b1d1d0a25c27339aaac0ba688ac0000000000000000266a24aa21a9edcb26cb3052426b9ebb4d19c819ef87c19677bbf3a7c46ef0855bd1b2abe83491012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101d20978463906ba4ff5e7192494b88dd5eb0de85d900ab253af909106faa22cc5010000000004000000014777ff000000000016001446c29eabe8208a33aa1023c741fa79aa92e881ff0347304402207d7ca96134f2bcfdd6b536536fdd39ad17793632016936f777ebb32c22943fda02206014d2fb8a6aa58279797f861042ba604ebd2f8f61e5bddbd9d3be5a245047b201004b632103eeaeba7ce5dc2470221e9517fb498e8d6bd4e73b85b8be655196972eb9ccd5566754b2752103a40b74d43df244799d041f32ce1ad515a6cd99501701540e38750d883ae21d3a68ac00000000",["002027a5000c7917f785d8fc6e5a55adfca8717ecb973ebb7743849ff956d896a7ed"],"31d66d516a9eda7de865df29f6ef6cb8e4bf9309e5dac899968a9a62a5df61e3","0385acb4f0fe889ef0","4e6d564c2a2452065c205dd7eb2791124e0c4e0dbb064c410c24968572589dec","Includes witness data"],
+[1414221,"0000000000000027b2b3b3381f114f674f481544ff2be37ae3788d7e078383b1","000000204ea88307a7959d8207968f152bedca5a93aefab253f1fb2cfb032a400000000070cebb14ec6dbc27a9dfd066d9849a4d3bac5f674665f73a5fe1de01a022a0c851fda85bf05f4c19a779d1450102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff18034d94154d696e6572476174653030310d000000f238f401ffffffff01c817a804000000000000000000",[],"5e5e12d90693c8e936f01847859404c67482439681928353ca1296982042864e","00","021e8882ef5a0ed932edeebbecfeda1d7ce528ec7b3daa27641acf1189d7b5dc","Empty data"]
]
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 94e8c95345..0bde92c18d 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <dbwrapper.h>
#include <uint256.h>
#include <random.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <memory>
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index e5d62a3ab2..bcb9a7c181 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -15,7 +15,7 @@
#include <util/system.h>
#include <validation.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <stdint.h>
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index ff2b8d4fc9..f5bda7d5e6 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-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.
@@ -6,7 +6,7 @@
#include <string>
#include <script/sign.h>
#include <script/standard.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <script/descriptor.h>
#include <util/strencodings.h>
@@ -154,8 +154,8 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
// Test whether the observed key path is present in the 'paths' variable (which contains expected, unobserved paths),
// and then remove it from that set.
for (const auto& origin : script_provider.origins) {
- BOOST_CHECK_MESSAGE(paths.count(origin.second.path), "Unexpected key path: " + prv);
- left_paths.erase(origin.second.path);
+ BOOST_CHECK_MESSAGE(paths.count(origin.second.second.path), "Unexpected key path: " + prv);
+ left_paths.erase(origin.second.second.path);
}
}
}
diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp
index 079a09f8f9..1db2f8054c 100644
--- a/src/test/flatfile_tests.cpp
+++ b/src/test/flatfile_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <flatfile.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
index 93aee10bb7..6bd6bb1be3 100644
--- a/src/test/fs_tests.cpp
+++ b/src/test/fs_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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 <fs.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 859fba0bdc..97d7633715 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// 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.
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 8048238028..8a42344642 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/strencodings.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <string>
#include <vector>
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index e8e5040855..325b7002f2 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2013-2018 The Bitcoin Core developers
+// 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.
#include <crypto/siphash.h>
#include <hash.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index bf295042de..e924f27d1b 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -9,7 +9,7 @@
#include <key_io.h>
#include <script/script.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/key_properties.cpp b/src/test/key_properties.cpp
index c564b4eab8..8b508ed7f7 100644
--- a/src/test/key_properties.cpp
+++ b/src/test/key_properties.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-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 <key.h>
@@ -8,7 +8,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <string>
#include <vector>
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index a768b4bcbd..e816546e62 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,7 +9,7 @@
#include <uint256.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <string>
#include <vector>
diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp
index 0788f75b04..00b36f51fb 100644
--- a/src/test/limitedmap_tests.cpp
+++ b/src/test/limitedmap_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <limitedmap.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 23ca9d89ae..0f74b379c0 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -6,7 +6,7 @@
#include <txmempool.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <list>
diff --git a/src/test/merkle_tests.cpp b/src/test/merkle_tests.cpp
index 4cdf0f003e..1684258c9f 100644
--- a/src/test/merkle_tests.cpp
+++ b/src/test/merkle_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-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 <consensus/merkle.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp
index 4978593285..eac43471c7 100644
--- a/src/test/merkleblock_tests.cpp
+++ b/src/test/merkleblock_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <merkleblock.h>
#include <uint256.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 5ba1df2ec2..6ed4359059 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -18,7 +18,7 @@
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <memory>
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 8afe4b8a59..682f1bee26 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -11,7 +11,7 @@
#include <script/sign.h>
#include <script/ismine.h>
#include <uint256.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index b4ae8e9765..54d18c0a1c 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -1,8 +1,8 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <string>
#include <boost/test/unit_test.hpp>
#include <hash.h>
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 0d557cff13..dd5e3eb6d5 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <netbase.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/strencodings.h>
#include <string>
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
index 5020192804..c5513ae9fa 100644
--- a/src/test/pmt_tests.cpp
+++ b/src/test/pmt_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,7 +9,7 @@
#include <uint256.h>
#include <arith_uint256.h>
#include <version.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 7b274a1658..149094fc00 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -8,7 +8,7 @@
#include <uint256.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 26cdc9bc5c..653433bfce 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-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.
@@ -7,7 +7,7 @@
#include <pow.h>
#include <random.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index 7341389208..141be50f50 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-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.
@@ -9,7 +9,7 @@
#include <serialize.h>
#include <streams.h>
-#include <test/test_bitcoin.h>
+#include <test/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 bdb411d53f..2b01acf7fa 100644
--- a/src/test/raii_event_tests.cpp
+++ b/src/test/raii_event_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Copyright (c) 2016-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.
@@ -12,7 +12,7 @@
#include <support/events.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp
index 8194070aba..e6fbe2355d 100644
--- a/src/test/random_tests.cpp
+++ b/src/test/random_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// 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.
#include <random.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/reverselock_tests.cpp b/src/test/reverselock_tests.cpp
index 91c76fda88..69db9dcf4e 100644
--- a/src/test/reverselock_tests.cpp
+++ b/src/test/reverselock_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-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 <reverselock.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index ff48398925..07d1326bcb 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -12,7 +12,7 @@
#include <key_io.h>
#include <netbase.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
@@ -31,10 +31,9 @@ UniValue CallRPC(std::string args)
request.strMethod = strMethod;
request.params = RPCConvertValues(strMethod, vArgs);
request.fHelp = false;
- BOOST_CHECK(tableRPC[strMethod]);
- rpcfn_type method = tableRPC[strMethod]->actor;
+ if (RPCIsInWarmup(nullptr)) SetRPCWarmupFinished();
try {
- UniValue result = (*method)(request);
+ UniValue result = tableRPC.execute(request);
return result;
}
catch (const UniValue& objError) {
diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp
index 8085a21928..891aa8e5c3 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <compat/sanity.h>
#include <key.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index 100d65b779..42242b962b 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <random.h>
#include <scheduler.h>
-#include <test/test_bitcoin.h>
+#include <test/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 3a2a11ef98..0ce5f09e42 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -10,9 +10,10 @@
#include <policy/policy.h>
#include <script/script.h>
#include <script/script_error.h>
+#include <policy/settings.h>
#include <script/sign.h>
#include <script/ismine.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index bde82018c7..36a2b1bee5 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// 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.
@@ -8,7 +8,7 @@
#include <script/script.h>
#include <script/script_error.h>
#include <script/standard.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 87c3e74df0..588ae55a69 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -12,8 +12,8 @@
#include <script/sign.h>
#include <util/system.h>
#include <util/strencodings.h>
-#include <test/test_bitcoin.h>
-#include <rpc/server.h>
+#include <test/setup_common.h>
+#include <rpc/util.h>
#if defined(HAVE_CONSENSUS_LIB)
#include <script/bitcoinconsensus.h>
diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp
index f9b407ce3e..e7916f5000 100644
--- a/src/test/scriptnum_tests.cpp
+++ b/src/test/scriptnum_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/scriptnum10.h>
#include <script/script.h>
-#include <test/test_bitcoin.h>
+#include <test/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 002f61f6a2..2fab309aa4 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <serialize.h>
#include <streams.h>
#include <hash.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <stdint.h>
diff --git a/src/test/test_bitcoin.cpp b/src/test/setup_common.cpp
index cdfd4db589..6b671b2400 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/setup_common.cpp
@@ -1,8 +1,8 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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 <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <banman.h>
#include <chainparams.h>
@@ -19,6 +19,7 @@
#include <script/sigcache.h>
#include <streams.h>
#include <ui_interface.h>
+#include <util/validation.h>
#include <validation.h>
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
@@ -32,7 +33,7 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
}
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
- : m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
+ : m_path_root(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{
SHA256AutoDetect();
ECC_Start();
@@ -41,11 +42,12 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
InitSignatureCache();
InitScriptExecutionCache();
fCheckBlockIndex = true;
- // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
- // TODO: fix the code to support SegWit blocks.
- gArgs.ForceSetArg("-vbparams", strprintf("segwit:0:%d", (int64_t)Consensus::BIP9Deployment::NO_TIMEOUT));
SelectParams(chainName);
- noui_connect();
+ static bool noui_connected = false;
+ if (!noui_connected) {
+ noui_connect();
+ noui_connected = true;
+ }
}
BasicTestingSetup::~BasicTestingSetup()
@@ -92,7 +94,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
nScriptCheckThreads = 3;
for (int i = 0; i < nScriptCheckThreads - 1; i++)
- threadGroup.create_thread(&ThreadScriptCheck);
+ threadGroup.create_thread([i]() { return ThreadScriptCheck(i); });
g_banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
@@ -114,6 +116,11 @@ TestingSetup::~TestingSetup()
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
+ // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
+ // TODO: fix the code to support SegWit blocks.
+ gArgs.ForceSetArg("-vbparams", strprintf("segwit:0:%d", (int64_t)Consensus::BIP9Deployment::NO_TIMEOUT));
+ SelectParams(CBaseChainParams::REGTEST);
+
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
diff --git a/src/test/test_bitcoin.h b/src/test/setup_common.h
index 38c6d85a8d..893eca216d 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/setup_common.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-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_TEST_BITCOIN_H
-#define BITCOIN_TEST_TEST_BITCOIN_H
+#ifndef BITCOIN_TEST_SETUP_COMMON_H
+#define BITCOIN_TEST_SETUP_COMMON_H
#include <chainparamsbase.h>
#include <fs.h>
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 04d5462acb..15f8db899b 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -1,8 +1,8 @@
-// Copyright (c) 2013-2018 The Bitcoin Core developers
+// 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.
-#include <consensus/tx_verify.h>
+#include <consensus/tx_check.h>
#include <consensus/validation.h>
#include <test/data/sighash.json.h>
#include <hash.h>
@@ -10,7 +10,7 @@
#include <script/script.h>
#include <serialize.h>
#include <streams.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/system.h>
#include <util/strencodings.h>
#include <version.h>
diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp
index 6a0349cd4e..4efa023fbb 100644
--- a/src/test/sigopcount_tests.cpp
+++ b/src/test/sigopcount_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -9,7 +9,7 @@
#include <script/script.h>
#include <script/standard.h>
#include <uint256.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index 5c46976ace..3d39dfdb75 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-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 <chain.h>
#include <util/system.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <vector>
@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_test)
// Pick a random element in vBlocksMain.
int r = InsecureRandRange(vBlocksMain.size());
int64_t test_time = vBlocksMain[r].nTime;
- CBlockIndex *ret = chain.FindEarliestAtLeast(test_time);
+ CBlockIndex* ret = chain.FindEarliestAtLeast(test_time, 0);
BOOST_CHECK(ret->nTimeMax >= test_time);
BOOST_CHECK((ret->pprev==nullptr) || ret->pprev->nTimeMax < test_time);
BOOST_CHECK(vBlocksMain[r].GetAncestor(ret->nHeight) == ret);
@@ -158,22 +158,34 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_edge_test)
CChain chain;
chain.SetTip(&blocks.back());
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50)->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100)->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150)->nHeight, 3);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200)->nHeight, 3);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250)->nHeight, 6);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300)->nHeight, 6);
- BOOST_CHECK(!chain.FindEarliestAtLeast(350));
-
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0)->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1)->nHeight, 0);
-
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min())->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1)->nHeight, 0);
- BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max()));
- BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max()));
- BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1));
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(50, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(100, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(150, 0)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(200, 0)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(250, 0)->nHeight, 6);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(300, 0)->nHeight, 6);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(350, 0));
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1, 0)->nHeight, 0);
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min(), 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1, 0)->nHeight, 0);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max(), 0));
+ BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max(), 0));
+ BOOST_CHECK(!chain.FindEarliestAtLeast(int64_t(std::numeric_limits<unsigned int>::max()) + 1, 0));
+
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, -1)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 0)->nHeight, 0);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 3)->nHeight, 3);
+ BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(0, 8)->nHeight, 8);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(0, 9));
+
+ CBlockIndex* ret1 = chain.FindEarliestAtLeast(100, 2);
+ BOOST_CHECK(ret1->nTimeMax >= 100 && ret1->nHeight == 2);
+ BOOST_CHECK(!chain.FindEarliestAtLeast(300, 9));
+ CBlockIndex* ret2 = chain.FindEarliestAtLeast(200, 4);
+ BOOST_CHECK(ret2->nTimeMax >= 200 && ret2->nHeight == 4);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index a1940eb80e..4e37199c63 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2012-2018 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <streams.h>
#include <support/allocators/zeroafterfree.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp
index df0380546e..c1399d2dbe 100644
--- a/src/test/sync_tests.cpp
+++ b/src/test/sync_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2012-2017 The Bitcoin Core developers
+// Copyright (c) 2012-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <sync.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp
index 474a67497f..b4c0e6a0f4 100644
--- a/src/test/timedata_tests.cpp
+++ b/src/test/timedata_tests.cpp
@@ -1,9 +1,9 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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 <timedata.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp
index c7ceb2f1e9..6d8459f5b1 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/test_bitcoin.h>
+#include <test/setup_common.h>
#include <torcontrol.h>
#include <boost/test/unit_test.hpp>
@@ -20,7 +20,6 @@ BOOST_FIXTURE_TEST_SUITE(torcontrol_tests, BasicTestingSetup)
static void CheckSplitTorReplyLine(std::string input, std::string command, std::string args)
{
- BOOST_TEST_MESSAGE(std::string("CheckSplitTorReplyLine(") + input + ")");
auto ret = SplitTorReplyLine(input);
BOOST_CHECK_EQUAL(ret.first, command);
BOOST_CHECK_EQUAL(ret.second, args);
@@ -61,7 +60,6 @@ BOOST_AUTO_TEST_CASE(util_SplitTorReplyLine)
static void CheckParseTorReplyMapping(std::string input, std::map<std::string,std::string> expected)
{
- BOOST_TEST_MESSAGE(std::string("CheckParseTorReplyMapping(") + input + ")");
auto ret = ParseTorReplyMapping(input);
BOOST_CHECK_EQUAL(ret.size(), expected.size());
auto r_it = ret.begin();
@@ -173,7 +171,6 @@ BOOST_AUTO_TEST_CASE(util_ParseTorReplyMapping)
// Special handling for null case
// (needed because string comparison reads the null as end-of-string)
- BOOST_TEST_MESSAGE(std::string("CheckParseTorReplyMapping(Null=\"\\0\")"));
auto ret = ParseTorReplyMapping("Null=\"\\0\"");
BOOST_CHECK_EQUAL(ret.size(), 1U);
auto r_it = ret.begin();
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 39cff3f463..6242fdabd1 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -1,20 +1,21 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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 <test/data/tx_invalid.json.h>
#include <test/data/tx_valid.json.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <clientversion.h>
#include <checkqueue.h>
-#include <consensus/tx_verify.h>
+#include <consensus/tx_check.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <key.h>
#include <keystore.h>
#include <validation.h>
#include <policy/policy.h>
+#include <policy/settings.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/script_error.h>
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index d667c26c3c..9d62b471c1 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -1,11 +1,11 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// 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.
#include <chainparams.h>
#include <index/txindex.h>
#include <script/standard.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/system.h>
#include <util/time.h>
#include <validation.h>
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index c2777cd6d1..331c340b74 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2018 The Bitcoin Core developers
+// 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.
@@ -8,7 +8,7 @@
#include <consensus/validation.h>
#include <primitives/transaction.h>
#include <script/script.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 4d04aae7e9..01018043b1 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -11,7 +11,7 @@
#include <random.h>
#include <script/standard.h>
#include <script/sign.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/time.h>
#include <core_io.h>
#include <keystore.h>
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index cca5e20296..c1749fb856 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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 <arith_uint256.h>
#include <uint256.h>
#include <version.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <boost/test/unit_test.hpp>
#include <stdint.h>
diff --git a/src/test/util.cpp b/src/test/util.cpp
new file mode 100644
index 0000000000..05d3a97a59
--- /dev/null
+++ b/src/test/util.cpp
@@ -0,0 +1,91 @@
+// 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.h>
+
+#include <chainparams.h>
+#include <consensus/merkle.h>
+#include <consensus/validation.h>
+#include <key_io.h>
+#include <miner.h>
+#include <outputtype.h>
+#include <pow.h>
+#include <scheduler.h>
+#include <script/standard.h>
+#include <txdb.h>
+#include <validation.h>
+#include <validationinterface.h>
+#ifdef ENABLE_WALLET
+#include <wallet/wallet.h>
+#endif
+
+#include <boost/thread.hpp>
+
+const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj";
+
+#ifdef ENABLE_WALLET
+std::string getnewaddress(CWallet& w)
+{
+ constexpr auto output_type = OutputType::BECH32;
+
+ CPubKey new_key;
+ if (!w.GetKeyFromPool(new_key)) assert(false);
+
+ w.LearnRelatedScripts(new_key, output_type);
+ const auto dest = GetDestinationForKey(new_key, output_type);
+
+ w.SetAddressBook(dest, /* label */ "", "receive");
+
+ return EncodeDestination(dest);
+}
+
+void importaddress(CWallet& wallet, const std::string& address)
+{
+ LOCK(wallet.cs_wallet);
+ const auto dest = DecodeDestination(address);
+ assert(IsValidDestination(dest));
+ const auto script = GetScriptForDestination(dest);
+ wallet.MarkDirty();
+ assert(!wallet.HaveWatchOnly(script));
+ if (!wallet.AddWatchOnly(script, 0 /* nCreateTime */)) assert(false);
+ wallet.SetAddressBook(dest, /* label */ "", "receive");
+}
+#endif // ENABLE_WALLET
+
+CTxIn generatetoaddress(const std::string& address)
+{
+ const auto dest = DecodeDestination(address);
+ assert(IsValidDestination(dest));
+ const auto coinbase_script = GetScriptForDestination(dest);
+
+ return MineBlock(coinbase_script);
+}
+
+CTxIn MineBlock(const CScript& coinbase_scriptPubKey)
+{
+ auto block = PrepareBlock(coinbase_scriptPubKey);
+
+ while (!CheckProofOfWork(block->GetHash(), block->nBits, Params().GetConsensus())) {
+ ++block->nNonce;
+ assert(block->nNonce);
+ }
+
+ bool processed{ProcessNewBlock(Params(), block, true, nullptr)};
+ assert(processed);
+
+ return CTxIn{block->vtx[0]->GetHash(), 0};
+}
+
+std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey)
+{
+ auto block = std::make_shared<CBlock>(
+ BlockAssembler{Params()}
+ .CreateNewBlock(coinbase_scriptPubKey)
+ ->block);
+
+ block->nTime = ::chainActive.Tip()->GetMedianTimePast() + 1;
+ block->hashMerkleRoot = BlockMerkleRoot(*block);
+
+ return block;
+}
diff --git a/src/test/util.h b/src/test/util.h
new file mode 100644
index 0000000000..8ba647ec3f
--- /dev/null
+++ b/src/test/util.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2019 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_UTIL_H
+#define BITCOIN_TEST_UTIL_H
+
+#include <memory>
+#include <string>
+
+class CBlock;
+class CScript;
+class CTxIn;
+class CWallet;
+
+// Constants //
+
+extern const std::string ADDRESS_BCRT1_UNSPENDABLE;
+
+// Lower-level utils //
+
+/** Returns the generated coin */
+CTxIn MineBlock(const CScript& coinbase_scriptPubKey);
+/** Prepare a block to be mined */
+std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey);
+
+
+// RPC-like //
+
+/** Import the address to the wallet */
+void importaddress(CWallet& wallet, const std::string& address);
+/** Returns a new address from the wallet */
+std::string getnewaddress(CWallet& w);
+/** Returns the generated coin */
+CTxIn generatetoaddress(const std::string& address);
+
+
+#endif // BITCOIN_TEST_UTIL_H
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index e17ae7103b..0f1834240d 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// 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.
@@ -9,7 +9,7 @@
#include <sync.h>
#include <util/strencodings.h>
#include <util/moneystr.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <stdint.h>
#include <vector>
@@ -36,8 +36,10 @@ BOOST_AUTO_TEST_CASE(util_criticalsection)
do {
TRY_LOCK(cs, lockTest);
- if (lockTest)
+ if (lockTest) {
+ BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
break;
+ }
BOOST_ERROR("break was swallowed!");
} while(0);
@@ -78,80 +80,40 @@ BOOST_AUTO_TEST_CASE(util_HexStr)
"04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f");
BOOST_CHECK_EQUAL(
- HexStr(ParseHex_expected, ParseHex_expected + 5, true),
- "04 67 8a fd b0");
-
- BOOST_CHECK_EQUAL(
HexStr(ParseHex_expected + sizeof(ParseHex_expected),
ParseHex_expected + sizeof(ParseHex_expected)),
"");
BOOST_CHECK_EQUAL(
- HexStr(ParseHex_expected + sizeof(ParseHex_expected),
- ParseHex_expected + sizeof(ParseHex_expected), true),
- "");
-
- BOOST_CHECK_EQUAL(
HexStr(ParseHex_expected, ParseHex_expected),
"");
- BOOST_CHECK_EQUAL(
- HexStr(ParseHex_expected, ParseHex_expected, true),
- "");
-
std::vector<unsigned char> ParseHex_vec(ParseHex_expected, ParseHex_expected + 5);
BOOST_CHECK_EQUAL(
- HexStr(ParseHex_vec, true),
- "04 67 8a fd b0");
-
- BOOST_CHECK_EQUAL(
HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend()),
"b0fd8a6704"
);
BOOST_CHECK_EQUAL(
- HexStr(ParseHex_vec.rbegin(), ParseHex_vec.rend(), true),
- "b0 fd 8a 67 04"
- );
-
- BOOST_CHECK_EQUAL(
HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected),
std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
""
);
BOOST_CHECK_EQUAL(
- HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected),
- std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
- ""
- );
-
- BOOST_CHECK_EQUAL(
HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1),
std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
"04"
);
BOOST_CHECK_EQUAL(
- HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 1),
- std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
- "04"
- );
-
- BOOST_CHECK_EQUAL(
HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5),
std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
"b0fd8a6704"
);
BOOST_CHECK_EQUAL(
- HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 5),
- std::reverse_iterator<const uint8_t *>(ParseHex_expected), true),
- "b0 fd 8a 67 04"
- );
-
- BOOST_CHECK_EQUAL(
HexStr(std::reverse_iterator<const uint8_t *>(ParseHex_expected + 65),
std::reverse_iterator<const uint8_t *>(ParseHex_expected)),
"5f1df16b2b704c8a578d0bbaf74d385cde12c11ee50455f3c438ef4c3fbcf649b6de611feae06279a60939e028a8d65c10b73071a6f16719274855feb0fd8a6704"
@@ -196,6 +158,9 @@ struct TestArgsManager : public ArgsManager
AddArg(args[i], "", false, OptionsCategory::OPTIONS);
}
}
+ using ArgsManager::ReadConfigStream;
+ using ArgsManager::cs_args;
+ using ArgsManager::m_network;
};
BOOST_AUTO_TEST_CASE(util_ParseParameters)
@@ -613,6 +578,232 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
}
+// 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
+// unintentially.
+//
+// The test covers:
+//
+// - Combining different setting actions. Possible actions are: configuring a
+// setting, negating a setting (adding "-no" prefix), and configuring/negating
+// settings in a network section (adding "main." or "test." prefixes).
+//
+// - Combining settings from command line arguments and a config file.
+//
+// - Combining SoftSet and ForceSet calls.
+//
+// - Testing "main" and "test" network values to make sure settings from network
+// sections are applied and to check for mainnet-specific behaviors like
+// inheriting settings from the default section.
+//
+// - Testing network-specific settings like "-wallet", that may be ignored
+// outside a network section, and non-network specific settings like "-server"
+// that aren't sensitive to the network.
+//
+struct SettingsMergeTestingSetup : 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 { SET = 0, NEGATE, SECTION_SET, SECTION_NEGATE, END };
+ using ActionList = Action[MAX_ACTIONS];
+
+ //! Enumerate all possible test configurations.
+ template <typename Fn>
+ void ForEachMergeSetup(Fn&& fn)
+ {
+ ForEachActionList([&](const ActionList& arg_actions) {
+ ForEachActionList([&](const ActionList& conf_actions) {
+ for (bool soft_set : {false, true}) {
+ for (bool force_set : {false, true}) {
+ for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) {
+ for (const std::string& network : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) {
+ for (bool net_specific : {false, true}) {
+ fn(arg_actions, conf_actions, soft_set, force_set, section, network, net_specific);
+ }
+ }
+ }
+ }
+ }
+ });
+ });
+ }
+
+ //! Enumerate interesting combinations of actions.
+ template <typename Fn>
+ void ForEachActionList(Fn&& fn)
+ {
+ ActionList actions = {SET};
+ for (bool done = false; !done;) {
+ int prev_action = -1;
+ bool skip_actions = false;
+ for (Action action : actions) {
+ if ((prev_action == END && action != END) || (prev_action != END && action == prev_action)) {
+ // To cut down list of enumerated settings, skip enumerating
+ // settings with ignored actions after an END, and settings that
+ // repeat the same action twice in a row.
+ skip_actions = true;
+ break;
+ }
+ prev_action = action;
+ }
+ if (!skip_actions) fn(actions);
+ done = true;
+ for (Action& action : actions) {
+ action = Action(action < END ? action + 1 : 0);
+ if (action) {
+ done = false;
+ break;
+ }
+ }
+ }
+ }
+
+ //! Translate actions into a list of <key>=<value> setting strings.
+ std::vector<std::string> GetValues(const ActionList& actions,
+ const std::string& section,
+ const std::string& name,
+ const std::string& value_prefix)
+ {
+ std::vector<std::string> values;
+ int suffix = 0;
+ for (Action action : actions) {
+ if (action == END) break;
+ std::string prefix;
+ if (action == SECTION_SET || action == SECTION_NEGATE) prefix = section + ".";
+ if (action == SET || action == SECTION_SET) {
+ for (int i = 0; i < 2; ++i) {
+ values.push_back(prefix + name + "=" + value_prefix + std::to_string(++suffix));
+ }
+ }
+ if (action == NEGATE || action == SECTION_NEGATE) {
+ values.push_back(prefix + "no" + name + "=1");
+ }
+ }
+ return values;
+ }
+};
+
+// 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 below).
+BOOST_FIXTURE_TEST_CASE(util_SettingsMerge, SettingsMergeTestingSetup)
+{
+ 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");
+ }
+
+ ForEachMergeSetup([&](const ActionList& arg_actions, const ActionList& conf_actions, bool soft_set, bool force_set,
+ const std::string& section, const std::string& network, bool net_specific) {
+ TestArgsManager parser;
+ LOCK(parser.cs_args);
+
+ std::string desc = "net=";
+ desc += network;
+ parser.m_network = network;
+
+ const std::string& name = net_specific ? "server" : "wallet";
+ const std::string key = "-" + name;
+ parser.AddArg(key, name, false, OptionsCategory::OPTIONS);
+ if (net_specific) parser.SetNetworkOnlyArg(key);
+
+ auto args = GetValues(arg_actions, section, name, "a");
+ std::vector<const char*> argv = {"ignored"};
+ for (auto& arg : args) {
+ arg.insert(0, "-");
+ desc += " ";
+ desc += arg;
+ argv.push_back(arg.c_str());
+ }
+ std::string error;
+ BOOST_CHECK(parser.ParseParameters(argv.size(), argv.data(), error));
+ BOOST_CHECK_EQUAL(error, "");
+
+ std::string conf;
+ for (auto& conf_val : GetValues(conf_actions, section, name, "c")) {
+ desc += " ";
+ desc += conf_val;
+ conf += conf_val;
+ conf += "\n";
+ }
+ std::istringstream conf_stream(conf);
+ BOOST_CHECK(parser.ReadConfigStream(conf_stream, "filepath", error));
+ BOOST_CHECK_EQUAL(error, "");
+
+ if (soft_set) {
+ desc += " soft";
+ parser.SoftSetArg(key, "soft1");
+ parser.SoftSetArg(key, "soft2");
+ }
+
+ if (force_set) {
+ desc += " force";
+ parser.ForceSetArg(key, "force1");
+ parser.ForceSetArg(key, "force2");
+ }
+
+ desc += " || ";
+
+ if (!parser.IsArgSet(key)) {
+ desc += "unset";
+ BOOST_CHECK(!parser.IsArgNegated(key));
+ BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "default");
+ BOOST_CHECK(parser.GetArgs(key).empty());
+ } else if (parser.IsArgNegated(key)) {
+ desc += "negated";
+ BOOST_CHECK_EQUAL(parser.GetArg(key, "default"), "0");
+ BOOST_CHECK(parser.GetArgs(key).empty());
+ } else {
+ desc += parser.GetArg(key, "default");
+ desc += " |";
+ for (const auto& arg : parser.GetArgs(key)) {
+ desc += " ";
+ desc += arg;
+ }
+ }
+
+ std::set<std::string> ignored = parser.GetUnsuitableSectionOnlyArgs();
+ if (!ignored.empty()) {
+ desc += " | ignored";
+ for (const auto& arg : ignored) {
+ desc += " ";
+ desc += arg;
+ }
+ }
+
+ 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=util_tests/util_SettingsMerge
+ //
+ // And verify diff against previous results to make sure the changes are expected.
+ //
+ // Results file is formatted like:
+ //
+ // <input> || <IsArgSet/IsArgNegated/GetArg output> | <GetArgs output> | <GetUnsuitable output>
+ BOOST_CHECK_EQUAL(out_sha_hex, "80964e17fbd3c5569d3c824d032e28e2d319ef57494735b0e76eb7aad9957f2c");
+}
+
BOOST_AUTO_TEST_CASE(util_FormatMoney)
{
BOOST_CHECK_EQUAL(FormatMoney(0), "0.00");
diff --git a/src/test/util_threadnames_tests.cpp b/src/test/util_threadnames_tests.cpp
new file mode 100644
index 0000000000..71c0168ca3
--- /dev/null
+++ b/src/test/util_threadnames_tests.cpp
@@ -0,0 +1,73 @@
+// Copyright (c) 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.
+
+#include <util/threadnames.h>
+#include <test/setup_common.h>
+
+#include <thread>
+#include <vector>
+#include <set>
+#include <mutex>
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(util_threadnames_tests, BasicTestingSetup)
+
+const std::string TEST_THREAD_NAME_BASE = "test_thread.";
+
+/**
+ * Run a bunch of threads to all call util::ThreadRename.
+ *
+ * @return the set of name each thread has after attempted renaming.
+ */
+std::set<std::string> RenameEnMasse(int num_threads)
+{
+ std::vector<std::thread> threads;
+ std::set<std::string> names;
+ std::mutex lock;
+
+ auto RenameThisThread = [&](int i) {
+ util::ThreadRename(TEST_THREAD_NAME_BASE + std::to_string(i));
+ std::lock_guard<std::mutex> guard(lock);
+ names.insert(util::ThreadGetInternalName());
+ };
+
+ for (int i = 0; i < num_threads; ++i) {
+ threads.push_back(std::thread(RenameThisThread, i));
+ }
+
+ for (std::thread& thread : threads) thread.join();
+
+ return names;
+}
+
+/**
+ * Rename a bunch of threads with the same basename (expect_multiple=true), ensuring suffixes are
+ * applied properly.
+ */
+BOOST_AUTO_TEST_CASE(util_threadnames_test_rename_threaded)
+{
+ BOOST_CHECK_EQUAL(util::ThreadGetInternalName(), "");
+
+#if !defined(HAVE_THREAD_LOCAL)
+ // This test doesn't apply to platforms where we don't have thread_local.
+ return;
+#endif
+
+ std::set<std::string> names = RenameEnMasse(100);
+
+ BOOST_CHECK_EQUAL(names.size(), 100);
+
+ // Names "test_thread.[n]" should exist for n = [0, 99]
+ for (int i = 0; i < 100; ++i) {
+ BOOST_CHECK(names.find(TEST_THREAD_NAME_BASE + std::to_string(i)) != names.end());
+ }
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 44432cd0a1..4d54aa9b2c 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018 The Bitcoin Core developers
+// Copyright (c) 2018-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.
@@ -10,7 +10,7 @@
#include <miner.h>
#include <pow.h>
#include <random.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <validation.h>
#include <validationinterface.h>
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index 8d06ecd3a9..101025d31e 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/test_bitcoin.h>
+#include <test/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 ca3196454a..38d91b6647 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -1,10 +1,10 @@
-// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Copyright (c) 2014-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 <chain.h>
#include <versionbits.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <chainparams.h>
#include <validation.h>
#include <consensus/params.h>
diff --git a/src/txdb.h b/src/txdb.h
index d5a4bfaeb1..c4ece11503 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -37,6 +37,8 @@ static const int64_t nMaxBlockDBCache = 2;
// Unlike for the UTXO database, for the txindex scenario the leveldb cache make
// a meaningful difference: https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991
static const int64_t nMaxTxIndexCache = 1024;
+//! Max memory allocated to all block filter index caches combined in MiB.
+static const int64_t max_filter_index_cache = 1024;
//! Max memory allocated to coin DB specific cache (MiB)
static const int64_t nMaxCoinsDBCache = 8;
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 8a414f5dec..90b28227a0 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -11,6 +11,7 @@
#include <validation.h>
#include <policy/policy.h>
#include <policy/fees.h>
+#include <policy/settings.h>
#include <reverse_iterator.h>
#include <streams.h>
#include <timedata.h>
diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp
index 16ab24686b..31a95486d7 100644
--- a/src/ui_interface.cpp
+++ b/src/ui_interface.cpp
@@ -28,10 +28,6 @@ struct UISignals {
boost::signals2::connection CClientUIInterface::signal_name##_connect(std::function<signal_name##Sig> fn) \
{ \
return g_ui_signals.signal_name.connect(fn); \
- } \
- void CClientUIInterface::signal_name##_disconnect(std::function<signal_name##Sig> fn) \
- { \
- return g_ui_signals.signal_name.disconnect(&fn); \
}
ADD_SIGNALS_IMPL_WRAPPER(ThreadSafeMessageBox);
@@ -69,13 +65,3 @@ void InitWarning(const std::string& str)
{
uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING);
}
-
-std::string AmountHighWarn(const std::string& optname)
-{
- return strprintf(_("%s is set very high!"), optname);
-}
-
-std::string AmountErrMsg(const char* const optname, const std::string& strValue)
-{
- return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
-}
diff --git a/src/ui_interface.h b/src/ui_interface.h
index f1aebce3bb..d408f6f889 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -81,8 +81,7 @@ public:
#define ADD_SIGNALS_DECL_WRAPPER(signal_name, rtype, ...) \
rtype signal_name(__VA_ARGS__); \
using signal_name##Sig = rtype(__VA_ARGS__); \
- boost::signals2::connection signal_name##_connect(std::function<signal_name##Sig> fn); \
- void signal_name##_disconnect(std::function<signal_name##Sig> fn);
+ boost::signals2::connection signal_name##_connect(std::function<signal_name##Sig> fn);
/** Show message box. */
ADD_SIGNALS_DECL_WRAPPER(ThreadSafeMessageBox, bool, const std::string& message, const std::string& caption, unsigned int style);
@@ -129,10 +128,6 @@ void InitWarning(const std::string& str);
/** Show error message **/
bool InitError(const std::string& str);
-std::string AmountHighWarn(const std::string& optname);
-
-std::string AmountErrMsg(const char* const optname, const std::string& strValue);
-
extern CClientUIInterface uiInterface;
#endif // BITCOIN_UI_INTERFACE_H
diff --git a/src/util/error.cpp b/src/util/error.cpp
new file mode 100644
index 0000000000..68ffd8b046
--- /dev/null
+++ b/src/util/error.cpp
@@ -0,0 +1,43 @@
+// Copyright (c) 2010-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.
+
+#include <util/error.h>
+
+#include <util/system.h>
+
+std::string TransactionErrorString(const TransactionError err)
+{
+ switch (err) {
+ case TransactionError::OK:
+ return "No error";
+ case TransactionError::MISSING_INPUTS:
+ return "Missing inputs";
+ case TransactionError::ALREADY_IN_CHAIN:
+ return "Transaction already in block chain";
+ case TransactionError::P2P_DISABLED:
+ return "Peer-to-peer functionality missing or disabled";
+ case TransactionError::MEMPOOL_REJECTED:
+ return "Transaction rejected by AcceptToMemoryPool";
+ case TransactionError::MEMPOOL_ERROR:
+ return "AcceptToMemoryPool failed";
+ case TransactionError::INVALID_PSBT:
+ return "PSBT is not sane";
+ case TransactionError::PSBT_MISMATCH:
+ return "PSBTs not compatible (different transactions)";
+ case TransactionError::SIGHASH_MISMATCH:
+ return "Specified sighash value does not match existing value";
+ // no default case, so the compiler can warn about missing cases
+ }
+ assert(false);
+}
+
+std::string AmountHighWarn(const std::string& optname)
+{
+ return strprintf(_("%s is set very high!"), optname);
+}
+
+std::string AmountErrMsg(const char* const optname, const std::string& strValue)
+{
+ return strprintf(_("Invalid amount for -%s=<amount>: '%s'"), optname, strValue);
+}
diff --git a/src/util/error.h b/src/util/error.h
new file mode 100644
index 0000000000..d93309551b
--- /dev/null
+++ b/src/util/error.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2010-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_UTIL_ERROR_H
+#define BITCOIN_UTIL_ERROR_H
+
+/**
+ * util/error.h is a common place for definitions of simple error types and
+ * string functions. Types and functions defined here should not require any
+ * outside dependencies.
+ *
+ * Error types defined here can be used in different parts of the bitcoin
+ * codebase, to avoid the need to write boilerplate code catching and
+ * translating errors passed across wallet/node/rpc/gui code boundaries.
+ */
+
+#include <string>
+
+enum class TransactionError {
+ OK, //!< No error
+ MISSING_INPUTS,
+ ALREADY_IN_CHAIN,
+ P2P_DISABLED,
+ MEMPOOL_REJECTED,
+ MEMPOOL_ERROR,
+ INVALID_PSBT,
+ PSBT_MISMATCH,
+ SIGHASH_MISMATCH,
+};
+
+std::string TransactionErrorString(const TransactionError error);
+
+std::string AmountHighWarn(const std::string& optname);
+
+std::string AmountErrMsg(const char* const optname, const std::string& strValue);
+
+#endif // BITCOIN_UTIL_ERROR_H
diff --git a/src/util/fees.cpp b/src/util/fees.cpp
new file mode 100644
index 0000000000..5fdaa1284c
--- /dev/null
+++ b/src/util/fees.cpp
@@ -0,0 +1,42 @@
+// 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.
+
+#include <policy/fees.h>
+
+#include <string>
+
+std::string StringForFeeReason(FeeReason reason) {
+ static const std::map<FeeReason, std::string> fee_reason_strings = {
+ {FeeReason::NONE, "None"},
+ {FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"},
+ {FeeReason::FULL_ESTIMATE, "Target 85% Threshold"},
+ {FeeReason::DOUBLE_ESTIMATE, "Double Target 95% Threshold"},
+ {FeeReason::CONSERVATIVE, "Conservative Double Target longer horizon"},
+ {FeeReason::MEMPOOL_MIN, "Mempool Min Fee"},
+ {FeeReason::PAYTXFEE, "PayTxFee set"},
+ {FeeReason::FALLBACK, "Fallback fee"},
+ {FeeReason::REQUIRED, "Minimum Required Fee"},
+ {FeeReason::MAXTXFEE, "MaxTxFee limit"}
+ };
+ auto reason_string = fee_reason_strings.find(reason);
+
+ if (reason_string == fee_reason_strings.end()) return "Unknown";
+
+ return reason_string->second;
+}
+
+bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
+ static const std::map<std::string, FeeEstimateMode> fee_modes = {
+ {"UNSET", FeeEstimateMode::UNSET},
+ {"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
+ {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
+ };
+ auto mode = fee_modes.find(mode_string);
+
+ if (mode == fee_modes.end()) return false;
+
+ fee_estimate_mode = mode->second;
+ return true;
+}
diff --git a/src/util/fees.h b/src/util/fees.h
new file mode 100644
index 0000000000..fc355ce9c2
--- /dev/null
+++ b/src/util/fees.h
@@ -0,0 +1,16 @@
+// 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_UTIL_FEES_H
+#define BITCOIN_UTIL_FEES_H
+
+#include <string>
+
+enum class FeeEstimateMode;
+enum class FeeReason;
+
+bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
+std::string StringForFeeReason(FeeReason reason);
+
+#endif // BITCOIN_UTIL_FEES_H
diff --git a/src/util/rbf.cpp b/src/util/rbf.cpp
new file mode 100644
index 0000000000..d520a9606d
--- /dev/null
+++ b/src/util/rbf.cpp
@@ -0,0 +1,17 @@
+// Copyright (c) 2016-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.
+
+#include <util/rbf.h>
+
+#include <primitives/transaction.h>
+
+bool SignalsOptInRBF(const CTransaction &tx)
+{
+ for (const CTxIn &txin : tx.vin) {
+ if (txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/util/rbf.h b/src/util/rbf.h
new file mode 100644
index 0000000000..d3ef110628
--- /dev/null
+++ b/src/util/rbf.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2016-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_UTIL_RBF_H
+#define BITCOIN_UTIL_RBF_H
+
+#include <cstdint>
+
+class CTransaction;
+
+static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
+
+// Check whether the sequence numbers on this transaction are signaling
+// opt-in to replace-by-fee, according to BIP 125
+bool SignalsOptInRBF(const CTransaction &tx);
+
+#endif // BITCOIN_UTIL_RBF_H
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index cf77044094..7c4364a082 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -12,6 +12,7 @@
#include <attributes.h>
#include <cstdint>
+#include <iterator>
#include <string>
#include <vector>
@@ -121,28 +122,25 @@ NODISCARD bool ParseUInt64(const std::string& str, uint64_t *out);
NODISCARD bool ParseDouble(const std::string& str, double *out);
template<typename T>
-std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
+std::string HexStr(const T itbegin, const T itend)
{
std::string rv;
static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- rv.reserve((itend-itbegin)*3);
+ rv.reserve(std::distance(itbegin, itend) * 2);
for(T it = itbegin; it < itend; ++it)
{
unsigned char val = (unsigned char)(*it);
- if(fSpaces && it != itbegin)
- rv.push_back(' ');
rv.push_back(hexmap[val>>4]);
rv.push_back(hexmap[val&15]);
}
-
return rv;
}
template<typename T>
-inline std::string HexStr(const T& vch, bool fSpaces=false)
+inline std::string HexStr(const T& vch)
{
- return HexStr(vch.begin(), vch.end(), fSpaces);
+ return HexStr(vch.begin(), vch.end());
}
/**
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 9594dd81bf..efd35bed55 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -60,10 +60,6 @@
#include <shlobj.h>
#endif
-#ifdef HAVE_SYS_PRCTL_H
-#include <sys/prctl.h>
-#endif
-
#ifdef HAVE_MALLOPT_ARENA_MAX
#include <malloc.h>
#endif
@@ -1137,22 +1133,6 @@ void runCommand(const std::string& strCommand)
LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
}
-void RenameThread(const char* name)
-{
-#if defined(PR_SET_NAME)
- // Only the first 15 characters are used (16 - NUL terminator)
- ::prctl(PR_SET_NAME, name, 0, 0, 0);
-#elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
- pthread_set_name_np(pthread_self(), name);
-
-#elif defined(MAC_OSX)
- pthread_setname_np(name);
-#else
- // Prevent warnings for unused parameters...
- (void)name;
-#endif
-}
-
void SetupEnvironment()
{
#ifdef HAVE_MALLOPT_ARENA_MAX
diff --git a/src/util/system.h b/src/util/system.h
index 54eb88e261..1a83cb67b1 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -20,6 +20,7 @@
#include <fs.h>
#include <logging.h>
#include <sync.h>
+#include <util/threadnames.h>
#include <tinyformat.h>
#include <util/memory.h>
#include <util/time.h>
@@ -325,15 +326,12 @@ std::string HelpMessageOpt(const std::string& option, const std::string& message
*/
int GetNumCores();
-void RenameThread(const char* name);
-
/**
* .. and a wrapper that just calls func once
*/
template <typename Callable> void TraceThread(const char* name, Callable func)
{
- std::string s = strprintf("bitcoin-%s", name);
- RenameThread(s.c_str());
+ util::ThreadRename(name);
try
{
LogPrintf("%s thread start\n", name);
diff --git a/src/util/threadnames.cpp b/src/util/threadnames.cpp
new file mode 100644
index 0000000000..7b0d744aec
--- /dev/null
+++ b/src/util/threadnames.cpp
@@ -0,0 +1,57 @@
+// Copyright (c) 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.
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <atomic>
+#include <thread>
+
+#include <util/threadnames.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h> // For prctl, PR_SET_NAME, PR_GET_NAME
+#endif
+
+//! Set the thread's name at the process level. Does not affect the
+//! internal name.
+static void SetThreadName(const char* name)
+{
+#if defined(PR_SET_NAME)
+ // Only the first 15 characters are used (16 - NUL terminator)
+ ::prctl(PR_SET_NAME, name, 0, 0, 0);
+#elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
+ pthread_set_name_np(pthread_self(), name);
+#elif defined(MAC_OSX)
+ pthread_setname_np(name);
+#else
+ // Prevent warnings for unused parameters...
+ (void)name;
+#endif
+}
+
+// If we have thread_local, just keep thread ID and name in a thread_local
+// global.
+#if defined(HAVE_THREAD_LOCAL)
+
+static thread_local std::string g_thread_name;
+const std::string& util::ThreadGetInternalName() { return g_thread_name; }
+//! Set the in-memory internal name for this thread. Does not affect the process
+//! name.
+static void SetInternalName(std::string name) { g_thread_name = std::move(name); }
+
+// Without thread_local available, don't handle internal name at all.
+#else
+
+static const std::string empty_string;
+const std::string& util::ThreadGetInternalName() { return empty_string; }
+static void SetInternalName(std::string name) { }
+#endif
+
+void util::ThreadRename(std::string&& name)
+{
+ SetThreadName(("bitcoin-" + name).c_str());
+ SetInternalName(std::move(name));
+}
diff --git a/src/util/threadnames.h b/src/util/threadnames.h
new file mode 100644
index 0000000000..aaf07b9bf8
--- /dev/null
+++ b/src/util/threadnames.h
@@ -0,0 +1,21 @@
+// Copyright (c) 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_UTIL_THREADNAMES_H
+#define BITCOIN_UTIL_THREADNAMES_H
+
+#include <string>
+
+namespace util {
+//! Rename a thread both in terms of an internal (in-memory) name as well
+//! as its system thread name.
+void ThreadRename(std::string&&);
+
+//! Get the thread's internal (in-memory) name; used e.g. for identification in
+//! logging.
+const std::string& ThreadGetInternalName();
+
+} // namespace util
+
+#endif // BITCOIN_UTIL_THREADNAMES_H
diff --git a/src/util/url.cpp b/src/util/url.cpp
new file mode 100644
index 0000000000..49eacbf2d0
--- /dev/null
+++ b/src/util/url.cpp
@@ -0,0 +1,21 @@
+// Copyright (c) 2015-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.
+
+#include <util/url.h>
+
+#include <event2/http.h>
+#include <stdlib.h>
+#include <string>
+
+std::string urlDecode(const std::string &urlEncoded) {
+ std::string res;
+ if (!urlEncoded.empty()) {
+ char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
+ if (decoded) {
+ res = std::string(decoded);
+ free(decoded);
+ }
+ }
+ return res;
+}
diff --git a/src/util/url.h b/src/util/url.h
new file mode 100644
index 0000000000..3d7315a338
--- /dev/null
+++ b/src/util/url.h
@@ -0,0 +1,12 @@
+// Copyright (c) 2015-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_UTIL_URL_H
+#define BITCOIN_UTIL_URL_H
+
+#include <string>
+
+std::string urlDecode(const std::string &urlEncoded);
+
+#endif // BITCOIN_UTIL_URL_H
diff --git a/src/util/validation.cpp b/src/util/validation.cpp
new file mode 100644
index 0000000000..fe1f5a277e
--- /dev/null
+++ b/src/util/validation.cpp
@@ -0,0 +1,20 @@
+// 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.
+
+#include <util/validation.h>
+
+#include <consensus/validation.h>
+#include <tinyformat.h>
+
+/** Convert CValidationState to a human-readable message for logging */
+std::string FormatStateMessage(const CValidationState &state)
+{
+ return strprintf("%s%s (code %i)",
+ state.GetRejectReason(),
+ state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(),
+ state.GetRejectCode());
+}
+
+const std::string strMessageMagic = "Bitcoin Signed Message:\n";
diff --git a/src/util/validation.h b/src/util/validation.h
new file mode 100644
index 0000000000..32559853ee
--- /dev/null
+++ b/src/util/validation.h
@@ -0,0 +1,18 @@
+// 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_UTIL_VALIDATION_H
+#define BITCOIN_UTIL_VALIDATION_H
+
+#include <string>
+
+class CValidationState;
+
+/** Convert CValidationState to a human-readable message for logging */
+std::string FormatStateMessage(const CValidationState &state);
+
+extern const std::string strMessageMagic;
+
+#endif // BITCOIN_UTIL_VALIDATION_H
diff --git a/src/validation.cpp b/src/validation.cpp
index 68dea5dc8d..1da79e09f6 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -8,10 +8,10 @@
#include <arith_uint256.h>
#include <chain.h>
#include <chainparams.h>
-#include <checkpoints.h>
#include <checkqueue.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
+#include <consensus/tx_check.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <cuckoocache.h>
@@ -21,6 +21,7 @@
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
+#include <policy/settings.h>
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
@@ -35,15 +36,19 @@
#include <txdb.h>
#include <txmempool.h>
#include <ui_interface.h>
+#include <uint256.h>
#include <undo.h>
-#include <util/system.h>
#include <util/moneystr.h>
+#include <util/rbf.h>
#include <util/strencodings.h>
+#include <util/system.h>
+#include <util/validation.h>
#include <validationinterface.h>
#include <warnings.h>
#include <future>
#include <sstream>
+#include <string>
#include <boost/algorithm/string/replace.hpp>
#include <boost/thread.hpp>
@@ -178,7 +183,7 @@ public:
// Manual block validity manipulation:
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
- bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex);
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
@@ -207,8 +212,10 @@ private:
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ //! Mark a block as not having block data
+ void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
} g_chainstate;
/**
@@ -234,7 +241,6 @@ std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false);
bool fHavePruned = false;
bool fPruneMode = false;
-bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
bool fRequireStandard = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
@@ -247,7 +253,6 @@ uint256 hashAssumeValid;
arith_uint256 nMinimumChainWork;
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
-CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
CBlockPolicyEstimator feeEstimator;
CTxMemPool mempool(&feeEstimator);
@@ -255,8 +260,6 @@ CTxMemPool mempool(&feeEstimator);
/** Constant stuff for coinbase transactions we create: */
CScript COINBASE_FLAGS;
-const std::string strMessageMagic = "Bitcoin Signed Message:\n";
-
// Internal stuff
namespace {
CBlockIndex *&pindexBestInvalid = g_chainstate.pindexBestInvalid;
@@ -458,15 +461,6 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
pcoinsTip->Uncache(removed);
}
-/** Convert CValidationState to a human-readable message for logging */
-std::string FormatStateMessage(const CValidationState &state)
-{
- return strprintf("%s%s (code %i)",
- state.GetRejectReason(),
- state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(),
- state.GetRejectCode());
-}
-
static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
@@ -567,6 +561,13 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata);
}
+/**
+ * @param[out] coins_to_uncache Return any outpoints which were not previously present in the
+ * coins cache, but were added as a result of validating the tx
+ * for mempool acceptance. This allows the caller to optionally
+ * remove the cache additions if the associated transaction ends
+ * up being rejected by the mempool.
+ */
static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -662,6 +663,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout);
}
+
+ // Note: this call may add txin.prevout to the coins cache
+ // (pcoinsTip.cacheCoins) by way of FetchCoin(). It should be removed
+ // later (via coins_to_uncache) if this tx turns out to be invalid.
if (!view.HaveCoin(txin.prevout)) {
// Are inputs missing because we already have the tx?
for (size_t out = 0; out < tx.vout.size(); out++) {
@@ -983,6 +988,11 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
std::vector<COutPoint> coins_to_uncache;
bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept);
if (!res) {
+ // Remove coins that were not present in the coins cache before calling ATMPW;
+ // this is to prevent memory DoS in case we receive a large number of
+ // invalid transactions that attempt to overrun the in-memory coins cache
+ // (`CCoinsViewCache::cacheCoins`).
+
for (const COutPoint& hashTx : coins_to_uncache)
pcoinsTip->Uncache(hashTx);
}
@@ -1452,9 +1462,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
return true;
}
-namespace {
-
-bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
+static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
{
// Open history file to append
CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION);
@@ -1481,7 +1489,7 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint25
return true;
}
-static bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex *pindex)
+bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
{
FlatFilePos pos = pindex->GetUndoPos();
if (pos.IsNull()) {
@@ -1530,8 +1538,6 @@ static bool AbortNode(CValidationState& state, const std::string& strMessage, co
return state.Error(strMessage);
}
-} // namespace
-
/**
* Restore the UTXO in a Coin at a given COutPoint
* @param undo The Coin to be restored.
@@ -1663,8 +1669,8 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState&
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
-void ThreadScriptCheck() {
- RenameThread("bitcoin-scriptch");
+void ThreadScriptCheck(int worker_num) {
+ util::ThreadRename(strprintf("scriptch.%i", worker_num));
scriptcheckqueue.Thread();
}
@@ -2625,6 +2631,14 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
}
}
+static void LimitValidationInterfaceQueue() {
+ AssertLockNotHeld(cs_main);
+
+ if (GetMainSignals().CallbacksPending() > 10) {
+ SyncWithValidationInterfaceQueue();
+ }
+}
+
/**
* Make the best chain active, in multiple steps. The result is either failure
* or an activated best chain. pblock is either nullptr or a pointer to a block
@@ -2653,15 +2667,13 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
do {
boost::this_thread::interruption_point();
- if (GetMainSignals().CallbacksPending() > 10) {
- // Block until the validation queue drains. This should largely
- // never happen in normal operation, however may happen during
- // reindex, causing memory blowup if we run too far ahead.
- // Note that if a validationinterface callback ends up calling
- // ActivateBestChain this may lead to a deadlock! We should
- // probably have a DEBUG_LOCKORDER test for this in the future.
- SyncWithValidationInterfaceQueue();
- }
+ // Block until the validation queue drains. This should largely
+ // never happen in normal operation, however may happen during
+ // reindex, causing memory blowup if we run too far ahead.
+ // Note that if a validationinterface callback ends up calling
+ // ActivateBestChain this may lead to a deadlock! We should
+ // probably have a DEBUG_LOCKORDER test for this in the future.
+ LimitValidationInterfaceQueue();
{
LOCK(cs_main);
@@ -2772,64 +2784,85 @@ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIn
bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
{
- AssertLockHeld(cs_main);
+ CBlockIndex* to_mark_failed = pindex;
+ bool pindex_was_in_chain = false;
+ int disconnected = 0;
- // We first disconnect backwards and then mark the blocks as invalid.
- // This prevents a case where pruned nodes may fail to invalidateblock
- // and be left unable to start as they have no tip candidates (as there
- // are no blocks that meet the "have data and are not invalid per
- // nStatus" criteria for inclusion in setBlockIndexCandidates).
+ // Disconnect (descendants of) pindex, and mark them invalid.
+ while (true) {
+ if (ShutdownRequested()) break;
- bool pindex_was_in_chain = false;
- CBlockIndex *invalid_walk_tip = chainActive.Tip();
+ // Make sure the queue of validation callbacks doesn't grow unboundedly.
+ LimitValidationInterfaceQueue();
- DisconnectedBlockTransactions disconnectpool;
- while (chainActive.Contains(pindex)) {
+ LOCK(cs_main);
+ if (!chainActive.Contains(pindex)) break;
pindex_was_in_chain = true;
+ CBlockIndex *invalid_walk_tip = chainActive.Tip();
+
// ActivateBestChain considers blocks already in chainActive
// unconditionally valid already, so force disconnect away from it.
- if (!DisconnectTip(state, chainparams, &disconnectpool)) {
- // It's probably hopeless to try to make the mempool consistent
- // here if DisconnectTip failed, but we can try.
- UpdateMempoolForReorg(disconnectpool, false);
- return false;
- }
- }
-
- // Now mark the blocks we just disconnected as descendants invalid
- // (note this may not be all descendants).
- while (pindex_was_in_chain && invalid_walk_tip != pindex) {
- invalid_walk_tip->nStatus |= BLOCK_FAILED_CHILD;
+ DisconnectedBlockTransactions disconnectpool;
+ bool ret = DisconnectTip(state, chainparams, &disconnectpool);
+ // DisconnectTip will add transactions to disconnectpool.
+ // Adjust the mempool to be consistent with the new tip, adding
+ // transactions back to the mempool if disconnecting was successful,
+ // and we're not doing a very deep invalidation (in which case
+ // keeping the mempool up to date is probably futile anyway).
+ UpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
+ if (!ret) return false;
+ assert(invalid_walk_tip->pprev == chainActive.Tip());
+
+ // We immediately mark the disconnected blocks as invalid.
+ // This prevents a case where pruned nodes may fail to invalidateblock
+ // and be left unable to start as they have no tip candidates (as there
+ // are no blocks that meet the "have data and are not invalid per
+ // nStatus" criteria for inclusion in setBlockIndexCandidates).
+ invalid_walk_tip->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(invalid_walk_tip);
setBlockIndexCandidates.erase(invalid_walk_tip);
- invalid_walk_tip = invalid_walk_tip->pprev;
- }
+ setBlockIndexCandidates.insert(invalid_walk_tip->pprev);
+ if (invalid_walk_tip->pprev == to_mark_failed && (to_mark_failed->nStatus & BLOCK_FAILED_VALID)) {
+ // We only want to mark the last disconnected block as BLOCK_FAILED_VALID; its children
+ // need to be BLOCK_FAILED_CHILD instead.
+ to_mark_failed->nStatus = (to_mark_failed->nStatus ^ BLOCK_FAILED_VALID) | BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(to_mark_failed);
+ }
- // Mark the block itself as invalid.
- pindex->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(pindex);
- setBlockIndexCandidates.erase(pindex);
- m_failed_blocks.insert(pindex);
+ // Track the last disconnected block, so we can correct its BLOCK_FAILED_CHILD status in future
+ // iterations, or, if it's the last one, call InvalidChainFound on it.
+ to_mark_failed = invalid_walk_tip;
+ }
- // DisconnectTip will add transactions to disconnectpool; try to add these
- // back to the mempool.
- UpdateMempoolForReorg(disconnectpool, true);
+ {
+ LOCK(cs_main);
+ if (chainActive.Contains(to_mark_failed)) {
+ // If the to-be-marked invalid block is in the active chain, something is interfering and we can't proceed.
+ return false;
+ }
- // The resulting new best tip may not be in setBlockIndexCandidates anymore, so
- // add it again.
- BlockMap::iterator it = mapBlockIndex.begin();
- while (it != mapBlockIndex.end()) {
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
- setBlockIndexCandidates.insert(it->second);
+ // Mark pindex (or the last disconnected block) as invalid, even when it never was in the main chain
+ to_mark_failed->nStatus |= BLOCK_FAILED_VALID;
+ setDirtyBlockIndex.insert(to_mark_failed);
+ setBlockIndexCandidates.erase(to_mark_failed);
+ m_failed_blocks.insert(to_mark_failed);
+
+ // The resulting new best tip may not be in setBlockIndexCandidates anymore, so
+ // add it again.
+ BlockMap::iterator it = mapBlockIndex.begin();
+ while (it != mapBlockIndex.end()) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
+ setBlockIndexCandidates.insert(it->second);
+ }
+ it++;
}
- it++;
- }
- InvalidChainFound(pindex);
+ InvalidChainFound(to_mark_failed);
+ }
// Only notify about a new block tip if the active chain was modified.
if (pindex_was_in_chain) {
- uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev);
+ uiInterface.NotifyBlockTip(IsInitialBlockDownload(), to_mark_failed->pprev);
}
return true;
}
@@ -3170,6 +3203,22 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
return commitment;
}
+//! Returns last CBlockIndex* that is a checkpoint
+static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data)
+{
+ const MapCheckpoints& checkpoints = data.mapCheckpoints;
+
+ for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
+ {
+ const uint256& hash = i.second;
+ CBlockIndex* pindex = LookupBlockIndex(hash);
+ if (pindex) {
+ return pindex;
+ }
+ }
+ return nullptr;
+}
+
/** Context-dependent validity checks.
* By "context", we mean only the previous block headers, but not the UTXO
* set; UTXO-related validity checks are done in ConnectBlock().
@@ -3194,7 +3243,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
// MapBlockIndex.
- CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(params.Checkpoints());
+ CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
}
@@ -4112,38 +4161,114 @@ bool ReplayBlocks(const CChainParams& params, CCoinsView* view) {
return g_chainstate.ReplayBlocks(params, view);
}
-bool CChainState::RewindBlockIndex(const CChainParams& params)
+//! Helper for CChainState::RewindBlockIndex
+void CChainState::EraseBlockData(CBlockIndex* index)
{
- LOCK(cs_main);
+ AssertLockHeld(cs_main);
+ assert(!chainActive.Contains(index)); // Make sure this block isn't active
+
+ // Reduce validity
+ index->nStatus = std::min<unsigned int>(index->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (index->nStatus & ~BLOCK_VALID_MASK);
+ // Remove have-data flags.
+ index->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
+ // Remove storage location.
+ index->nFile = 0;
+ index->nDataPos = 0;
+ index->nUndoPos = 0;
+ // Remove various other things
+ index->nTx = 0;
+ index->nChainTx = 0;
+ index->nSequenceId = 0;
+ // Make sure it gets written.
+ setDirtyBlockIndex.insert(index);
+ // Update indexes
+ setBlockIndexCandidates.erase(index);
+ std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> ret = mapBlocksUnlinked.equal_range(index->pprev);
+ while (ret.first != ret.second) {
+ if (ret.first->second == index) {
+ mapBlocksUnlinked.erase(ret.first++);
+ } else {
+ ++ret.first;
+ }
+ }
+ // Mark parent as eligible for main chain again
+ if (index->pprev && index->pprev->IsValid(BLOCK_VALID_TRANSACTIONS) && index->pprev->HaveTxsDownloaded()) {
+ setBlockIndexCandidates.insert(index->pprev);
+ }
+}
+bool CChainState::RewindBlockIndex(const CChainParams& params)
+{
// Note that during -reindex-chainstate we are called with an empty chainActive!
- int nHeight = 1;
- while (nHeight <= chainActive.Height()) {
- // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
- // blocks in ConnectBlock, we don't need to go back and
- // re-download/re-verify blocks from before segwit actually activated.
- if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
- break;
+ // First erase all post-segwit blocks without witness not in the main chain,
+ // as this can we done without costly DisconnectTip calls. Active
+ // blocks will be dealt with below (releasing cs_main in between).
+ {
+ LOCK(cs_main);
+ for (const auto& entry : mapBlockIndex) {
+ if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(entry.second)) {
+ EraseBlockData(entry.second);
+ }
}
- nHeight++;
}
+ // Find what height we need to reorganize to.
+ CBlockIndex *tip;
+ int nHeight = 1;
+ {
+ LOCK(cs_main);
+ while (nHeight <= chainActive.Height()) {
+ // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
+ // blocks in ConnectBlock, we don't need to go back and
+ // re-download/re-verify blocks from before segwit actually activated.
+ if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
+ break;
+ }
+ nHeight++;
+ }
+
+ tip = chainActive.Tip();
+ }
// nHeight is now the height of the first insufficiently-validated block, or tipheight + 1
+
CValidationState state;
- CBlockIndex* pindex = chainActive.Tip();
- while (chainActive.Height() >= nHeight) {
- if (fPruneMode && !(chainActive.Tip()->nStatus & BLOCK_HAVE_DATA)) {
- // If pruning, don't try rewinding past the HAVE_DATA point;
- // since older blocks can't be served anyway, there's
- // no need to walk further, and trying to DisconnectTip()
- // will fail (and require a needless reindex/redownload
- // of the blockchain).
- break;
- }
- if (!DisconnectTip(state, params, nullptr)) {
- return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", pindex->nHeight, FormatStateMessage(state));
+ // Loop until the tip is below nHeight, or we reach a pruned block.
+ while (!ShutdownRequested()) {
+ {
+ LOCK(cs_main);
+ // Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
+ assert(tip == chainActive.Tip());
+ if (tip == nullptr || tip->nHeight < nHeight) break;
+ if (fPruneMode && !(tip->nStatus & BLOCK_HAVE_DATA)) {
+ // If pruning, don't try rewinding past the HAVE_DATA point;
+ // since older blocks can't be served anyway, there's
+ // no need to walk further, and trying to DisconnectTip()
+ // will fail (and require a needless reindex/redownload
+ // of the blockchain).
+ break;
+ }
+
+ // Disconnect block
+ if (!DisconnectTip(state, params, nullptr)) {
+ return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", tip->nHeight, FormatStateMessage(state));
+ }
+
+ // Reduce validity flag and have-data flags.
+ // We do this after actual disconnecting, otherwise we'll end up writing the lack of data
+ // to disk before writing the chainstate, resulting in a failure to continue if interrupted.
+ // Note: If we encounter an insufficiently validated block that
+ // is on chainActive, it must be because we are a pruning node, and
+ // this block or some successor doesn't HAVE_DATA, so we were unable to
+ // rewind all the way. Blocks remaining on chainActive at this point
+ // must not have their validity reduced.
+ EraseBlockData(tip);
+
+ tip = tip->pprev;
}
+ // Make sure the queue of validation callbacks doesn't grow unboundedly.
+ LimitValidationInterfaceQueue();
+
// Occasionally flush state to disk.
if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
@@ -4151,53 +4276,15 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
}
}
- // Reduce validity flag and have-data flags.
- // We do this after actual disconnecting, otherwise we'll end up writing the lack of data
- // to disk before writing the chainstate, resulting in a failure to continue if interrupted.
- for (const auto& entry : mapBlockIndex) {
- CBlockIndex* pindexIter = entry.second;
-
- // Note: If we encounter an insufficiently validated block that
- // is on chainActive, it must be because we are a pruning node, and
- // this block or some successor doesn't HAVE_DATA, so we were unable to
- // rewind all the way. Blocks remaining on chainActive at this point
- // must not have their validity reduced.
- if (IsWitnessEnabled(pindexIter->pprev, params.GetConsensus()) && !(pindexIter->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(pindexIter)) {
- // Reduce validity
- pindexIter->nStatus = std::min<unsigned int>(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (pindexIter->nStatus & ~BLOCK_VALID_MASK);
- // Remove have-data flags.
- pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
- // Remove storage location.
- pindexIter->nFile = 0;
- pindexIter->nDataPos = 0;
- pindexIter->nUndoPos = 0;
- // Remove various other things
- pindexIter->nTx = 0;
- pindexIter->nChainTx = 0;
- pindexIter->nSequenceId = 0;
- // Make sure it gets written.
- setDirtyBlockIndex.insert(pindexIter);
- // Update indexes
- setBlockIndexCandidates.erase(pindexIter);
- std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> ret = mapBlocksUnlinked.equal_range(pindexIter->pprev);
- while (ret.first != ret.second) {
- if (ret.first->second == pindexIter) {
- mapBlocksUnlinked.erase(ret.first++);
- } else {
- ++ret.first;
- }
- }
- } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->HaveTxsDownloaded()) {
- setBlockIndexCandidates.insert(pindexIter);
- }
- }
-
- if (chainActive.Tip() != nullptr) {
- // We can't prune block index candidates based on our tip if we have
- // no tip due to chainActive being empty!
- PruneBlockIndexCandidates();
+ {
+ LOCK(cs_main);
+ if (chainActive.Tip() != nullptr) {
+ // We can't prune block index candidates based on our tip if we have
+ // no tip due to chainActive being empty!
+ PruneBlockIndexCandidates();
- CheckBlockIndex(params.GetConsensus());
+ CheckBlockIndex(params.GetConsensus());
+ }
}
return true;
diff --git a/src/validation.h b/src/validation.h
index 0ff6c619b5..7ab6adaf33 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -21,6 +21,7 @@
#include <versionbits.h>
#include <algorithm>
+#include <atomic>
#include <exception>
#include <map>
#include <memory>
@@ -30,10 +31,9 @@
#include <utility>
#include <vector>
-#include <atomic>
-
class CBlockIndex;
class CBlockTreeDB;
+class CBlockUndo;
class CChainParams;
class CCoinsViewDB;
class CInv;
@@ -53,12 +53,6 @@ static const bool DEFAULT_WHITELISTRELAY = true;
static const bool DEFAULT_WHITELISTFORCERELAY = false;
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
-//! -maxtxfee default
-static const CAmount DEFAULT_TRANSACTION_MAXFEE = COIN / 10;
-//! Discourage users to set fees higher than this amount (in satoshis) per kB
-static const CAmount HIGH_TX_FEE_PER_KB = COIN / 100;
-//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
-static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB;
/** Default for -limitancestorcount, max number of in-mempool ancestors */
static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25;
/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */
@@ -114,10 +108,9 @@ static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
/** Maximum age of our tip in seconds for us to be considered current for fee estimation */
static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60;
-/** Default for -permitbaremultisig */
-static const bool DEFAULT_PERMIT_BAREMULTISIG = true;
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
static const bool DEFAULT_TXINDEX = false;
+static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
/** Default for -persistmempool */
static const bool DEFAULT_PERSIST_MEMPOOL = true;
@@ -151,22 +144,18 @@ extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern BlockMap& mapBlockIndex GUARDED_BY(cs_main);
-extern const std::string strMessageMagic;
extern Mutex g_best_block_mutex;
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;
-extern bool fIsBareMultisigStd;
extern bool fRequireStandard;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
extern size_t nCoinCacheUsage;
/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */
extern CFeeRate minRelayTxFee;
-/** Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendrawtransaction) */
-extern CAmount maxTxFee;
/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
extern int64_t nMaxTipAge;
extern bool fEnableReplacement;
@@ -257,7 +246,7 @@ bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_m
/** Unload database information */
void UnloadBlockIndex();
/** Run an instance of the script checking thread */
-void ThreadScriptCheck();
+void ThreadScriptCheck(int worker_num);
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
bool IsInitialBlockDownload();
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
@@ -300,9 +289,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced,
bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Convert CValidationState to a human-readable message for logging */
-std::string FormatStateMessage(const CValidationState &state);
-
/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
@@ -390,6 +376,8 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start);
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start);
+bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex);
+
/** Functions for validating blocks and updating the block tree */
/** Context-independent validity checks */
@@ -442,7 +430,7 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main);
/** Mark a block as invalid. */
-bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex);
/** Remove invalidity status from a block and its descendants. */
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 70c274d20e..5d0ee1d1fc 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -25,7 +25,6 @@ struct ValidationInterfaceConnections {
boost::signals2::scoped_connection BlockDisconnected;
boost::signals2::scoped_connection TransactionRemovedFromMempool;
boost::signals2::scoped_connection ChainStateFlushed;
- boost::signals2::scoped_connection Broadcast;
boost::signals2::scoped_connection BlockChecked;
boost::signals2::scoped_connection NewPoWValidBlock;
};
@@ -37,7 +36,6 @@ struct MainSignalsInstance {
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
- boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
@@ -101,7 +99,6 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
conns.BlockDisconnected = g_signals.m_internals->BlockDisconnected.connect(std::bind(&CValidationInterface::BlockDisconnected, pwalletIn, std::placeholders::_1));
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.Broadcast = g_signals.m_internals->Broadcast.connect(std::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, std::placeholders::_1, std::placeholders::_2));
conns.BlockChecked = g_signals.m_internals->BlockChecked.connect(std::bind(&CValidationInterface::BlockChecked, pwalletIn, std::placeholders::_1, std::placeholders::_2));
conns.NewPoWValidBlock = g_signals.m_internals->NewPoWValidBlock.connect(std::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, std::placeholders::_1, std::placeholders::_2));
}
@@ -175,10 +172,6 @@ void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
});
}
-void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) {
- m_internals->Broadcast(nBestBlockTime, connman);
-}
-
void CMainSignals::BlockChecked(const CBlock& block, const CValidationState& state) {
m_internals->BlockChecked(block, state);
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index f0374e8e78..ea1b2e7e76 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -18,7 +18,6 @@ class CBlockIndex;
struct CBlockLocator;
class CBlockIndex;
class CConnman;
-class CReserveScript;
class CValidationInterface;
class CValidationState;
class uint256;
@@ -134,8 +133,6 @@ protected:
* Called on a background thread.
*/
virtual void ChainStateFlushed(const CBlockLocator &locator) {}
- /** Tells listeners to broadcast their data. */
- virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
/**
* Notifies listeners of a block validation result.
* If the provided CValidationState IsValid, the provided block
@@ -184,7 +181,6 @@ public:
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 ChainStateFlushed(const CBlockLocator &);
- void Broadcast(int64_t nBestBlockTime, CConnman* connman);
void BlockChecked(const CBlock&, const CValidationState&);
void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&);
};
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 48a924abfb..9257b272bc 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -36,6 +36,8 @@ public:
bool m_avoid_partial_spends;
//! Fee estimation mode to control arguments to estimateSmartFee
FeeEstimateMode m_fee_mode;
+ //! Minimum chain depth value for coin availability
+ int m_min_depth{0};
CCoinControl()
{
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 99d880daa0..6a326bfd97 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -84,6 +84,14 @@ bool IsWalletLoaded(const fs::path& wallet_path)
return database && database->IsDatabaseLoaded(database_filename);
}
+fs::path WalletDataFilePath(const fs::path& wallet_path)
+{
+ fs::path env_directory;
+ std::string database_filename;
+ SplitWalletPath(wallet_path, env_directory, database_filename);
+ return env_directory / database_filename;
+}
+
/**
* @param[in] wallet_path Path to wallet directory. Or (for backwards compatibility only) a path to a berkeley btree data file inside a wallet directory.
* @param[out] database_filename Filename of berkeley btree data file inside the wallet directory.
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 9df965305a..762fb83a2f 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -101,6 +101,9 @@ public:
/** Return whether a wallet database is currently loaded. */
bool IsWalletLoaded(const fs::path& wallet_path);
+/** Given a wallet directory path or legacy file path, return path to main data file in the wallet database. */
+fs::path WalletDataFilePath(const fs::path& wallet_path);
+
/** Get BerkeleyEnvironment and database filename given a wallet path. */
std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index a1c3a21d4b..78db4df5e5 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -14,7 +14,9 @@
#include <validation.h> //for mempool access
#include <txmempool.h>
#include <util/moneystr.h>
+#include <util/rbf.h>
#include <util/system.h>
+#include <util/validation.h>
#include <net.h>
//! Check whether transaction has descendant in wallet or mempool, or has been
@@ -73,9 +75,11 @@ bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid)
return res == feebumper::Result::OK;
}
-Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors,
- CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
+Result CreateTotalBumpTransaction(const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors,
+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
{
+ new_fee = total_fee;
+
auto locked_chain = wallet->chain().lock();
LOCK(wallet->cs_wallet);
errors.clear();
@@ -119,49 +123,33 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
// calculate the old fee and fee-rate
old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
CFeeRate nOldFeeRate(old_fee, txSize);
- CFeeRate nNewFeeRate;
// The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
// future proof against changes to network wide policy for incremental relay
// fee that our node may not be aware of.
+ CFeeRate nodeIncrementalRelayFee = wallet->chain().relayIncrementalFee();
CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
- if (::incrementalRelayFee > walletIncrementalRelayFee) {
- walletIncrementalRelayFee = ::incrementalRelayFee;
+ if (nodeIncrementalRelayFee > walletIncrementalRelayFee) {
+ walletIncrementalRelayFee = nodeIncrementalRelayFee;
}
- if (total_fee > 0) {
- CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize);
- if (total_fee < minTotalFee) {
- errors.push_back(strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)",
- FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize))));
- return Result::INVALID_PARAMETER;
- }
- CAmount requiredFee = GetRequiredFee(*wallet, maxNewTxSize);
- if (total_fee < requiredFee) {
- errors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)",
- FormatMoney(requiredFee)));
- return Result::INVALID_PARAMETER;
- }
- new_fee = total_fee;
- nNewFeeRate = CFeeRate(total_fee, maxNewTxSize);
- } else {
- new_fee = GetMinimumFee(*wallet, maxNewTxSize, coin_control, nullptr /* FeeCalculation */);
- nNewFeeRate = CFeeRate(new_fee, maxNewTxSize);
-
- // New fee rate must be at least old rate + minimum incremental relay rate
- // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized
- // in that unit (fee per kb).
- // However, nOldFeeRate is a calculated value from the tx fee/size, so
- // add 1 satoshi to the result, because it may have been rounded down.
- if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) {
- nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK());
- new_fee = nNewFeeRate.GetFee(maxNewTxSize);
- }
+ CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + nodeIncrementalRelayFee.GetFee(maxNewTxSize);
+ if (total_fee < minTotalFee) {
+ errors.push_back(strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)",
+ FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(nodeIncrementalRelayFee.GetFee(maxNewTxSize))));
+ return Result::INVALID_PARAMETER;
+ }
+ CAmount requiredFee = GetRequiredFee(*wallet, maxNewTxSize);
+ if (total_fee < requiredFee) {
+ errors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)",
+ FormatMoney(requiredFee)));
+ return Result::INVALID_PARAMETER;
}
// Check that in all cases the new fee doesn't violate maxTxFee
- if (new_fee > maxTxFee) {
- errors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)",
- FormatMoney(new_fee), FormatMoney(maxTxFee)));
+ const CAmount max_tx_fee = wallet->m_default_max_tx_fee;
+ if (new_fee > max_tx_fee) {
+ errors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than -maxtxfee %s)",
+ FormatMoney(new_fee), FormatMoney(max_tx_fee)));
return Result::WALLET_ERROR;
}
@@ -170,15 +158,15 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
// This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
// in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
// moment earlier. In this case, we report an error to the user, who may use total_fee to make an adjustment.
- CFeeRate minMempoolFeeRate = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ CFeeRate minMempoolFeeRate = wallet->chain().mempoolMinFee();
+ CFeeRate nNewFeeRate = CFeeRate(total_fee, maxNewTxSize);
if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
errors.push_back(strprintf(
"New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- "
- "the totalFee value should be at least %s or the settxfee value should be at least %s to add transaction",
+ "the totalFee value should be at least %s to add transaction",
FormatMoney(nNewFeeRate.GetFeePerK()),
FormatMoney(minMempoolFeeRate.GetFeePerK()),
- FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)),
- FormatMoney(minMempoolFeeRate.GetFeePerK())));
+ FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize))));
return Result::WALLET_ERROR;
}
@@ -208,6 +196,109 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
}
}
+ return Result::OK;
+}
+
+
+Result CreateRateBumpTransaction(CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, std::vector<std::string>& errors,
+ CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
+{
+ // We are going to modify coin control later, copy to re-use
+ CCoinControl new_coin_control(coin_control);
+
+ auto locked_chain = wallet->chain().lock();
+ LOCK(wallet->cs_wallet);
+ errors.clear();
+ auto it = wallet->mapWallet.find(txid);
+ if (it == wallet->mapWallet.end()) {
+ errors.push_back("Invalid or non-wallet transaction id");
+ return Result::INVALID_ADDRESS_OR_KEY;
+ }
+ const CWalletTx& wtx = it->second;
+
+ Result result = PreconditionChecks(*locked_chain, wallet, wtx, errors);
+ if (result != Result::OK) {
+ return result;
+ }
+
+ // Fill in recipients(and preserve a single change key if there is one)
+ std::vector<CRecipient> recipients;
+ for (const auto& output : wtx.tx->vout) {
+ if (!wallet->IsChange(output)) {
+ CRecipient recipient = {output.scriptPubKey, output.nValue, false};
+ recipients.push_back(recipient);
+ } else {
+ CTxDestination change_dest;
+ ExtractDestination(output.scriptPubKey, change_dest);
+ new_coin_control.destChange = change_dest;
+ }
+ }
+
+ // Get the fee rate of the original transaction. This is calculated from
+ // the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the
+ // result.
+ old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
+ int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
+ // Feerate of thing we are bumping
+ CFeeRate feerate(old_fee, txSize);
+ feerate += CFeeRate(1);
+
+ // The node has a configurable incremental relay fee. Increment the fee by
+ // the minimum of that and the wallet's conservative
+ // WALLET_INCREMENTAL_RELAY_FEE value to future proof against changes to
+ // network wide policy for incremental relay fee that our node may not be
+ // aware of. This ensures we're over the over the required relay fee rate
+ // (BIP 125 rule 4). The replacement tx will be at least as large as the
+ // original tx, so the total fee will be greater (BIP 125 rule 3)
+ CFeeRate node_incremental_relay_fee = wallet->chain().relayIncrementalFee();
+ CFeeRate wallet_incremental_relay_fee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
+ feerate += std::max(node_incremental_relay_fee, wallet_incremental_relay_fee);
+
+ // Fee rate must also be at least the wallet's GetMinimumFeeRate
+ CFeeRate min_feerate(GetMinimumFeeRate(*wallet, new_coin_control, /* feeCalc */ nullptr));
+
+ // Set the required fee rate for the replacement transaction in coin control.
+ new_coin_control.m_feerate = std::max(feerate, min_feerate);
+
+ // Fill in required inputs we are double-spending(all of them)
+ // N.B.: bip125 doesn't require all the inputs in the replaced transaction to be
+ // used in the replacement transaction, but it's very important for wallets to make
+ // sure that happens. If not, it would be possible to bump a transaction A twice to
+ // A2 and A3 where A2 and A3 don't conflict (or alternatively bump A to A2 and A2
+ // to A3 where A and A3 don't conflict). If both later get confirmed then the sender
+ // has accidentally double paid.
+ for (const auto& inputs : wtx.tx->vin) {
+ new_coin_control.Select(COutPoint(inputs.prevout));
+ }
+ new_coin_control.fAllowOtherInputs = true;
+
+ // We cannot source new unconfirmed inputs(bip125 rule 2)
+ new_coin_control.m_min_depth = 1;
+
+ CTransactionRef tx_new = MakeTransactionRef();
+ CReserveKey reservekey(wallet);
+ CAmount fee_ret;
+ int change_pos_in_out = -1; // No requested location for change
+ std::string fail_reason;
+ if (!wallet->CreateTransaction(*locked_chain, recipients, tx_new, reservekey, fee_ret, change_pos_in_out, fail_reason, new_coin_control, false)) {
+ errors.push_back("Unable to create transaction: " + fail_reason);
+ return Result::WALLET_ERROR;
+ }
+
+ // If change key hasn't been ReturnKey'ed by this point, we take it out of keypool
+ reservekey.KeepKey();
+
+ // Write back new fee if successful
+ new_fee = fee_ret;
+
+ // Write back transaction
+ mtx = CMutableTransaction(*tx_new);
+ // Mark new tx not replaceable, if requested.
+ if (!coin_control.m_signal_bip125_rbf.get_value_or(wallet->m_signal_rbf)) {
+ for (auto& input : mtx.vin) {
+ if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
+ }
+ }
return Result::OK;
}
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index a1eb5d0e0d..f9cbfc5f68 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -28,8 +28,8 @@ enum class Result
//! Return whether transaction can be bumped.
bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid);
-//! Create bumpfee transaction.
-Result CreateTransaction(const CWallet* wallet,
+//! Create bumpfee transaction based on total amount.
+Result CreateTotalBumpTransaction(const CWallet* wallet,
const uint256& txid,
const CCoinControl& coin_control,
CAmount total_fee,
@@ -38,6 +38,15 @@ Result CreateTransaction(const CWallet* wallet,
CAmount& new_fee,
CMutableTransaction& mtx);
+//! Create bumpfee transaction based on feerate estimates.
+Result CreateRateBumpTransaction(CWallet* wallet,
+ const uint256& txid,
+ const CCoinControl& coin_control,
+ std::vector<std::string>& errors,
+ CAmount& old_fee,
+ CAmount& new_fee,
+ CMutableTransaction& mtx);
+
//! Sign the new transaction,
//! @return false if the tx couldn't be found or if it was
//! impossible to create the signature(s)
diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp
index 545adaebc1..d9ae18ed60 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -22,8 +22,9 @@ CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinC
{
CAmount fee_needed = GetMinimumFeeRate(wallet, coin_control, feeCalc).GetFee(nTxBytes);
// Always obey the maximum
- if (fee_needed > maxTxFee) {
- fee_needed = maxTxFee;
+ const CAmount max_tx_fee = wallet.m_default_max_tx_fee;
+ if (fee_needed > max_tx_fee) {
+ fee_needed = max_tx_fee;
if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE;
}
return fee_needed;
@@ -31,7 +32,7 @@ CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinC
CFeeRate GetRequiredFeeRate(const CWallet& wallet)
{
- return std::max(wallet.m_min_fee, ::minRelayTxFee);
+ return std::max(wallet.m_min_fee, wallet.chain().relayMinFee());
}
CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, FeeCalculation* feeCalc)
@@ -96,6 +97,6 @@ CFeeRate GetDiscardRate(const CWallet& wallet)
// Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate
discard_rate = (discard_rate == CFeeRate(0)) ? wallet.m_discard_rate : std::min(discard_rate, wallet.m_discard_rate);
// Discard rate must be at least dustRelayFee
- discard_rate = std::max(discard_rate, ::dustRelayFee);
+ discard_rate = std::max(discard_rate, wallet.chain().relayDustFee());
return discard_rate;
}
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 7ad343c15f..47ef01bfd1 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -9,6 +9,7 @@
#include <net.h>
#include <scheduler.h>
#include <outputtype.h>
+#include <util/error.h>
#include <util/system.h>
#include <util/moneystr.h>
#include <validation.h>
@@ -47,6 +48,8 @@ void WalletInit::AddWalletOptions() const
gArgs.AddArg("-fallbackfee=<amt>", strprintf("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), false, OptionsCategory::WALLET);
gArgs.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u)", DEFAULT_KEYPOOL_SIZE), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-maxtxfee=<amt>", strprintf("Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)",
+ CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), false, OptionsCategory::WALLET);
gArgs.AddArg("-paytxfee=<amt>", strprintf("Fee (in %s/kB) to add to transactions you send (default: %s)",
@@ -123,62 +126,6 @@ bool WalletInit::ParameterInteraction() const
if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false))
return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));
- if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB)
- InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
- _("The wallet will avoid paying less than the minimum relay fee."));
-
- return true;
-}
-
-bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
-{
- if (gArgs.IsArgSet("-walletdir")) {
- fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
- boost::system::error_code error;
- // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
- fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
- if (error || !fs::exists(wallet_dir)) {
- chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
- return false;
- } else if (!fs::is_directory(wallet_dir)) {
- chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
- return false;
- // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
- } else if (!wallet_dir.is_absolute()) {
- chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
- return false;
- }
- gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
- }
-
- LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
-
- uiInterface.InitMessage(_("Verifying wallet(s)..."));
-
- // Parameter interaction code should have thrown an error if -salvagewallet
- // was enabled with more than wallet file, so the wallet_files size check
- // here should have no effect.
- bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1;
-
- // Keep track of each wallet absolute path to detect duplicates.
- std::set<fs::path> wallet_paths;
-
- for (const auto& wallet_file : wallet_files) {
- WalletLocation location(wallet_file);
-
- if (!wallet_paths.insert(location.GetPath()).second) {
- chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
- return false;
- }
-
- std::string error_string;
- std::string warning_string;
- bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warning_string);
- if (!error_string.empty()) chain.initError(error_string);
- if (!warning_string.empty()) chain.initWarning(warning_string);
- if (!verify_success) return false;
- }
-
return true;
}
@@ -191,51 +138,3 @@ void WalletInit::Construct(InitInterfaces& interfaces) const
gArgs.SoftSetArg("-wallet", "");
interfaces.chain_clients.emplace_back(interfaces::MakeWalletClient(*interfaces.chain, gArgs.GetArgs("-wallet")));
}
-
-bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
-{
- for (const std::string& walletFile : wallet_files) {
- std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile));
- if (!pwallet) {
- return false;
- }
- AddWallet(pwallet);
- }
-
- return true;
-}
-
-void StartWallets(CScheduler& scheduler)
-{
- for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
- pwallet->postInitProcess();
- }
-
- // Run a thread to flush wallet periodically
- scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
-}
-
-void FlushWallets()
-{
- for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
- pwallet->Flush(false);
- }
-}
-
-void StopWallets()
-{
- for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
- pwallet->Flush(true);
- }
-}
-
-void UnloadWallets()
-{
- auto wallets = GetWallets();
- while (!wallets.empty()) {
- auto wallet = wallets.back();
- wallets.pop_back();
- RemoveWallet(wallet);
- UnloadWallet(std::move(wallet));
- }
-}
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
new file mode 100644
index 0000000000..79c5f439df
--- /dev/null
+++ b/src/wallet/load.cpp
@@ -0,0 +1,112 @@
+// 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.
+
+#include <wallet/load.h>
+
+#include <interfaces/chain.h>
+#include <scheduler.h>
+#include <util/system.h>
+#include <wallet/wallet.h>
+
+bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
+{
+ if (gArgs.IsArgSet("-walletdir")) {
+ fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
+ boost::system::error_code error;
+ // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
+ fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
+ if (error || !fs::exists(wallet_dir)) {
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
+ return false;
+ } else if (!fs::is_directory(wallet_dir)) {
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
+ return false;
+ // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
+ } else if (!wallet_dir.is_absolute()) {
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
+ return false;
+ }
+ gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
+ }
+
+ LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
+
+ chain.initMessage(_("Verifying wallet(s)..."));
+
+ // Parameter interaction code should have thrown an error if -salvagewallet
+ // was enabled with more than wallet file, so the wallet_files size check
+ // here should have no effect.
+ bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1;
+
+ // Keep track of each wallet absolute path to detect duplicates.
+ std::set<fs::path> wallet_paths;
+
+ for (const auto& wallet_file : wallet_files) {
+ WalletLocation location(wallet_file);
+
+ if (!wallet_paths.insert(location.GetPath()).second) {
+ chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
+ return false;
+ }
+
+ std::string error_string;
+ std::string warning_string;
+ bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warning_string);
+ if (!error_string.empty()) chain.initError(error_string);
+ if (!warning_string.empty()) chain.initWarning(warning_string);
+ if (!verify_success) return false;
+ }
+
+ return true;
+}
+
+bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
+{
+ for (const std::string& walletFile : wallet_files) {
+ std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile));
+ if (!pwallet) {
+ return false;
+ }
+ AddWallet(pwallet);
+ }
+
+ return true;
+}
+
+void StartWallets(CScheduler& scheduler)
+{
+ for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
+ pwallet->postInitProcess();
+ }
+
+ // Schedule periodic wallet flushes and tx rebroadcasts
+ scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
+ scheduler.scheduleEvery(MaybeResendWalletTxs, 1000);
+}
+
+void FlushWallets()
+{
+ for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
+ pwallet->Flush(false);
+ }
+}
+
+void StopWallets()
+{
+ for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
+ pwallet->Flush(true);
+ }
+}
+
+void UnloadWallets()
+{
+ auto wallets = GetWallets();
+ while (!wallets.empty()) {
+ auto wallet = wallets.back();
+ wallets.pop_back();
+ RemoveWallet(wallet);
+ UnloadWallet(std::move(wallet));
+ }
+}
diff --git a/src/wallet/load.h b/src/wallet/load.h
new file mode 100644
index 0000000000..9bb6f2166e
--- /dev/null
+++ b/src/wallet/load.h
@@ -0,0 +1,38 @@
+// 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_WALLET_LOAD_H
+#define BITCOIN_WALLET_LOAD_H
+
+#include <string>
+#include <vector>
+
+class CScheduler;
+
+namespace interfaces {
+class Chain;
+} // namespace interfaces
+
+//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
+//! This function will perform salvage on the wallet if requested, as long as only one wallet is
+//! being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
+bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
+
+//! Load wallet databases.
+bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
+
+//! Complete startup of wallets.
+void StartWallets(CScheduler& scheduler);
+
+//! Flush all wallets in preparation for shutdown.
+void FlushWallets();
+
+//! Stop all wallets. Wallets will be flushed first.
+void StopWallets();
+
+//! Close all wallets.
+void UnloadWallets();
+
+#endif // BITCOIN_WALLET_LOAD_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 684d900478..c339e111ba 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -348,7 +348,11 @@ UniValue importaddress(const JSONRPCRequest& request)
if (fRescan)
{
RescanWallet(*pwallet, reserver);
- pwallet->ReacceptWalletTransactions();
+ {
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
+ pwallet->ReacceptWalletTransactions(*locked_chain);
+ }
}
return NullUniValue;
@@ -532,7 +536,11 @@ UniValue importpubkey(const JSONRPCRequest& request)
if (fRescan)
{
RescanWallet(*pwallet, reserver);
- pwallet->ReacceptWalletTransactions();
+ {
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
+ pwallet->ReacceptWalletTransactions(*locked_chain);
+ }
}
return NullUniValue;
@@ -595,11 +603,11 @@ UniValue importwallet(const JSONRPCRequest& request)
// Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which
// we don't want for this progress bar showing the import progress. uiInterface.ShowProgress does not have a cancel button.
- uiInterface.ShowProgress(strprintf("%s " + _("Importing..."), pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
+ pwallet->chain().showProgress(strprintf("%s " + _("Importing..."), pwallet->GetDisplayName()), 0, false); // show progress dialog in GUI
std::vector<std::tuple<CKey, int64_t, bool, std::string>> keys;
std::vector<std::pair<CScript, int64_t>> scripts;
while (file.good()) {
- uiInterface.ShowProgress("", std::max(1, std::min(50, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false);
+ pwallet->chain().showProgress("", std::max(1, std::min(50, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false);
std::string line;
std::getline(file, line);
if (line.empty() || line[0] == '#')
@@ -637,13 +645,13 @@ UniValue importwallet(const JSONRPCRequest& request)
file.close();
// We now know whether we are importing private keys, so we can error if private keys are disabled
if (keys.size() > 0 && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
- uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI
+ pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled when private keys are disabled");
}
double total = (double)(keys.size() + scripts.size());
double progress = 0;
for (const auto& key_tuple : keys) {
- uiInterface.ShowProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
+ pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
const CKey& key = std::get<0>(key_tuple);
int64_t time = std::get<1>(key_tuple);
bool has_label = std::get<2>(key_tuple);
@@ -668,7 +676,7 @@ UniValue importwallet(const JSONRPCRequest& request)
progress++;
}
for (const auto& script_pair : scripts) {
- uiInterface.ShowProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
+ pwallet->chain().showProgress("", std::max(50, std::min(75, (int)((progress / total) * 100) + 50)), false);
const CScript& script = script_pair.first;
int64_t time = script_pair.second;
CScriptID id(script);
@@ -687,10 +695,10 @@ UniValue importwallet(const JSONRPCRequest& request)
}
progress++;
}
- uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI
+ pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
pwallet->UpdateTimeFirstKey(nTimeBegin);
}
- uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI
+ pwallet->chain().showProgress("", 100, false); // hide progress dialog in GUI
RescanWallet(*pwallet, reserver, nTimeBegin, false /* update */);
pwallet->MarkDirty();
@@ -892,7 +900,7 @@ struct ImportData
// Output data
std::set<CScript> import_scripts;
std::map<CKeyID, bool> used_keys; //!< Import these private keys if available (the value indicates whether if the key is required for solvability)
- std::map<CKeyID, KeyOriginInfo> key_origins;
+ std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> key_origins;
};
enum class ScriptContext
@@ -1189,6 +1197,9 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
bool spendable = std::all_of(pubkey_map.begin(), pubkey_map.end(),
[&](const std::pair<CKeyID, CPubKey>& used_key) {
return privkey_map.count(used_key.first) > 0;
+ }) && std::all_of(import_data.key_origins.begin(), import_data.key_origins.end(),
+ [&](const std::pair<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& entry) {
+ return privkey_map.count(entry.first) > 0;
});
if (!watch_only && !spendable) {
warnings.push_back("Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag.");
@@ -1266,7 +1277,10 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
pwallet->UpdateTimeFirstKey(timestamp);
- }
+ }
+ for (const auto& entry : import_data.key_origins) {
+ pwallet->AddKeyOrigin(entry.second.first, entry.second.second);
+ }
for (const CKeyID& id : ordered_pubkeys) {
auto entry = pubkey_map.find(id);
if (entry == pubkey_map.end()) {
@@ -1277,10 +1291,6 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
if (!pwallet->GetPubKey(id, temp) && !pwallet->AddWatchOnly(GetScriptForRawPubKey(pubkey), timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
- const auto& key_orig_it = import_data.key_origins.find(id);
- if (key_orig_it != import_data.key_origins.end()) {
- pwallet->AddKeyOrigin(pubkey, key_orig_it->second);
- }
pwallet->mapKeyMetadata[id].nCreateTime = timestamp;
// Add to keypool only works with pubkeys
@@ -1468,7 +1478,11 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
}
if (fRescan && fRunScan && requests.size()) {
int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */);
- pwallet->ReacceptWalletTransactions();
+ {
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
+ pwallet->ReacceptWalletTransactions(*locked_chain);
+ }
if (pwallet->IsAbortingRescan()) {
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user.");
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 31a3209a49..5a22508c4b 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -7,7 +7,6 @@
#include <chain.h>
#include <consensus/validation.h>
#include <core_io.h>
-#include <httpserver.h>
#include <init.h>
#include <interfaces/chain.h>
#include <validation.h>
@@ -19,7 +18,7 @@
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
-#include <rpc/rawtransaction.h>
+#include <rpc/rawtransaction_util.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h>
@@ -27,8 +26,11 @@
#include <shutdown.h>
#include <timedata.h>
#include <util/bip32.h>
+#include <util/fees.h>
#include <util/system.h>
#include <util/moneystr.h>
+#include <util/url.h>
+#include <util/validation.h>
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
#include <wallet/psbtwallet.h>
@@ -308,7 +310,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
{
- CAmount curBalance = pwallet->GetBalance();
+ CAmount curBalance = pwallet->GetBalance().m_mine_trusted;
// Check amount
if (nValue <= 0)
@@ -761,12 +763,14 @@ static UniValue getbalance(const JSONRPCRequest& request)
min_depth = request.params[1].get_int();
}
- isminefilter filter = ISMINE_SPENDABLE;
+ bool include_watchonly = false;
if (!request.params[2].isNull() && request.params[2].get_bool()) {
- filter = filter | ISMINE_WATCH_ONLY;
+ include_watchonly = true;
}
- return ValueFromAmount(pwallet->GetBalance(filter, min_depth));
+ const auto bal = pwallet->GetBalance(min_depth);
+
+ return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
}
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
@@ -794,7 +798,7 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- return ValueFromAmount(pwallet->GetUnconfirmedBalance());
+ return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending);
}
@@ -807,9 +811,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
- throw std::runtime_error(
- RPCHelpMan{"sendmany",
+ const RPCHelpMan help{"sendmany",
"\nSend multiple times. Amounts are double-precision floating point numbers." +
HelpRequiringPassphrase(pwallet) + "\n",
{
@@ -819,7 +821,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
},
},
- {"minconf", RPCArg::Type::NUM, /* default */ "1", "Only use the balance confirmed at least this many times."},
+ {"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
{"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
{"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array with addresses.\n"
" The fee will be equally deducted from the amount of each selected address.\n"
@@ -850,7 +852,11 @@ static UniValue sendmany(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
@@ -867,9 +873,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
}
UniValue sendTo = request.params[1].get_obj();
- int nMinDepth = 1;
- if (!request.params[2].isNull())
- nMinDepth = request.params[2].get_int();
mapValue_t mapValue;
if (!request.params[3].isNull() && !request.params[3].get_str().empty())
@@ -897,7 +900,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
std::set<CTxDestination> destinations;
std::vector<CRecipient> vecSend;
- CAmount totalAmount = 0;
std::vector<std::string> keys = sendTo.getKeys();
for (const std::string& name_ : keys) {
CTxDestination dest = DecodeDestination(name_);
@@ -914,7 +916,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
CAmount nAmount = AmountFromValue(sendTo[name_]);
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
- totalAmount += nAmount;
bool fSubtractFeeFromAmount = false;
for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
@@ -929,11 +930,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- // Check funds
- if (totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth)) {
- throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds");
- }
-
// Shuffle recipient list
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
@@ -1760,7 +1756,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
entry.pushKV("details", details);
- std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
+ std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
entry.pushKV("hex", strHex);
return entry;
@@ -1978,7 +1974,7 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
// wallet before the following callback is called. If a valid shared pointer
// is acquired in the callback then the wallet is still loaded.
std::weak_ptr<CWallet> weak_wallet = wallet;
- RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] {
+ pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] {
if (auto shared_wallet = weak_wallet.lock()) {
LOCK(shared_wallet->cs_wallet);
shared_wallet->Lock();
@@ -2367,8 +2363,8 @@ static UniValue settxfee(const JSONRPCRequest& request)
CFeeRate tx_fee_rate(nAmount, 1000);
if (tx_fee_rate == 0) {
// automatic selection
- } else if (tx_fee_rate < ::minRelayTxFee) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than min relay tx fee (%s)", ::minRelayTxFee.ToString()));
+ } else if (tx_fee_rate < pwallet->chain().relayMinFee()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than min relay tx fee (%s)", pwallet->chain().relayMinFee().ToString()));
} else if (tx_fee_rate < pwallet->m_min_fee) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("txfee cannot be less than wallet min fee (%s)", pwallet->m_min_fee.ToString()));
}
@@ -2424,11 +2420,12 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
+ const auto bal = pwallet->GetBalance();
obj.pushKV("walletname", pwallet->GetName());
obj.pushKV("walletversion", pwallet->GetVersion());
- obj.pushKV("balance", ValueFromAmount(pwallet->GetBalance()));
- obj.pushKV("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance()));
- obj.pushKV("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance()));
+ obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
+ obj.pushKV("unconfirmed_balance", ValueFromAmount(bal.m_mine_untrusted_pending));
+ obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
obj.pushKV("txcount", (int)pwallet->mapWallet.size());
obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
@@ -2668,50 +2665,6 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
return NullUniValue;
}
-static UniValue resendwallettransactions(const JSONRPCRequest& request)
-{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
- if (request.fHelp || request.params.size() != 0)
- throw std::runtime_error(
- RPCHelpMan{"resendwallettransactions",
- "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n"
- "Intended only for testing; the wallet code periodically re-broadcasts\n"
- "automatically.\n",
- {},
- RPCResult{
- "Returns an RPC error if -walletbroadcast is set to false.\n"
- "Returns array of transaction ids that were re-broadcast.\n"
- },
- RPCExamples{""},
- }.ToString()
- );
-
- if (!pwallet->chain().p2pEnabled()) {
- throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- }
-
- auto locked_chain = pwallet->chain().lock();
- LOCK(pwallet->cs_wallet);
-
- if (!pwallet->GetBroadcastTransactions()) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast");
- }
-
- std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(*locked_chain, GetTime());
- UniValue result(UniValue::VARR);
- for (const uint256& txid : txids)
- {
- result.push_back(txid.ToString());
- }
- return result;
-}
-
static UniValue listunspent(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -3150,7 +3103,7 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
{"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"},
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
{"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
- {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"},
+ {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "(required for Segwit inputs) the amount spent"},
},
},
},
@@ -3197,7 +3150,14 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
- return SignTransaction(pwallet->chain(), mtx, request.params[1], pwallet, false, request.params[2]);
+ // Fetch previous transactions (inputs):
+ std::map<COutPoint, Coin> coins;
+ for (const CTxIn& txin : mtx.vin) {
+ coins[txin.prevout]; // Create empty map entry keyed by prevout.
+ }
+ pwallet->chain().findCoins(coins);
+
+ return SignTransaction(mtx, request.params[1], pwallet, coins, false, request.params[2]);
}
static UniValue bumpfee(const JSONRPCRequest& request)
@@ -3214,9 +3174,9 @@ static UniValue bumpfee(const JSONRPCRequest& request)
RPCHelpMan{"bumpfee",
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
"An opt-in RBF transaction with the given txid must be in the wallet.\n"
- "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n"
- "If the change output is not big enough to cover the increased fee, the command will currently fail\n"
- "instead of adding new inputs to compensate. (A future implementation could improve this.)\n"
+ "The command will pay the additional fee by reducing change outputs or adding inputs when necessary. It may add a new change output if one does not already exist.\n"
+ "If `totalFee` is given, adding inputs is not supported, so there must be a single change output that is big enough or it will fail.\n"
+ "All inputs in the original transaction will be included in the replacement transaction.\n"
"The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
"By default, the new fee will be calculated automatically using estimatesmartfee.\n"
"The user can specify a confirmation target for estimatesmartfee.\n"
@@ -3313,7 +3273,14 @@ static UniValue bumpfee(const JSONRPCRequest& request)
CAmount old_fee;
CAmount new_fee;
CMutableTransaction mtx;
- feebumper::Result res = feebumper::CreateTransaction(pwallet, hash, coin_control, totalFee, errors, old_fee, new_fee, mtx);
+ feebumper::Result res;
+ if (totalFee > 0) {
+ // Targeting total fee bump. Requires a change output of sufficient size.
+ res = feebumper::CreateTotalBumpTransaction(pwallet, hash, coin_control, totalFee, errors, old_fee, new_fee, mtx);
+ } else {
+ // Targeting feerate bump.
+ res = feebumper::CreateRateBumpTransaction(pwallet, hash, coin_control, errors, old_fee, new_fee, mtx);
+ }
if (res != feebumper::Result::OK) {
switch(res) {
case feebumper::Result::INVALID_ADDRESS_OR_KEY:
@@ -3455,7 +3422,7 @@ class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue>
public:
CWallet * const pwallet;
- void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const
+ void ProcessSubScript(const CScript& subscript, UniValue& obj) const
{
// Always present: script type and redeemscript
std::vector<std::vector<unsigned char>> solutions_data;
@@ -3464,7 +3431,6 @@ public:
obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
CTxDestination embedded;
- UniValue a(UniValue::VARR);
if (ExtractDestination(subscript, embedded)) {
// Only when the script corresponds to an address.
UniValue subobj(UniValue::VOBJ);
@@ -3477,7 +3443,6 @@ public:
// Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
obj.pushKV("embedded", std::move(subobj));
- if (include_addresses) a.push_back(EncodeDestination(embedded));
} else if (which_type == TX_MULTISIG) {
// Also report some information on multisig scripts (which do not have a corresponding address).
// TODO: abstract out the common functionality between this logic and ExtractDestinations.
@@ -3485,17 +3450,10 @@ public:
UniValue pubkeys(UniValue::VARR);
for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
- if (include_addresses) a.push_back(EncodeDestination(key.GetID()));
pubkeys.push_back(HexStr(key.begin(), key.end()));
}
obj.pushKV("pubkeys", std::move(pubkeys));
}
-
- // The "addresses" field is confusing because it refers to public keys using their P2PKH address.
- // For that reason, only add the 'addresses' field when needed for backward compatibility. New applications
- // can use the 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for
- // inspecting multisig participants.
- if (include_addresses) obj.pushKV("addresses", std::move(a));
}
explicit DescribeWalletAddressVisitor(CWallet* _pwallet) : pwallet(_pwallet) {}
@@ -3518,7 +3476,7 @@ public:
UniValue obj(UniValue::VOBJ);
CScript subscript;
if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
- ProcessSubScript(subscript, obj, IsDeprecatedRPCEnabled("validateaddress"));
+ ProcessSubScript(subscript, obj);
}
return obj;
}
@@ -3732,9 +3690,20 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
// Find all addresses that have the given label
UniValue ret(UniValue::VOBJ);
+ std::set<std::string> addresses;
for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
if (item.second.name == label) {
- ret.pushKV(EncodeDestination(item.first), AddressBookDataToJSON(item.second, false));
+ std::string address = EncodeDestination(item.first);
+ // CWallet::mapAddressBook is not expected to contain duplicate
+ // address strings, but build a separate set as a precaution just in
+ // case it does.
+ bool unique = addresses.emplace(address).second;
+ assert(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,
+ // which currently is O(1).
+ ret.__pushKV(address, AddressBookDataToJSON(item.second, false));
}
}
@@ -3881,19 +3850,6 @@ UniValue sethdseed(const JSONRPCRequest& request)
return NullUniValue;
}
-void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubKey, KeyOriginInfo>& hd_keypaths)
-{
- CPubKey vchPubKey;
- if (!pwallet->GetPubKey(keyID, vchPubKey)) {
- return;
- }
- KeyOriginInfo info;
- if (!pwallet->GetKeyOrigin(keyID, info)) {
- throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal keypath is broken");
- }
- hd_keypaths.emplace(vchPubKey, std::move(info));
-}
-
UniValue walletprocesspsbt(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -4098,7 +4054,6 @@ UniValue importmulti(const JSONRPCRequest& request);
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
- { "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} },
@@ -4156,8 +4111,8 @@ static const CRPCCommand commands[] =
};
// clang-format on
-void RegisterWalletRPCCommands(CRPCTable &t)
+void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers)
{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
- t.appendCommand(commands[vcidx].name, &commands[vcidx]);
+ handlers.emplace_back(chain.handleRpc(commands[vcidx]));
}
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index 58053bde59..7cf607ccc7 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -5,7 +5,9 @@
#ifndef BITCOIN_WALLET_RPCWALLET_H
#define BITCOIN_WALLET_RPCWALLET_H
+#include <memory>
#include <string>
+#include <vector>
class CRPCTable;
class CWallet;
@@ -14,7 +16,12 @@ class UniValue;
struct PartiallySignedTransaction;
class CTransaction;
-void RegisterWalletRPCCommands(CRPCTable &t);
+namespace interfaces {
+class Chain;
+class Handler;
+}
+
+void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers);
/**
* Figures out what wallet, if any, to use for a JSONRPCRequest.
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 5c65acf601..34b9770e8b 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -8,7 +8,7 @@
#include <amount.h>
#include <primitives/transaction.h>
#include <random.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <wallet/test/wallet_test_fixture.h>
#include <boost/test/unit_test.hpp>
@@ -29,7 +29,7 @@ typedef std::set<CInputCoin> CoinSet;
static std::vector<COutput> vCoins;
static auto testChain = interfaces::MakeChain();
-static CWallet testWallet(*testChain, WalletLocation(), WalletDatabase::CreateDummy());
+static CWallet testWallet(testChain.get(), WalletLocation(), WalletDatabase::CreateDummy());
static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0);
@@ -69,8 +69,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
if (fIsFromMe)
{
- wtx->fDebitCached = true;
- wtx->nDebitCached = 1;
+ 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);
@@ -115,7 +114,7 @@ inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& coins)
{
static std::vector<OutputGroup> static_groups;
static_groups.clear();
- for (auto& coin : coins) static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, coin.tx->fDebitCached && coin.tx->nDebitCached == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0);
+ for (auto& coin : coins) static_groups.emplace_back(coin.GetInputCoin(), coin.nDepth, coin.tx->m_amounts[CWalletTx::DEBIT].m_cached[ISMINE_SPENDABLE] && coin.tx->m_amounts[CWalletTx::DEBIT].m_value[ISMINE_SPENDABLE] == 1 /* HACK: we can't figure out the is_me flag so we use the conditions defined above; perhaps set safe to false for !fIsFromMe in add_coin() */, 0, 0);
return static_groups;
}
@@ -135,7 +134,6 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
/////////////////////////
// Known Outcome tests //
/////////////////////////
- BOOST_TEST_MESSAGE("Testing known outcomes");
// Empty utxo pool
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp
index 2a64749379..d9b07af329 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/test_bitcoin.h>
+#include <test/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 cd47b31da1..0f2d9fbd3d 100644
--- a/src/wallet/test/init_test_fixture.h
+++ b/src/wallet/test/init_test_fixture.h
@@ -6,7 +6,7 @@
#define BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
#include <interfaces/chain.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
struct InitWalletDirTestingSetup: public BasicTestingSetup {
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index 5852d3ef84..e1c53c83e2 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -4,7 +4,7 @@
#include <boost/test/unit_test.hpp>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <wallet/test/init_test_fixture.h>
#include <init.h>
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index 789e86e21b..f774cb4ad1 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -12,7 +12,7 @@
#include <univalue.h>
#include <boost/test/unit_test.hpp>
-#include <test/test_bitcoin.h>
+#include <test/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 ae7092fa89..acc61c984f 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/test_bitcoin.h>
+#include <test/setup_common.h>
#include <util/strencodings.h>
#include <wallet/crypter.h>
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index a5fb1db86c..6526e69eea 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -8,17 +8,13 @@
#include <wallet/db.h>
#include <wallet/rpcwallet.h>
-WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
- TestingSetup(chainName), m_wallet(*m_chain, WalletLocation(), WalletDatabase::CreateMock())
+WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
+ : TestingSetup(chainName),
+ m_wallet(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock())
{
bool fFirstRun;
m_wallet.LoadWallet(fFirstRun);
- RegisterValidationInterface(&m_wallet);
+ m_wallet.handleNotifications();
- RegisterWalletRPCCommands(tableRPC);
-}
-
-WalletTestingSetup::~WalletTestingSetup()
-{
- UnregisterValidationInterface(&m_wallet);
+ m_chain_client->registerRpcs();
}
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index e6fe8c9473..1017e61700 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/test_bitcoin.h>
+#include <test/setup_common.h>
#include <interfaces/chain.h>
#include <interfaces/wallet.h>
@@ -17,9 +17,9 @@
*/
struct WalletTestingSetup: public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
- ~WalletTestingSetup();
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
+ std::unique_ptr<interfaces::ChainClient> m_chain_client = interfaces::MakeWalletClient(*m_chain, {});
CWallet m_wallet;
};
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index af57dbf5f6..3cdbde33c3 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -13,7 +13,7 @@
#include <consensus/validation.h>
#include <interfaces/chain.h>
#include <rpc/server.h>
-#include <test/test_bitcoin.h>
+#include <test/setup_common.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/test/wallet_test_fixture.h>
@@ -49,7 +49,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions accommodates a null start block.
{
- CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -58,13 +58,13 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK(result.last_failed_block.IsNull());
BOOST_CHECK(result.last_scanned_block.IsNull());
BOOST_CHECK(!result.last_scanned_height);
- BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
+ BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 0);
}
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
- CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -73,7 +73,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK(result.last_failed_block.IsNull());
BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
- BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
+ BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 100 * COIN);
}
// Prune the older block file.
@@ -83,7 +83,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
- CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -92,7 +92,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK_EQUAL(result.last_failed_block, oldTip->GetBlockHash());
BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
BOOST_CHECK_EQUAL(*result.last_scanned_height, newTip->nHeight);
- BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
+ BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 50 * COIN);
}
// Prune the remaining block file.
@@ -101,7 +101,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions scans no blocks.
{
- CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -110,7 +110,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK_EQUAL(result.last_failed_block, newTip->GetBlockHash());
BOOST_CHECK(result.last_scanned_block.IsNull());
BOOST_CHECK(!result.last_scanned_height);
- BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
+ BOOST_CHECK_EQUAL(wallet.GetBalance().m_mine_immature, 0);
}
}
@@ -135,7 +135,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is
// after.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
AddWallet(wallet);
UniValue keys;
keys.setArray();
@@ -198,7 +198,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Import key into wallet and call dumpwallet to create backup file.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
LOCK(wallet->cs_wallet);
wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
@@ -214,7 +214,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
JSONRPCRequest request;
request.params.setArray();
@@ -245,7 +245,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
auto chain = interfaces::MakeChain();
- CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
CWalletTx wtx(&wallet, m_coinbase_txns.back());
auto locked_chain = chain->lock();
LOCK(wallet.cs_wallet);
@@ -340,7 +340,7 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = MakeUnique<CWallet>(*m_chain, WalletLocation(), WalletDatabase::CreateMock());
+ wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
AddKey(*wallet, coinbaseKey);
@@ -451,7 +451,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{
auto chain = interfaces::MakeChain();
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
wallet->SetMinVersion(FEATURE_LATEST);
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
BOOST_CHECK(!wallet->TopUpKeyPool(1000));
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 02f3a265d9..4ab94f0c2c 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1,13 +1,11 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2018 The Bitcoin Core developers
+// 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.
#include <wallet/wallet.h>
-#include <checkpoints.h>
#include <chain.h>
-#include <wallet/coincontrol.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <fs.h>
@@ -16,7 +14,6 @@
#include <key.h>
#include <key_io.h>
#include <keystore.h>
-#include <validation.h>
#include <net.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -29,7 +26,13 @@
#include <timedata.h>
#include <txmempool.h>
#include <util/bip32.h>
+#include <util/error.h>
+#include <util/fees.h>
#include <util/moneystr.h>
+#include <util/rbf.h>
+#include <util/validation.h>
+#include <validation.h>
+#include <wallet/coincontrol.h>
#include <wallet/fees.h>
#include <algorithm>
@@ -96,7 +99,7 @@ static void ReleaseWallet(CWallet* wallet)
wallet->WalletLogPrintf("Releasing wallet\n");
wallet->BlockUntilSyncedToCurrentChain();
wallet->Flush();
- UnregisterValidationInterface(wallet);
+ wallet->m_chain_notifications_handler.reset();
delete wallet;
// Wallet is now released, notify UnloadWallet, if any.
{
@@ -193,7 +196,7 @@ CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
{
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
assert(!IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET));
- AssertLockHeld(cs_wallet); // mapKeyMetadata
+ AssertLockHeld(cs_wallet);
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
CKey secret;
@@ -281,9 +284,9 @@ void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
}
-bool CWallet::AddKeyPubKeyWithDB(WalletBatch &batch, const CKey& secret, const CPubKey &pubkey)
+bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const CPubKey& pubkey)
{
- AssertLockHeld(cs_wallet); // mapKeyMetadata
+ AssertLockHeld(cs_wallet);
// Make sure we aren't adding private keys to private key disabled wallets
assert(!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
@@ -345,16 +348,16 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
}
}
-void CWallet::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &meta)
+void CWallet::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta)
{
- AssertLockHeld(cs_wallet); // mapKeyMetadata
+ AssertLockHeld(cs_wallet);
UpdateTimeFirstKey(meta.nCreateTime);
mapKeyMetadata[keyID] = meta;
}
-void CWallet::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &meta)
+void CWallet::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta)
{
- AssertLockHeld(cs_wallet); // m_script_metadata
+ AssertLockHeld(cs_wallet);
UpdateTimeFirstKey(meta.nCreateTime);
m_script_metadata[script_id] = meta;
}
@@ -367,7 +370,7 @@ bool CWallet::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey,
void CWallet::UpgradeKeyMetadata()
{
- AssertLockHeld(cs_wallet); // mapKeyMetadata
+ AssertLockHeld(cs_wallet);
if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
return;
}
@@ -569,7 +572,7 @@ void CWallet::ChainStateFlushed(const CBlockLocator& loc)
void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in, bool fExplicit)
{
- LOCK(cs_wallet); // nWalletVersion
+ LOCK(cs_wallet);
if (nWalletVersion >= nVersion)
return;
@@ -593,7 +596,7 @@ void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in,
bool CWallet::SetMaxVersion(int nVersion)
{
- LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion
+ LOCK(cs_wallet);
// cannot downgrade below current version
if (nWalletVersion > nVersion)
return false;
@@ -877,9 +880,9 @@ DBErrors CWallet::ReorderTransactions()
return DBErrors::LOAD_OK;
}
-int64_t CWallet::IncOrderPosNext(WalletBatch *batch)
+int64_t CWallet::IncOrderPosNext(WalletBatch* batch)
{
- AssertLockHeld(cs_wallet); // nOrderPosNext
+ AssertLockHeld(cs_wallet);
int64_t nRet = nOrderPosNext++;
if (batch) {
batch->WriteOrderPosNext(nOrderPosNext);
@@ -1243,7 +1246,8 @@ void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
}
}
-void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
+void CWallet::BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) {
+ const uint256& block_hash = block.GetHash();
auto locked_chain = chain().lock();
LOCK(cs_wallet);
// TODO: Temporarily ensure that mempool removals are notified before
@@ -1258,27 +1262,30 @@ void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const
SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */);
TransactionRemovedFromMempool(ptx);
}
- for (size_t i = 0; i < pblock->vtx.size(); i++) {
- SyncTransaction(pblock->vtx[i], pindex->GetBlockHash(), i);
- TransactionRemovedFromMempool(pblock->vtx[i]);
+ for (size_t i = 0; i < block.vtx.size(); i++) {
+ SyncTransaction(block.vtx[i], block_hash, i);
+ TransactionRemovedFromMempool(block.vtx[i]);
}
- m_last_block_processed = pindex->GetBlockHash();
+ m_last_block_processed = block_hash;
}
-void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) {
+void CWallet::BlockDisconnected(const CBlock& block) {
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- for (const CTransactionRef& ptx : pblock->vtx) {
+ for (const CTransactionRef& ptx : block.vtx) {
SyncTransaction(ptx, {} /* block hash */, 0 /* position in block */);
}
}
+void CWallet::UpdatedBlockTip()
+{
+ m_best_block_time = GetTime();
+}
void CWallet::BlockUntilSyncedToCurrentChain() {
- AssertLockNotHeld(cs_main);
AssertLockNotHeld(cs_wallet);
{
@@ -1297,7 +1304,7 @@ void CWallet::BlockUntilSyncedToCurrentChain() {
// ...otherwise put a callback in the validation interface queue and wait
// for the queue to drain enough to execute it (indicating we are caught up
// at least with the time we entered this function).
- SyncWithValidationInterfaceQueue();
+ chain().waitForNotifications();
}
@@ -1721,7 +1728,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
uint256 start_block;
{
auto locked_chain = chain().lock();
- const Optional<int> start_height = locked_chain->findFirstBlockWithTime(startTime - TIMESTAMP_WINDOW, &start_block);
+ const Optional<int> start_height = locked_chain->findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, &start_block);
const Optional<int> tip_height = locked_chain->getHeight();
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, tip_height && start_height ? *tip_height - *start_height + 1 : 0);
}
@@ -1764,6 +1771,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, const uint256& stop_block, const WalletRescanReserver& reserver, bool fUpdate)
{
int64_t nNow = GetTime();
+ int64_t start_time = GetTimeMillis();
assert(reserver.isReserved());
@@ -1772,101 +1780,99 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
WalletLogPrintf("Rescan started from block %s...\n", start_block.ToString());
+ fAbortRescan = false;
+ ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
+ uint256 tip_hash;
+ // The way the 'block_height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0.
+ Optional<int> block_height = MakeOptional(false, int());
+ double progress_begin;
+ double progress_end;
{
- fAbortRescan = false;
- ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
- uint256 tip_hash;
- // The way the 'block_height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0.
- Optional<int> block_height = MakeOptional(false, int());
- double progress_begin;
- double progress_end;
- {
- auto locked_chain = chain().lock();
- if (Optional<int> tip_height = locked_chain->getHeight()) {
- tip_hash = locked_chain->getBlockHash(*tip_height);
- }
- block_height = locked_chain->getBlockHeight(block_hash);
- progress_begin = chain().guessVerificationProgress(block_hash);
- progress_end = chain().guessVerificationProgress(stop_block.IsNull() ? tip_hash : stop_block);
- }
- double progress_current = progress_begin;
- while (block_height && !fAbortRescan && !ShutdownRequested()) {
- if (*block_height % 100 == 0 && progress_end - progress_begin > 0.0) {
- ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), std::max(1, std::min(99, (int)((progress_current - progress_begin) / (progress_end - progress_begin) * 100))));
- }
- if (GetTime() >= nNow + 60) {
- nNow = GetTime();
- WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", *block_height, progress_current);
- }
+ auto locked_chain = chain().lock();
+ if (Optional<int> tip_height = locked_chain->getHeight()) {
+ tip_hash = locked_chain->getBlockHash(*tip_height);
+ }
+ block_height = locked_chain->getBlockHeight(block_hash);
+ progress_begin = chain().guessVerificationProgress(block_hash);
+ progress_end = chain().guessVerificationProgress(stop_block.IsNull() ? tip_hash : stop_block);
+ }
+ double progress_current = progress_begin;
+ while (block_height && !fAbortRescan && !chain().shutdownRequested()) {
+ if (*block_height % 100 == 0 && progress_end - progress_begin > 0.0) {
+ ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), std::max(1, std::min(99, (int)((progress_current - progress_begin) / (progress_end - progress_begin) * 100))));
+ }
+ if (GetTime() >= nNow + 60) {
+ nNow = GetTime();
+ WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", *block_height, progress_current);
+ }
- CBlock block;
- if (chain().findBlock(block_hash, &block) && !block.IsNull()) {
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
- if (!locked_chain->getBlockHeight(block_hash)) {
- // Abort scan if current block is no longer active, to prevent
- // marking transactions as coming from the wrong block.
- // TODO: This should return success instead of failure, see
- // https://github.com/bitcoin/bitcoin/pull/14711#issuecomment-458342518
- result.last_failed_block = block_hash;
- result.status = ScanResult::FAILURE;
- break;
- }
- for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- SyncTransaction(block.vtx[posInBlock], block_hash, posInBlock, fUpdate);
- }
- // scan succeeded, record block as most recent successfully scanned
- result.last_scanned_block = block_hash;
- result.last_scanned_height = *block_height;
- } else {
- // could not scan block, keep scanning but record this block as the most recent failure
+ CBlock block;
+ if (chain().findBlock(block_hash, &block) && !block.IsNull()) {
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
+ if (!locked_chain->getBlockHeight(block_hash)) {
+ // Abort scan if current block is no longer active, to prevent
+ // marking transactions as coming from the wrong block.
+ // TODO: This should return success instead of failure, see
+ // https://github.com/bitcoin/bitcoin/pull/14711#issuecomment-458342518
result.last_failed_block = block_hash;
result.status = ScanResult::FAILURE;
+ break;
+ }
+ for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
+ SyncTransaction(block.vtx[posInBlock], block_hash, posInBlock, fUpdate);
}
- if (block_hash == stop_block) {
+ // scan succeeded, record block as most recent successfully scanned
+ result.last_scanned_block = block_hash;
+ result.last_scanned_height = *block_height;
+ } else {
+ // could not scan block, keep scanning but record this block as the most recent failure
+ result.last_failed_block = block_hash;
+ result.status = ScanResult::FAILURE;
+ }
+ if (block_hash == stop_block) {
+ break;
+ }
+ {
+ auto locked_chain = chain().lock();
+ Optional<int> tip_height = locked_chain->getHeight();
+ if (!tip_height || *tip_height <= block_height || !locked_chain->getBlockHeight(block_hash)) {
+ // break successfully when rescan has reached the tip, or
+ // previous block is no longer on the chain due to a reorg
break;
}
- {
- auto locked_chain = chain().lock();
- Optional<int> tip_height = locked_chain->getHeight();
- if (!tip_height || *tip_height <= block_height || !locked_chain->getBlockHeight(block_hash)) {
- // break successfully when rescan has reached the tip, or
- // previous block is no longer on the chain due to a reorg
- break;
- }
- // increment block and verification progress
- block_hash = locked_chain->getBlockHash(++*block_height);
- progress_current = chain().guessVerificationProgress(block_hash);
+ // increment block and verification progress
+ block_hash = locked_chain->getBlockHash(++*block_height);
+ progress_current = chain().guessVerificationProgress(block_hash);
- // handle updated tip hash
- const uint256 prev_tip_hash = tip_hash;
- tip_hash = locked_chain->getBlockHash(*tip_height);
- if (stop_block.IsNull() && prev_tip_hash != tip_hash) {
- // in case the tip has changed, update progress max
- progress_end = chain().guessVerificationProgress(tip_hash);
- }
+ // handle updated tip hash
+ const uint256 prev_tip_hash = tip_hash;
+ tip_hash = locked_chain->getBlockHash(*tip_height);
+ if (stop_block.IsNull() && prev_tip_hash != tip_hash) {
+ // in case the tip has changed, update progress max
+ progress_end = chain().guessVerificationProgress(tip_hash);
}
}
- ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
- if (block_height && fAbortRescan) {
- WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", *block_height, progress_current);
- result.status = ScanResult::USER_ABORT;
- } else if (block_height && ShutdownRequested()) {
- WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", *block_height, progress_current);
- result.status = ScanResult::USER_ABORT;
- }
+ }
+ ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
+ if (block_height && fAbortRescan) {
+ WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", *block_height, progress_current);
+ result.status = ScanResult::USER_ABORT;
+ } else if (block_height && chain().shutdownRequested()) {
+ WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", *block_height, progress_current);
+ result.status = ScanResult::USER_ABORT;
+ } else {
+ WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - start_time);
}
return result;
}
-void CWallet::ReacceptWalletTransactions()
+void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
{
// If transactions aren't being broadcasted, don't let them into local mempool either
if (!fBroadcastTransactions)
return;
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
std::map<int64_t, CWalletTx*> mapSorted;
// Sort pending wallet transactions based on their initial wallet insertion order
@@ -1876,7 +1882,7 @@ void CWallet::ReacceptWalletTransactions()
CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid);
- int nDepth = wtx.GetDepthInMainChain(*locked_chain);
+ int nDepth = wtx.GetDepthInMainChain(locked_chain);
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
@@ -1887,26 +1893,31 @@ void CWallet::ReacceptWalletTransactions()
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
CValidationState state;
- wtx.AcceptToMemoryPool(*locked_chain, state);
+ wtx.AcceptToMemoryPool(locked_chain, state);
}
}
bool CWalletTx::RelayWalletTransaction(interfaces::Chain::Lock& locked_chain)
{
- assert(pwallet->GetBroadcastTransactions());
- if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain(locked_chain) == 0)
- {
- CValidationState state;
- /* GetDepthInMainChain already catches known conflicts. */
- if (InMempool() || AcceptToMemoryPool(locked_chain, state)) {
- pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
- if (pwallet->chain().p2pEnabled()) {
- pwallet->chain().relayTransaction(GetHash());
- return true;
- }
- }
- }
- return false;
+ // Can't relay if wallet is not broadcasting
+ if (!pwallet->GetBroadcastTransactions()) return false;
+ // Don't relay coinbase transactions outside blocks
+ if (IsCoinBase()) return false;
+ // Don't relay abandoned transactions
+ if (isAbandoned()) return false;
+ // Don't relay conflicted or already confirmed transactions
+ if (GetDepthInMainChain(locked_chain) != 0) return false;
+ // Don't relay transactions that aren't accepted to the mempool
+ CValidationState unused_state;
+ if (!InMempool() && !AcceptToMemoryPool(locked_chain, unused_state)) return false;
+ // Don't try to relay if the node is not connected to the p2p network
+ if (!pwallet->chain().p2pEnabled()) return false;
+
+ // Try to relay the transaction
+ pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
+ pwallet->chain().relayTransaction(GetHash());
+
+ return true;
}
std::set<uint256> CWalletTx::GetConflicts() const
@@ -1921,33 +1932,26 @@ std::set<uint256> CWalletTx::GetConflicts() const
return result;
}
+CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate) const
+{
+ auto& amount = m_amounts[type];
+ if (recalculate || !amount.m_cached[filter]) {
+ amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter));
+ }
+ return amount.m_value[filter];
+}
+
CAmount CWalletTx::GetDebit(const isminefilter& filter) const
{
if (tx->vin.empty())
return 0;
CAmount debit = 0;
- if(filter & ISMINE_SPENDABLE)
- {
- if (fDebitCached)
- debit += nDebitCached;
- else
- {
- nDebitCached = pwallet->GetDebit(*tx, ISMINE_SPENDABLE);
- fDebitCached = true;
- debit += nDebitCached;
- }
+ if (filter & ISMINE_SPENDABLE) {
+ debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE);
}
- if(filter & ISMINE_WATCH_ONLY)
- {
- if(fWatchDebitCached)
- debit += nWatchDebitCached;
- else
- {
- nWatchDebitCached = pwallet->GetDebit(*tx, ISMINE_WATCH_ONLY);
- fWatchDebitCached = true;
- debit += nWatchDebitCached;
- }
+ if (filter & ISMINE_WATCH_ONLY) {
+ debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY);
}
return debit;
}
@@ -1959,28 +1963,12 @@ CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const ismine
return 0;
CAmount credit = 0;
- if (filter & ISMINE_SPENDABLE)
- {
+ if (filter & ISMINE_SPENDABLE) {
// GetBalance can assume transactions in mapWallet won't change
- if (fCreditCached)
- credit += nCreditCached;
- else
- {
- nCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
- fCreditCached = true;
- credit += nCreditCached;
- }
+ credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE);
}
- if (filter & ISMINE_WATCH_ONLY)
- {
- if (fWatchCreditCached)
- credit += nWatchCreditCached;
- else
- {
- nWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
- fWatchCreditCached = true;
- credit += nWatchCreditCached;
- }
+ if (filter & ISMINE_WATCH_ONLY) {
+ credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY);
}
return credit;
}
@@ -1988,11 +1976,7 @@ CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const ismine
CAmount CWalletTx::GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache) const
{
if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
- if (fUseCache && fImmatureCreditCached)
- return nImmatureCreditCached;
- nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
- fImmatureCreditCached = true;
- return nImmatureCreditCached;
+ return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
}
return 0;
@@ -2003,23 +1987,15 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
if (pwallet == nullptr)
return 0;
+ // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
+ bool allow_cache = filter == ISMINE_SPENDABLE || filter == ISMINE_WATCH_ONLY;
+
// Must wait until coinbase is safely deep enough in the chain before valuing it
if (IsImmatureCoinBase(locked_chain))
return 0;
- CAmount* cache = nullptr;
- bool* cache_used = nullptr;
-
- if (filter == ISMINE_SPENDABLE) {
- cache = &nAvailableCreditCached;
- cache_used = &fAvailableCreditCached;
- } else if (filter == ISMINE_WATCH_ONLY) {
- cache = &nAvailableWatchCreditCached;
- cache_used = &fAvailableWatchCreditCached;
- }
-
- if (fUseCache && cache_used && *cache_used) {
- return *cache;
+ if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) {
+ return m_amounts[AVAILABLE_CREDIT].m_value[filter];
}
CAmount nCredit = 0;
@@ -2035,22 +2011,17 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
}
}
- if (cache) {
- *cache = nCredit;
- assert(cache_used);
- *cache_used = true;
+ if (allow_cache) {
+ m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit);
}
+
return nCredit;
}
CAmount CWalletTx::GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache) const
{
if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
- if (fUseCache && fImmatureWatchCreditCached)
- return nImmatureWatchCreditCached;
- nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
- fImmatureWatchCreditCached = true;
- return nImmatureWatchCreditCached;
+ return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
}
return 0;
@@ -2111,185 +2082,95 @@ bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
return CTransaction(tx1) == CTransaction(tx2);
}
-std::vector<uint256> CWallet::ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime)
+// Rebroadcast transactions from the wallet. We do this on a random timer
+// to slightly obfuscate which transactions come from our wallet.
+//
+// Ideally, we'd only resend transactions that we think should have been
+// mined in the most recent block. Any transaction that wasn't in the top
+// blockweight of transactions in the mempool shouldn't have been mined,
+// and so is probably just sitting in the mempool waiting to be confirmed.
+// Rebroadcasting does nothing to speed up confirmation and only damages
+// privacy.
+void CWallet::ResendWalletTransactions()
{
- std::vector<uint256> result;
+ // During reindex, importing and IBD, old wallet transactions become
+ // unconfirmed. Don't resend them as that would spam other nodes.
+ if (!chain().isReadyToBroadcast()) return;
- LOCK(cs_wallet);
-
- // Sort them in chronological order
- std::multimap<unsigned int, CWalletTx*> mapSorted;
- for (std::pair<const uint256, CWalletTx>& item : mapWallet)
- {
- CWalletTx& wtx = item.second;
- // Don't rebroadcast if newer than nTime:
- if (wtx.nTimeReceived > nTime)
- continue;
- mapSorted.insert(std::make_pair(wtx.nTimeReceived, &wtx));
- }
- for (const std::pair<const unsigned int, CWalletTx*>& item : mapSorted)
- {
- CWalletTx& wtx = *item.second;
- if (wtx.RelayWalletTransaction(locked_chain)) {
- result.push_back(wtx.GetHash());
- }
- }
- return result;
-}
-
-void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman)
-{
// Do this infrequently and randomly to avoid giving away
// that these are our transactions.
- if (GetTime() < nNextResend || !fBroadcastTransactions)
- return;
+ if (GetTime() < nNextResend || !fBroadcastTransactions) return;
bool fFirst = (nNextResend == 0);
nNextResend = GetTime() + GetRand(30 * 60);
- if (fFirst)
- return;
+ if (fFirst) return;
// Only do it if there's been a new block since last time
- if (nBestBlockTime < nLastResend)
- return;
+ if (m_best_block_time < nLastResend) return;
nLastResend = GetTime();
- // Rebroadcast unconfirmed txes older than 5 minutes before the last
- // block was found:
- auto locked_chain = chain().assumeLocked(); // Temporary. Removed in upcoming lock cleanup
- std::vector<uint256> relayed = ResendWalletTransactionsBefore(*locked_chain, nBestBlockTime-5*60);
- if (!relayed.empty())
- WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size());
-}
-
-/** @} */ // end of mapWallet
-
-
+ int relayed_tx_count = 0;
-
-/** @defgroup Actions
- *
- * @{
- */
-
-
-CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) const
-{
- CAmount nTotal = 0;
- {
+ { // locked_chain and cs_wallet scope
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- if (pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) >= min_depth) {
- nTotal += pcoin->GetAvailableCredit(*locked_chain, true, filter);
- }
- }
- }
-
- return nTotal;
-}
-CAmount CWallet::GetUnconfirmedBalance() const
-{
- CAmount nTotal = 0;
- {
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- if (!pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) == 0 && pcoin->InMempool())
- nTotal += pcoin->GetAvailableCredit(*locked_chain);
+ // Relay transactions
+ for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
+ CWalletTx& wtx = item.second;
+ // only rebroadcast unconfirmed txes older than 5 minutes before the
+ // last block was found
+ if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
+ if (wtx.RelayWalletTransaction(*locked_chain)) ++relayed_tx_count;
}
- }
- return nTotal;
-}
+ } // locked_chain and cs_wallet
-CAmount CWallet::GetImmatureBalance() const
-{
- CAmount nTotal = 0;
- {
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- nTotal += pcoin->GetImmatureCredit(*locked_chain);
- }
+ if (relayed_tx_count > 0) {
+ WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed_tx_count);
}
- return nTotal;
}
-CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
+/** @} */ // end of mapWallet
+
+void MaybeResendWalletTxs()
{
- CAmount nTotal = 0;
- {
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
- for (const auto& entry : mapWallet)
- {
- const CWalletTx* pcoin = &entry.second;
- if (!pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) == 0 && pcoin->InMempool())
- nTotal += pcoin->GetAvailableCredit(*locked_chain, true, ISMINE_WATCH_ONLY);
- }
+ for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
+ pwallet->ResendWalletTransactions();
}
- return nTotal;
}
-CAmount CWallet::GetImmatureWatchOnlyBalance() const
+
+/** @defgroup Actions
+ *
+ * @{
+ */
+
+
+CWallet::Balance CWallet::GetBalance(const int min_depth) const
{
- CAmount nTotal = 0;
+ Balance ret;
{
auto locked_chain = chain().lock();
LOCK(cs_wallet);
for (const auto& entry : mapWallet)
{
- const CWalletTx* pcoin = &entry.second;
- nTotal += pcoin->GetImmatureWatchOnlyCredit(*locked_chain);
- }
- }
- return nTotal;
-}
-
-// Calculate total balance in a different way from GetBalance. The biggest
-// difference is that GetBalance sums up all unspent TxOuts paying to the
-// wallet, while this sums up both spent and unspent TxOuts paying to the
-// wallet, and then subtracts the values of TxIns spending from the wallet. This
-// also has fewer restrictions on which unconfirmed transactions are considered
-// trusted.
-CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) const
-{
- auto locked_chain = chain().lock();
- LOCK(cs_wallet);
-
- CAmount balance = 0;
- for (const auto& entry : mapWallet) {
- const CWalletTx& wtx = entry.second;
- const int depth = wtx.GetDepthInMainChain(*locked_chain);
- if (depth < 0 || !locked_chain->checkFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase(*locked_chain)) {
- continue;
- }
-
- // Loop through tx outputs and add incoming payments. For outgoing txs,
- // treat change outputs specially, as part of the amount debited.
- CAmount debit = wtx.GetDebit(filter);
- const bool outgoing = debit > 0;
- for (const CTxOut& out : wtx.tx->vout) {
- if (outgoing && IsChange(out)) {
- debit -= out.nValue;
- } else if (IsMine(out) & filter && depth >= minDepth) {
- balance += out.nValue;
+ 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)};
+ const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(*locked_chain, /* fUseCache */ true, ISMINE_WATCH_ONLY)};
+ if (is_trusted && tx_depth >= min_depth) {
+ ret.m_mine_trusted += tx_credit_mine;
+ ret.m_watchonly_trusted += tx_credit_watchonly;
}
- }
-
- // For outgoing txs, subtract amount debited.
- if (outgoing) {
- balance -= debit;
+ if (!is_trusted && tx_depth == 0 && wtx.InMempool()) {
+ 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);
}
}
-
- return balance;
+ return ret;
}
CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
@@ -2310,7 +2191,6 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t nMaximumCount, const int nMinDepth, const int nMaxDepth) const
{
- AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
vCoins.clear();
@@ -2319,25 +2199,25 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
for (const auto& entry : mapWallet)
{
const uint256& wtxid = entry.first;
- const CWalletTx* pcoin = &entry.second;
+ const CWalletTx& wtx = entry.second;
- if (!locked_chain.checkFinalTx(*pcoin->tx)) {
+ if (!locked_chain.checkFinalTx(*wtx.tx)) {
continue;
}
- if (pcoin->IsImmatureCoinBase(locked_chain))
+ if (wtx.IsImmatureCoinBase(locked_chain))
continue;
- int nDepth = pcoin->GetDepthInMainChain(locked_chain);
+ int nDepth = wtx.GetDepthInMainChain(locked_chain);
if (nDepth < 0)
continue;
// We should not consider coins which aren't at least in our mempool
// It's possible for these to be conflicted via ancestors which we may never be able to detect
- if (nDepth == 0 && !pcoin->InMempool())
+ if (nDepth == 0 && !wtx.InMempool())
continue;
- bool safeTx = pcoin->IsTrusted(locked_chain);
+ bool safeTx = wtx.IsTrusted(locked_chain);
// We should not consider coins from transactions that are replacing
// other transactions.
@@ -2354,7 +2234,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
// be a 1-block reorg away from the chain where transactions A and C
// were accepted to another chain where B, B', and C were all
// accepted.
- if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) {
+ if (nDepth == 0 && wtx.mapValue.count("replaces_txid")) {
safeTx = false;
}
@@ -2366,7 +2246,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
// intending to replace A', but potentially resulting in a scenario
// where A, A', and D could all be accepted (instead of just B and
// D, or just A and A' like the user would want).
- if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) {
+ if (nDepth == 0 && wtx.mapValue.count("replaced_by_txid")) {
safeTx = false;
}
@@ -2377,8 +2257,8 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
if (nDepth < nMinDepth || nDepth > nMaxDepth)
continue;
- for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
- if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount)
+ for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
+ if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount)
continue;
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i)))
@@ -2390,20 +2270,20 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
if (IsSpent(locked_chain, wtxid, i))
continue;
- isminetype mine = IsMine(pcoin->tx->vout[i]);
+ isminetype mine = IsMine(wtx.tx->vout[i]);
if (mine == ISMINE_NO) {
continue;
}
- bool solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey);
+ bool solvable = IsSolvable(*this, wtx.tx->vout[i].scriptPubKey);
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
- vCoins.push_back(COutput(pcoin, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
+ vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
- nTotal += pcoin->tx->vout[i].nValue;
+ nTotal += wtx.tx->vout[i].nValue;
if (nTotal >= nMinimumSumAmount) {
return;
@@ -2420,7 +2300,6 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Chain::Lock& locked_chain) const
{
- AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
std::map<CTxDestination, std::vector<COutput>> result;
@@ -2562,13 +2441,13 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
if (it != mapWallet.end())
{
- const CWalletTx* pcoin = &it->second;
+ const CWalletTx& wtx = it->second;
// Clearly invalid input, fail
- if (pcoin->tx->vout.size() <= outpoint.n)
+ if (wtx.tx->vout.size() <= outpoint.n)
return false;
// Just to calculate the marginal byte size
- nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue;
- setPresetCoins.insert(CInputCoin(pcoin->tx, outpoint.n));
+ nValueFromPresetInputs += wtx.tx->vout[outpoint.n].nValue;
+ setPresetCoins.insert(CInputCoin(wtx.tx, outpoint.n));
} else
return false; // TODO: Allow non-wallet inputs
}
@@ -2614,9 +2493,9 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
return res;
}
-bool CWallet::SignTransaction(CMutableTransaction &tx)
+bool CWallet::SignTransaction(CMutableTransaction& tx)
{
- AssertLockHeld(cs_wallet); // mapWallet
+ AssertLockHeld(cs_wallet);
// sign the new tx
int nIn = 0;
@@ -2692,13 +2571,13 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
return true;
}
-static bool IsCurrentForAntiFeeSniping(interfaces::Chain::Lock& locked_chain)
+static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain)
{
- if (IsInitialBlockDownload()) {
+ if (chain.isInitialBlockDownload()) {
return false;
}
constexpr int64_t MAX_ANTI_FEE_SNIPING_TIP_AGE = 8 * 60 * 60; // in seconds
- if (chainActive.Tip()->GetBlockTime() < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) {
+ if (locked_chain.getBlockTime(*locked_chain.getHeight()) < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) {
return false;
}
return true;
@@ -2708,7 +2587,7 @@ static bool IsCurrentForAntiFeeSniping(interfaces::Chain::Lock& locked_chain)
* Return a height-based locktime for new transactions (uses the height of the
* current chain tip unless we are not synced with the current chain
*/
-static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_chain)
+static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain)
{
uint32_t const height = locked_chain.getHeight().get_value_or(-1);
uint32_t locktime;
@@ -2732,7 +2611,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain::Lock& locked_cha
// enough, that fee sniping isn't a problem yet, but by implementing a fix
// now we ensure code won't be written that makes assumptions about
// nLockTime that preclude a fix later.
- if (IsCurrentForAntiFeeSniping(locked_chain)) {
+ if (IsCurrentForAntiFeeSniping(chain, locked_chain)) {
locktime = height;
// Secondly occasionally randomly pick a nLockTime even further back, so
@@ -2806,7 +2685,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
CMutableTransaction txNew;
- txNew.nLockTime = GetLocktimeForNewTransaction(locked_chain);
+ txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chain);
FeeCalculation feeCalc;
CAmount nFeeNeeded;
@@ -2817,7 +2696,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
LOCK(cs_wallet);
{
std::vector<COutput> vAvailableCoins;
- AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control);
+ AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0, coin_control.m_min_depth);
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
// Create change script that will be used if we need change
@@ -2902,7 +2781,7 @@ 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 (IsDust(txout, ::dustRelayFee))
+ if (IsDust(txout, chain().relayDustFee()))
{
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
@@ -3003,7 +2882,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
// because we must be at the maximum allowed fee.
- if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes))
+ if (nFeeNeeded < chain().relayMinFee().GetFee(nBytes))
{
strFailReason = _("Transaction too large for fee policy");
return false;
@@ -3197,7 +3076,6 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
- auto locked_chain = chain().lock();
LOCK(cs_wallet);
fFirstRunRet = false;
@@ -3230,8 +3108,8 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
{
- AssertLockHeld(cs_wallet); // mapWallet
- DBErrors nZapSelectTxRet = WalletBatch(*database,"cr+").ZapSelectTx(vHashIn, vHashOut);
+ AssertLockHeld(cs_wallet);
+ DBErrors nZapSelectTxRet = WalletBatch(*database, "cr+").ZapSelectTx(vHashIn, vHashOut);
for (uint256 hash : vHashOut) {
const auto& it = mapWallet.find(hash);
wtxOrdered.erase(it->second.m_it_wtxOrdered);
@@ -3257,7 +3135,6 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
MarkDirty();
return DBErrors::LOAD_OK;
-
}
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
@@ -3377,7 +3254,7 @@ bool CWallet::NewKeyPool()
size_t CWallet::KeypoolCountExternalKeys()
{
- AssertLockHeld(cs_wallet); // setExternalKeyPool
+ AssertLockHeld(cs_wallet);
return setExternalKeyPool.size() + set_pre_split_keypool.size();
}
@@ -3607,27 +3484,27 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
LOCK(cs_wallet);
for (const auto& walletEntry : mapWallet)
{
- const CWalletTx *pcoin = &walletEntry.second;
+ const CWalletTx& wtx = walletEntry.second;
- if (!pcoin->IsTrusted(locked_chain))
+ if (!wtx.IsTrusted(locked_chain))
continue;
- if (pcoin->IsImmatureCoinBase(locked_chain))
+ if (wtx.IsImmatureCoinBase(locked_chain))
continue;
- int nDepth = pcoin->GetDepthInMainChain(locked_chain);
- if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
+ int nDepth = wtx.GetDepthInMainChain(locked_chain);
+ if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1))
continue;
- for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++)
+ for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
{
CTxDestination addr;
- if (!IsMine(pcoin->tx->vout[i]))
+ if (!IsMine(wtx.tx->vout[i]))
continue;
- if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr))
+ if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr))
continue;
- CAmount n = IsSpent(locked_chain, walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue;
+ CAmount n = IsSpent(locked_chain, walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
if (!balances.count(addr))
balances[addr] = 0;
@@ -3641,19 +3518,19 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
{
- AssertLockHeld(cs_wallet); // mapWallet
+ AssertLockHeld(cs_wallet);
std::set< std::set<CTxDestination> > groupings;
std::set<CTxDestination> grouping;
for (const auto& walletEntry : mapWallet)
{
- const CWalletTx *pcoin = &walletEntry.second;
+ const CWalletTx& wtx = walletEntry.second;
- if (pcoin->tx->vin.size() > 0)
+ if (wtx.tx->vin.size() > 0)
{
bool any_mine = false;
// group all input addresses with each other
- for (const CTxIn& txin : pcoin->tx->vin)
+ for (const CTxIn& txin : wtx.tx->vin)
{
CTxDestination address;
if(!IsMine(txin)) /* If this input isn't mine, ignore it */
@@ -3667,7 +3544,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
// group change with input addresses
if (any_mine)
{
- for (const CTxOut& txout : pcoin->tx->vout)
+ for (const CTxOut& txout : wtx.tx->vout)
if (IsChange(txout))
{
CTxDestination txoutAddr;
@@ -3684,7 +3561,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
}
// group lone addrs by themselves
- for (const auto& txout : pcoin->tx->vout)
+ for (const auto& txout : wtx.tx->vout)
if (IsMine(txout))
{
CTxDestination address;
@@ -3809,25 +3686,25 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
void CWallet::LockCoin(const COutPoint& output)
{
- AssertLockHeld(cs_wallet); // setLockedCoins
+ AssertLockHeld(cs_wallet);
setLockedCoins.insert(output);
}
void CWallet::UnlockCoin(const COutPoint& output)
{
- AssertLockHeld(cs_wallet); // setLockedCoins
+ AssertLockHeld(cs_wallet);
setLockedCoins.erase(output);
}
void CWallet::UnlockAllCoins()
{
- AssertLockHeld(cs_wallet); // setLockedCoins
+ AssertLockHeld(cs_wallet);
setLockedCoins.clear();
}
bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
{
- AssertLockHeld(cs_wallet); // setLockedCoins
+ AssertLockHeld(cs_wallet);
COutPoint outpt(hash, n);
return (setLockedCoins.count(outpt) > 0);
@@ -3835,7 +3712,7 @@ bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
{
- AssertLockHeld(cs_wallet); // setLockedCoins
+ AssertLockHeld(cs_wallet);
for (std::set<COutPoint>::iterator it = setLockedCoins.begin();
it != setLockedCoins.end(); it++) {
COutPoint outpt = (*it);
@@ -3845,8 +3722,8 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
/** @} */ // end of Actions
-void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CTxDestination, int64_t> &mapKeyBirth) const {
- AssertLockHeld(cs_wallet); // mapKeyMetadata
+void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CTxDestination, int64_t>& mapKeyBirth) const {
+ AssertLockHeld(cs_wallet);
mapKeyBirth.clear();
// get birth times for keys with metadata
@@ -4062,7 +3939,7 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
if (salvage_wallet) {
// Recover readable keypairs:
- CWallet dummyWallet(chain, WalletLocation(), WalletDatabase::CreateDummy());
+ CWallet dummyWallet(&chain, WalletLocation(), WalletDatabase::CreateDummy());
std::string backup_filename;
if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
return false;
@@ -4074,7 +3951,7 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags)
{
- const std::string& walletFile = location.GetName();
+ const std::string& walletFile = WalletDataFilePath(location.GetPath()).string();
// needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
@@ -4082,7 +3959,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
chain.initMessage(_("Zapping all transactions from wallet..."));
- std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(chain, location, WalletDatabase::Create(location.GetPath()));
+ std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, WalletDatabase::Create(location.GetPath()));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DBErrors::LOAD_OK) {
chain.initError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
@@ -4096,7 +3973,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
bool fFirstRun = true;
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
- std::shared_ptr<CWallet> walletInstance(new CWallet(chain, location, WalletDatabase::Create(location.GetPath())), ReleaseWallet);
+ std::shared_ptr<CWallet> walletInstance(new CWallet(&chain, location, WalletDatabase::Create(location.GetPath())), ReleaseWallet);
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK)
{
@@ -4125,7 +4002,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
}
- int prev_version = walletInstance->nWalletVersion;
+ int prev_version = walletInstance->GetVersion();
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
{
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
@@ -4150,8 +4027,8 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
LOCK(walletInstance->cs_wallet);
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
- int max_version = walletInstance->nWalletVersion;
- if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >=FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
+ int max_version = walletInstance->GetVersion();
+ if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >= FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
chain.initError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified."));
return nullptr;
}
@@ -4281,12 +4158,35 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
_("This is the transaction fee you will pay if you send a transaction."));
}
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000);
- if (walletInstance->m_pay_tx_fee < ::minRelayTxFee) {
+ if (walletInstance->m_pay_tx_fee < chain.relayMinFee()) {
chain.initError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
- gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
+ gArgs.GetArg("-paytxfee", ""), chain.relayMinFee().ToString()));
return nullptr;
}
}
+
+ if (gArgs.IsArgSet("-maxtxfee"))
+ {
+ CAmount nMaxFee = 0;
+ if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) {
+ chain.initError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")));
+ return nullptr;
+ }
+ if (nMaxFee > HIGH_MAX_TX_FEE) {
+ chain.initWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
+ }
+ if (CFeeRate(nMaxFee, 1000) < chain.relayMinFee()) {
+ chain.initError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
+ gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString()));
+ return nullptr;
+ }
+ walletInstance->m_default_max_tx_fee = nMaxFee;
+ }
+
+ if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB)
+ chain.initWarning(AmountHighWarn("-minrelaytxfee") + " " +
+ _("The wallet will avoid paying less than the minimum relay fee."));
+
walletInstance->m_confirm_target = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
walletInstance->m_spend_zero_conf_change = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
walletInstance->m_signal_rbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
@@ -4341,12 +4241,11 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// No need to read and scan block if block was created before
// our wallet birthday (as adjusted for block time variability)
if (walletInstance->nTimeFirstKey) {
- if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height)) {
+ if (Optional<int> first_block = locked_chain->findFirstBlockWithTimeAndHeight(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW, rescan_height, nullptr)) {
rescan_height = *first_block;
}
}
- nStart = GetTimeMillis();
{
WalletRescanReserver reserver(walletInstance.get());
if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(locked_chain->getBlockHash(rescan_height), {} /* stop block */, reserver, true /* update */).status)) {
@@ -4354,7 +4253,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
return nullptr;
}
}
- walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nStart);
walletInstance->ChainStateFlushed(locked_chain->getTipLocator());
walletInstance->database->IncrementUpdateCounter();
@@ -4385,8 +4283,8 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
chain.loadWallet(interfaces::MakeWallet(walletInstance));
- // Register with the validation interface. It's ok to do this after rescan since we're still holding cs_main.
- RegisterValidationInterface(walletInstance.get());
+ // Register with the validation interface. It's ok to do this after rescan since we're still holding locked_chain.
+ walletInstance->handleNotifications();
walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
@@ -4399,11 +4297,22 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
return walletInstance;
}
+void CWallet::handleNotifications()
+{
+ m_chain_notifications_handler = m_chain->handleNotifications(*this);
+}
+
void CWallet::postInitProcess()
{
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
+
// Add wallet transactions that aren't already in a block to mempool
// Do this here as mempool requires genesis block to be loaded
- ReacceptWalletTransactions();
+ ReacceptWalletTransactions(*locked_chain);
+
+ // Update wallet transactions with current mempool transactions.
+ chain().requestMempoolTransactions(*this);
}
bool CWallet::BackupWallet(const std::string& strDest)
@@ -4446,8 +4355,6 @@ int CMerkleTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const
if (hashUnset())
return 0;
- AssertLockHeld(cs_main);
-
return locked_chain.getBlockDepth(hashBlock) * (nIndex == -1 ? -1 : 1);
}
@@ -4473,7 +4380,7 @@ bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValid
// user could call sendmoney in a loop and hit spurious out of funds errors
// because we think that this newly generated transaction's change is
// unavailable as we're not yet aware that it is in the mempool.
- bool ret = locked_chain.submitToMemoryPool(tx, pwallet->chain().maxTxFee(), state);
+ bool ret = locked_chain.submitToMemoryPool(tx, pwallet->m_default_max_tx_fee, state);
fInMempool |= ret;
return ret;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 51e3edac34..900af75f4f 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -8,18 +8,19 @@
#include <amount.h>
#include <interfaces/chain.h>
+#include <interfaces/handler.h>
#include <outputtype.h>
#include <policy/feerate.h>
+#include <script/ismine.h>
+#include <script/sign.h>
#include <streams.h>
#include <tinyformat.h>
#include <ui_interface.h>
#include <util/strencodings.h>
-#include <validationinterface.h>
-#include <script/ismine.h>
-#include <script/sign.h>
#include <util/system.h>
-#include <wallet/crypter.h>
+#include <validationinterface.h>
#include <wallet/coinselection.h>
+#include <wallet/crypter.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
@@ -34,26 +35,6 @@
#include <utility>
#include <vector>
-//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
-//! This function will perform salvage on the wallet if requested, as long as only one wallet is
-//! being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
-bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
-
-//! Load wallet databases.
-bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
-
-//! Complete startup of wallets.
-void StartWallets(CScheduler& scheduler);
-
-//! Flush all wallets in preparation for shutdown.
-void FlushWallets();
-
-//! Stop all wallets. Wallets will be flushed first.
-void StopWallets();
-
-//! Close all wallets.
-void UnloadWallets();
-
//! Explicitly unload and delete the wallet.
//! Blocks the current thread after signaling the unload intent so that all
//! wallet clients release the wallet.
@@ -92,6 +73,12 @@ static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6;
static const bool DEFAULT_WALLET_RBF = false;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;
+//! -maxtxfee default
+constexpr CAmount DEFAULT_TRANSACTION_MAXFEE{COIN / 10};
+//! Discourage users to set fees higher than this amount (in satoshis) per kB
+constexpr CAmount HIGH_TX_FEE_PER_KB{COIN / 100};
+//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
+constexpr CAmount HIGH_MAX_TX_FEE{100 * HIGH_TX_FEE_PER_KB};
//! Pre-calculated constants for input size estimation in *virtual size*
static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
@@ -388,24 +375,11 @@ public:
std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
// memory only
- mutable bool fDebitCached;
- mutable bool fCreditCached;
- mutable bool fImmatureCreditCached;
- mutable bool fAvailableCreditCached;
- mutable bool fWatchDebitCached;
- mutable bool fWatchCreditCached;
- mutable bool fImmatureWatchCreditCached;
- mutable bool fAvailableWatchCreditCached;
+ enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
+ CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const;
+ mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
mutable bool fChangeCached;
mutable bool fInMempool;
- mutable CAmount nDebitCached;
- mutable CAmount nCreditCached;
- mutable CAmount nImmatureCreditCached;
- mutable CAmount nAvailableCreditCached;
- mutable CAmount nWatchDebitCached;
- mutable CAmount nWatchCreditCached;
- mutable CAmount nImmatureWatchCreditCached;
- mutable CAmount nAvailableWatchCreditCached;
mutable CAmount nChangeCached;
CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
@@ -422,24 +396,8 @@ public:
nTimeReceived = 0;
nTimeSmart = 0;
fFromMe = false;
- fDebitCached = false;
- fCreditCached = false;
- fImmatureCreditCached = false;
- fAvailableCreditCached = false;
- fWatchDebitCached = false;
- fWatchCreditCached = false;
- fImmatureWatchCreditCached = false;
- fAvailableWatchCreditCached = false;
fChangeCached = false;
fInMempool = false;
- nDebitCached = 0;
- nCreditCached = 0;
- nImmatureCreditCached = 0;
- nAvailableCreditCached = 0;
- nWatchDebitCached = 0;
- nWatchCreditCached = 0;
- nAvailableWatchCreditCached = 0;
- nImmatureWatchCreditCached = 0;
nChangeCached = 0;
nOrderPos = -1;
}
@@ -483,14 +441,10 @@ public:
//! make sure balances are recalculated
void MarkDirty()
{
- fCreditCached = false;
- fAvailableCreditCached = false;
- fImmatureCreditCached = false;
- fWatchDebitCached = false;
- fWatchCreditCached = false;
- fAvailableWatchCreditCached = false;
- fImmatureWatchCreditCached = false;
- fDebitCached = false;
+ m_amounts[DEBIT].Reset();
+ m_amounts[CREDIT].Reset();
+ m_amounts[IMMATURE_CREDIT].Reset();
+ m_amounts[AVAILABLE_CREDIT].Reset();
fChangeCached = false;
}
@@ -505,7 +459,7 @@ public:
CAmount GetCredit(interfaces::Chain::Lock& locked_chain, const isminefilter& filter) const;
CAmount GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true) const;
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(cs_main, pwallet->cs_wallet)". The
+ // 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;
@@ -534,7 +488,7 @@ public:
int64_t GetTxTime() const;
- // RelayWalletTransaction may only be called if fBroadcastTransactions!
+ // Pass this transaction to the node to relay to its peers
bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain);
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
@@ -637,7 +591,7 @@ class WalletRescanReserver; //forward declarations for ScanForWalletTransactions
* A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
* and provides the ability to create new transactions.
*/
-class CWallet final : public CCryptoKeyStore, public CValidationInterface
+class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notifications
{
private:
std::atomic<bool> fAbortRescan{false};
@@ -648,7 +602,7 @@ private:
WalletBatch *encrypted_batch GUARDED_BY(cs_wallet) = nullptr;
//! the current wallet version: clients below this version are not able to load the wallet
- int nWalletVersion = FEATURE_BASE;
+ int nWalletVersion GUARDED_BY(cs_wallet){FEATURE_BASE};
//! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded
int nWalletMaxVersion GUARDED_BY(cs_wallet) = FEATURE_BASE;
@@ -656,6 +610,8 @@ private:
int64_t nNextResend = 0;
int64_t nLastResend = 0;
bool fBroadcastTransactions = false;
+ // Local time that the tip block was received. Used to schedule wallet rebroadcasts.
+ std::atomic<int64_t> m_best_block_time {0};
/**
* Used to keep track of spent outpoints, and
@@ -698,11 +654,11 @@ private:
CHDChain hdChain;
/* HD derive new child key (on internal or external chain) */
- void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- std::set<int64_t> setInternalKeyPool;
+ std::set<int64_t> setInternalKeyPool GUARDED_BY(cs_wallet);
std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_wallet);
- std::set<int64_t> set_pre_split_keypool;
+ std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_wallet);
int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0;
std::map<CKeyID, int64_t> m_pool_key_to_index;
std::atomic<uint64_t> m_wallet_flags{0};
@@ -721,7 +677,7 @@ private:
bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Interface for accessing chain state. */
- interfaces::Chain& m_chain;
+ interfaces::Chain* m_chain;
/** Wallet location which includes wallet name (see WalletLocation). */
WalletLocation m_location;
@@ -784,7 +740,10 @@ public:
unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */
- CWallet(interfaces::Chain& chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database) : m_chain(chain), m_location(location), database(std::move(database))
+ CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database)
+ : m_chain(chain),
+ m_location(location),
+ database(std::move(database))
{
}
@@ -808,8 +767,14 @@ public:
std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet);
+ /** Registered interfaces::Chain::Notifications handler. */
+ std::unique_ptr<interfaces::Handler> m_chain_notifications_handler;
+
+ /** Register the wallet for chain notifications */
+ void handleNotifications();
+
/** Interface for accessing chain state. */
- interfaces::Chain& chain() const { return m_chain; }
+ interfaces::Chain& chain() const { assert(m_chain); return *m_chain; }
const CWalletTx* GetWalletTx(const uint256& hash) const;
@@ -920,8 +885,9 @@ public:
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
void LoadToWallet(const CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void TransactionAddedToMempool(const CTransactionRef& tx) override;
- void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
- void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
+ void BlockConnected(const CBlock& block, const std::vector<CTransactionRef>& vtxConflicted) override;
+ void BlockDisconnected(const CBlock& block) override;
+ void UpdatedBlockTip() override;
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
struct ScanResult {
@@ -941,16 +907,17 @@ public:
};
ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
- void ReacceptWalletTransactions();
- void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- // ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
- std::vector<uint256> ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime);
- CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
- CAmount GetUnconfirmedBalance() const;
- CAmount GetImmatureBalance() const;
- CAmount GetUnconfirmedWatchOnlyBalance() const;
- CAmount GetImmatureWatchOnlyBalance() const;
- CAmount GetLegacyBalance(const isminefilter& filter, int minDepth) const;
+ void ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void ResendWalletTransactions();
+ struct Balance {
+ CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
+ CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
+ CAmount m_mine_immature{0}; //!< Immature coinbases in the main chain
+ CAmount m_watchonly_trusted{0};
+ CAmount m_watchonly_untrusted_pending{0};
+ CAmount m_watchonly_immature{0};
+ };
+ Balance GetBalance(int min_depth = 0) const;
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);
@@ -995,6 +962,8 @@ public:
CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE};
OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE};
OutputType m_default_change_type{DEFAULT_CHANGE_TYPE};
+ /** Absolute maximum transaction fee (in satoshis) used by default for the wallet */
+ CAmount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE};
bool NewKeyPool();
size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -1065,7 +1034,7 @@ public:
unsigned int GetKeyPoolSize() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{
- AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool
+ AssertLockHeld(cs_wallet);
return setInternalKeyPool.size() + setExternalKeyPool.size();
}
@@ -1222,8 +1191,14 @@ public:
bool AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info);
};
+/**
+ * Called periodically by the schedule thread. Prompts individual wallets to resend
+ * their transactions. Actual rebroadcast schedule is managed by the wallets themselves.
+ */
+void MaybeResendWalletTxs();
+
/** A key allocated from the key pool. */
-class CReserveKey final : public CReserveScript
+class CReserveKey
{
protected:
CWallet* pwallet;
@@ -1248,7 +1223,6 @@ public:
void ReturnKey();
bool GetReservedKey(CPubKey &pubkey, bool internal = false);
void KeepKey();
- void KeepScript() override { KeepKey(); }
};
/** RAII object to check and reserve a wallet rescan */
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 2783f83fd6..3122cd6fa4 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -5,7 +5,7 @@
#include <wallet/walletdb.h>
-#include <consensus/tx_verify.h>
+#include <consensus/tx_check.h>
#include <consensus/validation.h>
#include <fs.h>
#include <key_io.h>
@@ -422,8 +422,15 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
strType != "minversion" && strType != "acentry") {
wss.m_unknown_records++;
}
- } catch (...)
- {
+ } catch (const std::exception& e) {
+ if (strErr.empty()) {
+ strErr = e.what();
+ }
+ return false;
+ } catch (...) {
+ if (strErr.empty()) {
+ strErr = "Caught unknown exception in ReadKeyValue";
+ }
return false;
}
return true;
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 797f051189..1ff1e8b840 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -4,7 +4,6 @@
#include <base58.h>
#include <fs.h>
-#include <interfaces/chain.h>
#include <util/system.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
@@ -28,8 +27,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
return nullptr;
}
// dummy chain interface
- auto chain = interfaces::MakeChain();
- std::shared_ptr<CWallet> wallet_instance(new CWallet(*chain, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
+ std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
bool first_run = true;
DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run);
if (load_wallet_ret != DBErrors::LOAD_OK) {
@@ -56,8 +54,7 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
}
// dummy chain interface
- auto chain = interfaces::MakeChain();
- std::shared_ptr<CWallet> wallet_instance(new CWallet(*chain, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
+ std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet);
DBErrors load_wallet_ret;
try {
bool first_run;
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index d779251d56..b227a15556 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -4,6 +4,7 @@
#include <wallet/walletutil.h>
+#include <logging.h>
#include <util/system.h>
fs::path GetWalletDir()
@@ -30,10 +31,14 @@ fs::path GetWalletDir()
static bool IsBerkeleyBtree(const fs::path& path)
{
+ if (!fs::exists(path)) return false;
+
// A Berkeley DB Btree file has at least 4K.
// This check also prevents opening lock files.
boost::system::error_code ec;
- if (fs::file_size(path, ec) < 4096) return false;
+ auto size = fs::file_size(path, ec);
+ if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
+ if (size < 4096) return false;
fsbridge::ifstream file(path, std::ios::binary);
if (!file.is_open()) return false;
@@ -54,8 +59,14 @@ std::vector<fs::path> ListWalletDir()
const fs::path wallet_dir = GetWalletDir();
const size_t offset = wallet_dir.string().size() + 1;
std::vector<fs::path> paths;
+ boost::system::error_code ec;
+
+ for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) {
+ if (ec) {
+ LogPrintf("%s: %s %s\n", __func__, ec.message(), it->path().string());
+ continue;
+ }
- for (auto it = fs::recursive_directory_iterator(wallet_dir); it != fs::recursive_directory_iterator(); ++it) {
// Get wallet path relative to walletdir by removing walletdir from the wallet path.
// This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
const fs::path path = it->path().string().substr(offset);