aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am20
-rw-r--r--src/Makefile.bench.include9
-rw-r--r--src/Makefile.test.include298
-rw-r--r--src/addrman.cpp13
-rw-r--r--src/addrman.h3
-rw-r--r--src/bench/poly1305.cpp41
-rw-r--r--src/bench/rpc_mempool.cpp43
-rw-r--r--src/bitcoin-cli.cpp6
-rw-r--r--src/blockfilter.cpp2
-rw-r--r--src/blockfilter.h5
-rw-r--r--src/chain.h49
-rw-r--r--src/compat/assumptions.h18
-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/flatfile.cpp98
-rw-r--r--src/flatfile.h96
-rw-r--r--src/fs.cpp2
-rw-r--r--src/index/txindex.cpp8
-rw-r--r--src/init.cpp92
-rw-r--r--src/interfaces/README.md4
-rw-r--r--src/interfaces/chain.cpp198
-rw-r--r--src/interfaces/chain.h156
-rw-r--r--src/interfaces/node.cpp2
-rw-r--r--src/interfaces/wallet.cpp270
-rw-r--r--src/interfaces/wallet.h3
-rw-r--r--src/net.cpp11
-rw-r--r--src/net.h16
-rw-r--r--src/net_processing.cpp183
-rw-r--r--src/node/coin.cpp22
-rw-r--r--src/node/coin.h22
-rw-r--r--src/node/transaction.cpp40
-rw-r--r--src/node/transaction.h20
-rw-r--r--src/policy/rbf.cpp2
-rw-r--r--src/policy/rbf.h2
-rw-r--r--src/psbt.cpp172
-rw-r--r--src/psbt.h58
-rw-r--r--src/qt/bitcoingui.cpp2
-rw-r--r--src/qt/rpcconsole.cpp1
-rw-r--r--src/qt/walletcontroller.cpp15
-rw-r--r--src/qt/walletframe.cpp24
-rw-r--r--src/qt/walletframe.h6
-rw-r--r--src/qt/walletmodeltransaction.cpp2
-rw-r--r--src/rest.cpp4
-rw-r--r--src/rpc/blockchain.cpp90
-rw-r--r--src/rpc/blockchain.h5
-rw-r--r--src/rpc/client.cpp7
-rw-r--r--src/rpc/mining.cpp10
-rw-r--r--src/rpc/misc.cpp40
-rw-r--r--src/rpc/net.cpp16
-rw-r--r--src/rpc/rawtransaction.cpp381
-rw-r--r--src/rpc/server.cpp61
-rw-r--r--src/rpc/server.h31
-rw-r--r--src/rpc/util.cpp57
-rw-r--r--src/rpc/util.h15
-rw-r--r--src/test/blockfilter_tests.cpp6
-rw-r--r--src/test/crypto_tests.cpp169
-rw-r--r--src/test/flatfile_tests.cpp123
-rw-r--r--src/test/fuzz/fuzz.h2
-rw-r--r--src/test/main.cpp7
-rw-r--r--src/test/rpc_tests.cpp5
-rw-r--r--src/test/test_bitcoin.cpp60
-rw-r--r--src/test/test_bitcoin.h4
-rw-r--r--src/test/test_bitcoin_main.cpp30
-rw-r--r--src/test/util_tests.cpp43
-rw-r--r--src/test/validation_tests.cpp (renamed from src/test/main_tests.cpp)6
-rw-r--r--src/txmempool.cpp2
-rw-r--r--src/txmempool.h4
-rw-r--r--src/ui_interface.cpp2
-rw-r--r--src/ui_interface.h7
-rw-r--r--src/util/strencodings.h12
-rw-r--r--src/util/system.cpp56
-rw-r--r--src/util/system.h19
-rw-r--r--src/validation.cpp510
-rw-r--r--src/validation.h23
-rw-r--r--src/wallet/db.cpp8
-rw-r--r--src/wallet/db.h3
-rw-r--r--src/wallet/feebumper.cpp24
-rw-r--r--src/wallet/fees.cpp26
-rw-r--r--src/wallet/fees.h8
-rw-r--r--src/wallet/init.cpp18
-rw-r--r--src/wallet/psbtwallet.cpp10
-rw-r--r--src/wallet/psbtwallet.h8
-rw-r--r--src/wallet/rpcdump.cpp62
-rw-r--r--src/wallet/rpcwallet.cpp189
-rw-r--r--src/wallet/rpcwallet.h9
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp3
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp9
-rw-r--r--src/wallet/test/wallet_test_fixture.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp2
-rw-r--r--src/wallet/wallet.cpp458
-rw-r--r--src/wallet/wallet.h41
-rw-r--r--src/wallet/walletdb.cpp11
-rw-r--r--src/wallet/walletutil.cpp15
97 files changed, 2848 insertions, 2204 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d491530ca1..0385c825ea 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -128,6 +128,7 @@ BITCOIN_CORE_H = \
core_io.h \
core_memusage.h \
cuckoocache.h \
+ flatfile.h \
fs.h \
httprpc.h \
httpserver.h \
@@ -153,6 +154,7 @@ BITCOIN_CORE_H = \
netaddress.h \
netbase.h \
netmessagemaker.h \
+ node/coin.h \
node/transaction.h \
noui.h \
optional.h \
@@ -247,6 +249,7 @@ libbitcoin_server_a_SOURCES = \
chain.cpp \
checkpoints.cpp \
consensus/tx_verify.cpp \
+ flatfile.cpp \
httprpc.cpp \
httpserver.cpp \
index/base.cpp \
@@ -260,6 +263,7 @@ libbitcoin_server_a_SOURCES = \
miner.cpp \
net.cpp \
net_processing.cpp \
+ node/coin.cpp \
node/transaction.cpp \
noui.cpp \
outputtype.cpp \
@@ -342,6 +346,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 \
@@ -428,8 +434,8 @@ libbitcoin_common_a_SOURCES = \
netaddress.cpp \
netbase.cpp \
policy/feerate.cpp \
- psbt.cpp \
protocol.cpp \
+ psbt.cpp \
scheduler.cpp \
script/descriptor.cpp \
script/ismine.cpp \
@@ -491,6 +497,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) \
@@ -566,12 +574,14 @@ bitcoin_wallet_LDADD = \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
+ $(LIBBITCOIN_ZMQ) \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
- $(LIBSECP256K1)
+ $(LIBSECP256K1) \
+ $(LIBUNIVALUE)
-bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
+bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS)
#
# bitcoinconsensus library #
@@ -638,13 +648,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 5e787ca222..b84360e84b 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -26,10 +26,12 @@ bench_bench_bitcoin_SOURCES = \
bench/gcs_filter.cpp \
bench/merkle_root.cpp \
bench/mempool_eviction.cpp \
+ bench/rpc_mempool.cpp \
bench/verify_script.cpp \
bench/base58.cpp \
bench/bech32.cpp \
bench/lockedpool.cpp \
+ bench/poly1305.cpp \
bench/prevector.cpp
nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES)
@@ -37,6 +39,7 @@ nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES)
bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/
bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
bench_bench_bitcoin_LDADD = \
+ $(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_WALLET) \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
@@ -47,7 +50,9 @@ bench_bench_bitcoin_LDADD = \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
$(LIBSECP256K1) \
- $(LIBUNIVALUE)
+ $(LIBUNIVALUE) \
+ $(EVENT_PTHREADS_LIBS) \
+ $(EVENT_LIBS)
if ENABLE_ZMQ
bench_bench_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
@@ -57,7 +62,7 @@ if ENABLE_WALLET
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
endif
-bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
+bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS)
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 84bc326cfe..a6e0785616 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -51,14 +51,32 @@ RAW_TEST_FILES =
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
BITCOIN_TEST_SUITE = \
- test/test_bitcoin_main.cpp \
+ test/main.cpp \
test/test_bitcoin.h \
test/test_bitcoin.cpp
FUZZ_SUITE = \
+ test/test_bitcoin.h \
+ test/test_bitcoin.cpp \
test/fuzz/fuzz.cpp \
test/fuzz/fuzz.h
+FUZZ_SUITE_LD_COMMON = \
+ $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_COMMON) \
+ $(LIBBITCOIN_UTIL) \
+ $(LIBBITCOIN_CONSENSUS) \
+ $(LIBBITCOIN_CRYPTO) \
+ $(LIBUNIVALUE) \
+ $(LIBLEVELDB) \
+ $(LIBLEVELDB_SSE42) \
+ $(BOOST_LIBS) \
+ $(LIBMEMENV) \
+ $(LIBSECP256K1) \
+ $(EVENT_LIBS) \
+ $(CRYPTO_LIBS) \
+ $(EVENT_PTHREADS_LIBS)
+
# test_bitcoin binary #
BITCOIN_TESTS =\
test/arith_uint256_tests.cpp \
@@ -83,6 +101,7 @@ BITCOIN_TESTS =\
test/cuckoocache_tests.cpp \
test/denialofservice_tests.cpp \
test/descriptor_tests.cpp \
+ test/flatfile_tests.cpp \
test/fs_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
@@ -90,7 +109,7 @@ BITCOIN_TESTS =\
test/key_tests.cpp \
test/limitedmap_tests.cpp \
test/dbwrapper_tests.cpp \
- test/main_tests.cpp \
+ test/validation_tests.cpp \
test/mempool_tests.cpp \
test/merkle_tests.cpp \
test/merkleblock_tests.cpp \
@@ -169,7 +188,7 @@ test_test_bitcoin_LDADD += $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDC
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
if ENABLE_ZMQ
-test_test_bitcoin_LDADD += $(ZMQ_LIBS)
+test_test_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
if ENABLE_FUZZ
@@ -177,358 +196,127 @@ test_fuzz_block_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_block_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_DESERIALIZE=1
test_fuzz_block_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_block_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_block_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_block_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_transaction_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTRANSACTION_DESERIALIZE=1
test_fuzz_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_transaction_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_transaction_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_blocklocator_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blocklocator_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKLOCATOR_DESERIALIZE=1
test_fuzz_blocklocator_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blocklocator_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blocklocator_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blocklocator_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blocklocator_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_blockmerkleroot_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blockmerkleroot_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKMERKLEROOT=1
test_fuzz_blockmerkleroot_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockmerkleroot_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blockmerkleroot_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blockmerkleroot_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blockmerkleroot_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_addrman_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_addrman_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRMAN_DESERIALIZE=1
test_fuzz_addrman_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_addrman_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_addrman_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_addrman_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_addrman_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_blockheader_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blockheader_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKHEADER_DESERIALIZE=1
test_fuzz_blockheader_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockheader_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blockheader_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blockheader_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blockheader_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_banentry_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_banentry_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBANENTRY_DESERIALIZE=1
test_fuzz_banentry_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_banentry_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_banentry_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_banentry_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_banentry_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_txundo_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_txundo_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTXUNDO_DESERIALIZE=1
test_fuzz_txundo_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_txundo_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_txundo_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_txundo_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_txundo_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_blockundo_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blockundo_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKUNDO_DESERIALIZE=1
test_fuzz_blockundo_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockundo_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blockundo_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blockundo_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blockundo_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_coins_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_coins_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DCOINS_DESERIALIZE=1
test_fuzz_coins_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_coins_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_coins_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_coins_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_coins_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_netaddr_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_netaddr_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DNETADDR_DESERIALIZE=1
test_fuzz_netaddr_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_netaddr_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_netaddr_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_netaddr_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_netaddr_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_script_flags_SOURCES = $(FUZZ_SUITE) test/fuzz/script_flags.cpp
test_fuzz_script_flags_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_flags_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_flags_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_script_flags_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_script_flags_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_script_flags_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_service_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_service_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSERVICE_DESERIALIZE=1
test_fuzz_service_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_service_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_service_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_service_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_service_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_messageheader_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_messageheader_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGEHEADER_DESERIALIZE=1
test_fuzz_messageheader_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_messageheader_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_messageheader_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_messageheader_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_messageheader_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_address_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_address_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDRESS_DESERIALIZE=1
test_fuzz_address_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_address_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_address_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_address_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_address_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_inv_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_inv_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DINV_DESERIALIZE=1
test_fuzz_inv_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_inv_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_inv_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_inv_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_inv_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_bloomfilter_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_bloomfilter_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOOMFILTER_DESERIALIZE=1
test_fuzz_bloomfilter_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_bloomfilter_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_bloomfilter_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_bloomfilter_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_bloomfilter_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_diskblockindex_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_diskblockindex_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DDISKBLOCKINDEX_DESERIALIZE=1
test_fuzz_diskblockindex_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_diskblockindex_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_diskblockindex_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_diskblockindex_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_diskblockindex_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_txoutcompressor_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_txoutcompressor_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTXOUTCOMPRESSOR_DESERIALIZE=1
test_fuzz_txoutcompressor_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_txoutcompressor_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_txoutcompressor_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_txoutcompressor_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_txoutcompressor_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_blocktransactions_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blocktransactions_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKTRANSACTIONS_DESERIALIZE=1
test_fuzz_blocktransactions_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blocktransactions_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blocktransactions_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blocktransactions_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blocktransactions_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_blocktransactionsrequest_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
test_fuzz_blocktransactionsrequest_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCKTRANSACTIONSREQUEST_DESERIALIZE=1
test_fuzz_blocktransactionsrequest_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blocktransactionsrequest_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-test_fuzz_blocktransactionsrequest_deserialize_LDADD = \
- $(LIBUNIVALUE) \
- $(LIBBITCOIN_SERVER) \
- $(LIBBITCOIN_COMMON) \
- $(LIBBITCOIN_UTIL) \
- $(LIBBITCOIN_CONSENSUS) \
- $(LIBBITCOIN_CRYPTO) \
- $(LIBBITCOIN_CRYPTO_SSE41) \
- $(LIBBITCOIN_CRYPTO_AVX2) \
- $(LIBBITCOIN_CRYPTO_SHANI) \
- $(LIBSECP256K1)
-test_fuzz_blocktransactionsrequest_deserialize_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+test_fuzz_blocktransactionsrequest_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
endif # ENABLE_FUZZ
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
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/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/rpc_mempool.cpp b/src/bench/rpc_mempool.cpp
new file mode 100644
index 0000000000..67d8a25564
--- /dev/null
+++ b/src/bench/rpc_mempool.cpp
@@ -0,0 +1,43 @@
+// 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 <policy/policy.h>
+#include <rpc/blockchain.h>
+#include <txmempool.h>
+
+#include <univalue.h>
+
+#include <list>
+#include <vector>
+
+static void AddTx(const CTransactionRef& tx, const CAmount& fee, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
+{
+ LockPoints lp;
+ pool.addUnchecked(CTxMemPoolEntry(tx, fee, /* time */ 0, /* height */ 1, /* spendsCoinbase */ false, /* sigOpCost */ 4, lp));
+}
+
+static void RpcMempool(benchmark::State& state)
+{
+ CTxMemPool pool;
+ LOCK2(cs_main, pool.cs);
+
+ for (int i = 0; i < 1000; ++i) {
+ CMutableTransaction tx = CMutableTransaction();
+ tx.vin.resize(1);
+ tx.vin[0].scriptSig = CScript() << OP_1;
+ tx.vin[0].scriptWitness.stack.push_back({1});
+ tx.vout.resize(1);
+ tx.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
+ tx.vout[0].nValue = i;
+ const CTransactionRef tx_r{MakeTransactionRef(tx)};
+ AddTx(tx_r, /* fee */ i, pool);
+ }
+
+ while (state.KeepRunning()) {
+ (void)MempoolToJSON(pool, /*verbose*/ true);
+ }
+}
+
+BENCHMARK(RpcMempool, 40);
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/blockfilter.cpp b/src/blockfilter.cpp
index bcf24047ff..e15213c552 100644
--- a/src/blockfilter.cpp
+++ b/src/blockfilter.cpp
@@ -251,6 +251,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..e5e087ed5a 100644
--- a/src/blockfilter.h
+++ b/src/blockfilter.h
@@ -83,9 +83,10 @@ 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,
};
/**
@@ -95,7 +96,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.h b/src/chain.h
index 5a6f10b84f..2b6d2d082c 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -8,6 +8,7 @@
#include <arith_uint256.h>
#include <consensus/params.h>
+#include <flatfile.h>
#include <primitives/block.h>
#include <tinyformat.h>
#include <uint256.h>
@@ -90,46 +91,6 @@ public:
}
};
-struct CDiskBlockPos
-{
- int nFile;
- unsigned int nPos;
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITE(VARINT(nFile, VarIntMode::NONNEGATIVE_SIGNED));
- READWRITE(VARINT(nPos));
- }
-
- CDiskBlockPos() {
- SetNull();
- }
-
- CDiskBlockPos(int nFileIn, unsigned int nPosIn) {
- nFile = nFileIn;
- nPos = nPosIn;
- }
-
- friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) {
- return (a.nFile == b.nFile && a.nPos == b.nPos);
- }
-
- friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) {
- return !(a == b);
- }
-
- void SetNull() { nFile = -1; nPos = 0; }
- bool IsNull() const { return (nFile == -1); }
-
- std::string ToString() const
- {
- return strprintf("CDiskBlockPos(nFile=%i, nPos=%i)", nFile, nPos);
- }
-
-};
-
enum BlockStatus: uint32_t {
//! Unused.
BLOCK_VALID_UNKNOWN = 0,
@@ -266,8 +227,8 @@ public:
nNonce = block.nNonce;
}
- CDiskBlockPos GetBlockPos() const {
- CDiskBlockPos ret;
+ FlatFilePos GetBlockPos() const {
+ FlatFilePos ret;
if (nStatus & BLOCK_HAVE_DATA) {
ret.nFile = nFile;
ret.nPos = nDataPos;
@@ -275,8 +236,8 @@ public:
return ret;
}
- CDiskBlockPos GetUndoPos() const {
- CDiskBlockPos ret;
+ FlatFilePos GetUndoPos() const {
+ FlatFilePos ret;
if (nStatus & BLOCK_HAVE_UNDO) {
ret.nFile = nFile;
ret.nPos = nUndoPos;
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/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/flatfile.cpp b/src/flatfile.cpp
new file mode 100644
index 0000000000..8a8f7b681c
--- /dev/null
+++ b/src/flatfile.cpp
@@ -0,0 +1,98 @@
+// 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 <stdexcept>
+
+#include <flatfile.h>
+#include <logging.h>
+#include <tinyformat.h>
+#include <util/system.h>
+
+FlatFileSeq::FlatFileSeq(fs::path dir, const char* prefix, size_t chunk_size) :
+ m_dir(std::move(dir)),
+ m_prefix(prefix),
+ m_chunk_size(chunk_size)
+{
+ if (chunk_size == 0) {
+ throw std::invalid_argument("chunk_size must be positive");
+ }
+}
+
+std::string FlatFilePos::ToString() const
+{
+ return strprintf("FlatFilePos(nFile=%i, nPos=%i)", nFile, nPos);
+}
+
+fs::path FlatFileSeq::FileName(const FlatFilePos& pos) const
+{
+ return m_dir / strprintf("%s%05u.dat", m_prefix, pos.nFile);
+}
+
+FILE* FlatFileSeq::Open(const FlatFilePos& pos, bool read_only)
+{
+ if (pos.IsNull()) {
+ return nullptr;
+ }
+ fs::path path = FileName(pos);
+ fs::create_directories(path.parent_path());
+ FILE* file = fsbridge::fopen(path, read_only ? "rb": "rb+");
+ if (!file && !read_only)
+ file = fsbridge::fopen(path, "wb+");
+ if (!file) {
+ LogPrintf("Unable to open file %s\n", path.string());
+ return nullptr;
+ }
+ if (pos.nPos && fseek(file, pos.nPos, SEEK_SET)) {
+ LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string());
+ fclose(file);
+ return nullptr;
+ }
+ return file;
+}
+
+size_t FlatFileSeq::Allocate(const FlatFilePos& pos, size_t add_size, bool& out_of_space)
+{
+ out_of_space = false;
+
+ unsigned int n_old_chunks = (pos.nPos + m_chunk_size - 1) / m_chunk_size;
+ unsigned int n_new_chunks = (pos.nPos + add_size + m_chunk_size - 1) / m_chunk_size;
+ if (n_new_chunks > n_old_chunks) {
+ size_t old_size = pos.nPos;
+ size_t new_size = n_new_chunks * m_chunk_size;
+ size_t inc_size = new_size - old_size;
+
+ if (CheckDiskSpace(m_dir, inc_size)) {
+ FILE *file = Open(pos);
+ if (file) {
+ LogPrintf("Pre-allocating up to position 0x%x in %s%05u.dat\n", new_size, m_prefix, pos.nFile);
+ AllocateFileRange(file, pos.nPos, inc_size);
+ fclose(file);
+ return inc_size;
+ }
+ } else {
+ out_of_space = true;
+ }
+ }
+ return 0;
+}
+
+bool FlatFileSeq::Flush(const FlatFilePos& pos, bool finalize)
+{
+ FILE* file = Open(FlatFilePos(pos.nFile, 0)); // Avoid fseek to nPos
+ if (!file) {
+ return error("%s: failed to open file %d", __func__, pos.nFile);
+ }
+ if (finalize && !TruncateFile(file, pos.nPos)) {
+ fclose(file);
+ return error("%s: failed to truncate file %d", __func__, pos.nFile);
+ }
+ if (!FileCommit(file)) {
+ fclose(file);
+ return error("%s: failed to commit file %d", __func__, pos.nFile);
+ }
+
+ fclose(file);
+ return true;
+}
diff --git a/src/flatfile.h b/src/flatfile.h
new file mode 100644
index 0000000000..374ceff411
--- /dev/null
+++ b/src/flatfile.h
@@ -0,0 +1,96 @@
+// 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_FLATFILE_H
+#define BITCOIN_FLATFILE_H
+
+#include <string>
+
+#include <fs.h>
+#include <serialize.h>
+
+struct FlatFilePos
+{
+ int nFile;
+ unsigned int nPos;
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ READWRITE(VARINT(nFile, VarIntMode::NONNEGATIVE_SIGNED));
+ READWRITE(VARINT(nPos));
+ }
+
+ FlatFilePos() : nFile(-1), nPos(0) {}
+
+ FlatFilePos(int nFileIn, unsigned int nPosIn) :
+ nFile(nFileIn),
+ nPos(nPosIn)
+ {}
+
+ friend bool operator==(const FlatFilePos &a, const FlatFilePos &b) {
+ return (a.nFile == b.nFile && a.nPos == b.nPos);
+ }
+
+ friend bool operator!=(const FlatFilePos &a, const FlatFilePos &b) {
+ return !(a == b);
+ }
+
+ void SetNull() { nFile = -1; nPos = 0; }
+ bool IsNull() const { return (nFile == -1); }
+
+ std::string ToString() const;
+};
+
+/**
+ * FlatFileSeq represents a sequence of numbered files storing raw data. This class facilitates
+ * access to and efficient management of these files.
+ */
+class FlatFileSeq
+{
+private:
+ const fs::path m_dir;
+ const char* const m_prefix;
+ const size_t m_chunk_size;
+
+public:
+ /**
+ * Constructor
+ *
+ * @param dir The base directory that all files live in.
+ * @param prefix A short prefix given to all file names.
+ * @param chunk_size Disk space is pre-allocated in multiples of this amount.
+ */
+ FlatFileSeq(fs::path dir, const char* prefix, size_t chunk_size);
+
+ /** Get the name of the file at the given position. */
+ fs::path FileName(const FlatFilePos& pos) const;
+
+ /** Open a handle to the file at the given position. */
+ FILE* Open(const FlatFilePos& pos, bool read_only = false);
+
+ /**
+ * Allocate additional space in a file after the given starting position. The amount allocated
+ * will be the minimum multiple of the sequence chunk size greater than add_size.
+ *
+ * @param[in] pos The starting position that bytes will be allocated after.
+ * @param[in] add_size The minimum number of bytes to be allocated.
+ * @param[out] out_of_space Whether the allocation failed due to insufficient disk space.
+ * @return The number of bytes successfully allocated.
+ */
+ size_t Allocate(const FlatFilePos& pos, size_t add_size, bool& out_of_space);
+
+ /**
+ * Commit a file to disk, and optionally truncate off extra pre-allocated bytes if final.
+ *
+ * @param[in] pos The first unwritten position in the file to be flushed.
+ * @param[in] finalize True if no more data will be written to this file.
+ * @return true on success, false on failure.
+ */
+ bool Flush(const FlatFilePos& pos, bool finalize = false);
+};
+
+#endif // BITCOIN_FLATFILE_H
diff --git a/src/fs.cpp b/src/fs.cpp
index 3c8f4c0247..f937f64e04 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -160,6 +160,7 @@ static std::string openmodeToStr(std::ios_base::openmode mode)
void ifstream::open(const fs::path& p, std::ios_base::openmode mode)
{
close();
+ mode |= std::ios_base::in;
m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
if (m_file == nullptr) {
return;
@@ -183,6 +184,7 @@ void ifstream::close()
void ofstream::open(const fs::path& p, std::ios_base::openmode mode)
{
close();
+ mode |= std::ios_base::out;
m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
if (m_file == nullptr) {
return;
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index 10bc8419dd..7367ec7cb6 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -16,7 +16,7 @@ constexpr char DB_TXINDEX_BLOCK = 'T';
std::unique_ptr<TxIndex> g_txindex;
-struct CDiskTxPos : public CDiskBlockPos
+struct CDiskTxPos : public FlatFilePos
{
unsigned int nTxOffset; // after header
@@ -24,11 +24,11 @@ struct CDiskTxPos : public CDiskBlockPos
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
- READWRITEAS(CDiskBlockPos, *this);
+ READWRITEAS(FlatFilePos, *this);
READWRITE(VARINT(nTxOffset));
}
- CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn) : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {
+ CDiskTxPos(const FlatFilePos &blockIn, unsigned int nTxOffsetIn) : FlatFilePos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {
}
CDiskTxPos() {
@@ -36,7 +36,7 @@ struct CDiskTxPos : public CDiskBlockPos
}
void SetNull() {
- CDiskBlockPos::SetNull();
+ FlatFilePos::SetNull();
nTxOffset = 0;
}
};
diff --git a/src/init.cpp b/src/init.cpp
index 0013319ad5..6564ce5b9a 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -53,6 +53,8 @@
#include <stdio.h>
#ifndef WIN32
+#include <attributes.h>
+#include <cerrno>
#include <signal.h>
#include <sys/stat.h>
#endif
@@ -92,6 +94,32 @@ std::unique_ptr<BanMan> g_banman;
static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
+/**
+ * The PID file facilities.
+ */
+static const char* BITCOIN_PID_FILENAME = "bitcoind.pid";
+
+static fs::path GetPidFile()
+{
+ return AbsPathForConfigVal(fs::path(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)));
+}
+
+NODISCARD static bool CreatePidFile()
+{
+ FILE* file = fsbridge::fopen(GetPidFile(), "w");
+ if (file) {
+#ifdef WIN32
+ fprintf(file, "%d\n", GetCurrentProcessId());
+#else
+ fprintf(file, "%d\n", getpid());
+#endif
+ fclose(file);
+ return true;
+ } else {
+ return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno)));
+ }
+}
+
//////////////////////////////////////////////////////////////////////////////
//
// Shutdown
@@ -260,13 +288,13 @@ void Shutdown(InitInterfaces& interfaces)
}
#endif
-#ifndef WIN32
try {
- fs::remove(GetPidFile());
+ if (!fs::remove(GetPidFile())) {
+ LogPrintf("%s: Unable to remove PID file: File does not exist\n", __func__);
+ }
} catch (const fs::filesystem_error& e) {
- LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
+ LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
}
-#endif
interfaces.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
@@ -352,7 +380,7 @@ void SetupServerArgs()
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS);
- gArgs.AddArg("-dbcache=<n>", strprintf("Set database cache size in MiB (%d to %d, default: %d)", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS);
gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", false, OptionsCategory::OPTIONS);
@@ -364,11 +392,7 @@ void SetupServerArgs()
gArgs.AddArg("-par=<n>", strprintf("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)",
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), false, OptionsCategory::OPTIONS);
-#ifndef WIN32
gArgs.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), false, OptionsCategory::OPTIONS);
-#else
- hidden_args.emplace_back("-pid");
-#endif
gArgs.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), false, OptionsCategory::OPTIONS);
@@ -476,7 +500,7 @@ void SetupServerArgs()
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)",
+ 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)", // TODO move setting to wallet
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);
@@ -644,8 +668,8 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
if (fReindex) {
int nFile = 0;
while (true) {
- CDiskBlockPos pos(nFile, 0);
- if (!fs::exists(GetBlockPosFilename(pos, "blk")))
+ FlatFilePos pos(nFile, 0);
+ if (!fs::exists(GetBlockPosFilename(pos)))
break; // No block files left to reindex
FILE *file = OpenBlockFile(pos, true);
if (!file)
@@ -815,7 +839,7 @@ void InitParameterInteraction()
// Warn if unrecognized section name are present in the config file.
for (const auto& section : gArgs.GetUnrecognizedSections()) {
- InitWarning(strprintf(_("Section [%s] is not recognized."), section));
+ InitWarning(strprintf("%s:%i " + _("Section [%s] is not recognized."), section.m_file, section.m_line, section.m_name));
}
}
@@ -1200,9 +1224,10 @@ bool AppInitMain(InitInterfaces& interfaces)
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization
-#ifndef WIN32
- CreatePidFile(GetPidFile(), getpid());
-#endif
+ if (!CreatePidFile()) {
+ // Detailed error printed inside CreatePidFile().
+ return false;
+ }
if (LogInstance().m_print_to_file) {
if (gArgs.GetBoolArg("-shrinkdebugfile", LogInstance().DefaultShrinkDebugFile())) {
// Do this first since it both loads a bunch of debug.log into memory,
@@ -1443,11 +1468,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();
@@ -1519,7 +1544,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)) {
@@ -1528,18 +1553,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) {
@@ -1644,11 +1676,11 @@ bool AppInitMain(InitInterfaces& interfaces)
// ********************************************************* Step 11: import blocks
- if (!CheckDiskSpace(/* additional_bytes */ 0, /* blocks_dir */ false)) {
+ if (!CheckDiskSpace(GetDataDir())) {
InitError(strprintf(_("Error: Disk space is low for %s"), GetDataDir()));
return false;
}
- if (!CheckDiskSpace(/* additional_bytes */ 0, /* blocks_dir */ true)) {
+ if (!CheckDiskSpace(GetBlocksDir())) {
InitError(strprintf(_("Error: Disk space is low for %s"), GetBlocksDir()));
return false;
}
diff --git a/src/interfaces/README.md b/src/interfaces/README.md
index 57d41df746..f77d172153 100644
--- a/src/interfaces/README.md
+++ b/src/interfaces/README.md
@@ -2,9 +2,9 @@
The following interfaces are defined here:
-* [`Chain`](chain.h) — used by wallet to access blockchain and mempool state. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
+* [`Chain`](chain.h) — used by wallet to access blockchain and mempool state. Added in [#14437](https://github.com/bitcoin/bitcoin/pull/14437), [#14711](https://github.com/bitcoin/bitcoin/pull/14711), [#15288](https://github.com/bitcoin/bitcoin/pull/15288), and [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
-* [`ChainClient`](chain.h) — used by node to start & stop `Chain` clients. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
+* [`ChainClient`](chain.h) — used by node to start & stop `Chain` clients. Added in [#14437](https://github.com/bitcoin/bitcoin/pull/14437).
* [`Node`](node.h) — used by GUI to start & stop bitcoin node. Added in [#10244](https://github.com/bitcoin/bitcoin/pull/10244).
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index da810bc5e6..e409ced601 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -6,11 +6,29 @@
#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 <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>
@@ -132,6 +150,17 @@ class LockImpl : public Chain::Lock
}
return nullopt;
}
+ bool checkFinalTx(const CTransaction& tx) override
+ {
+ LockAnnotation lock(::cs_main);
+ return CheckFinalTx(tx);
+ }
+ bool submitToMemoryPool(const CTransactionRef& tx, CAmount absurd_fee, CValidationState& state) override
+ {
+ LockAnnotation lock(::cs_main);
+ return AcceptToMemoryPool(::mempool, state, tx, nullptr /* missing inputs */, nullptr /* txn replaced */,
+ false /* bypass limits */, absurd_fee);
+ }
};
class LockingStateImpl : public LockImpl, public UniqueLock<CCriticalSection>
@@ -139,6 +168,94 @@ 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 ChainStateFlushed(const CBlockLocator& locator) override { m_notifications->ChainStateFlushed(locator); }
+ void ResendWalletTransactions(int64_t best_block_time, CConnman*) override
+ {
+ // `cs_main` is always held when this method is called, so it is safe to
+ // call `assumeLocked`. This is awkward, and the `assumeLocked` method
+ // should be able to be removed entirely if `ResendWalletTransactions`
+ // is replaced by a wallet timer as suggested in
+ // https://github.com/bitcoin/bitcoin/issues/15619
+ auto locked_chain = m_chain.assumeLocked();
+ m_notifications->ResendWalletTransactions(*locked_chain, best_block_time);
+ }
+ 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:
@@ -172,13 +289,92 @@ public:
}
return true;
}
+ void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(coins); }
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash));
}
+ RBFTransactionState isRBFOptIn(const CTransaction& tx) override
+ {
+ LOCK(::mempool.cs);
+ return IsRBFOptIn(tx, ::mempool);
+ }
+ bool hasDescendantsInMempool(const uint256& txid) override
+ {
+ LOCK(::mempool.cs);
+ auto it = ::mempool.GetIter(txid);
+ return it && (*it)->GetCountWithDescendants() > 1;
+ }
+ void relayTransaction(const uint256& txid) override
+ {
+ CInv inv(MSG_TX, txid);
+ g_connman->ForEachNode([&inv](CNode* node) { node->PushInventory(inv); });
+ }
+ void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
+ {
+ ::mempool.GetTransactionAncestry(txid, ancestors, descendants);
+ }
+ bool checkChainLimits(const CTransactionRef& tx) override
+ {
+ LockPoints lp;
+ CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp);
+ CTxMemPool::setEntries ancestors;
+ auto limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ auto limit_ancestor_size = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
+ auto limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
+ auto limit_descendant_size = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
+ std::string unused_error_string;
+ LOCK(::mempool.cs);
+ return ::mempool.CalculateMemPoolAncestors(entry, ancestors, limit_ancestor_count, limit_ancestor_size,
+ limit_descendant_count, limit_descendant_size, unused_error_string);
+ }
+ CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override
+ {
+ return ::feeEstimator.estimateSmartFee(num_blocks, calc, conservative);
+ }
+ unsigned int estimateMaxBlocks() override
+ {
+ return ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ }
+ CFeeRate mempoolMinFee() override
+ {
+ return ::mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ }
+ CFeeRate relayMinFee() override { return ::minRelayTxFee; }
+ CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
+ CFeeRate relayDustFee() override { return ::dustRelayFee; }
+ CAmount maxTxFee() override { return ::maxTxFee; }
+ bool getPruneMode() override { return ::fPruneMode; }
+ bool p2pEnabled() override { return g_connman != nullptr; }
+ 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);
+ }
+ 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 3a54b9164e..d11a59241e 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -5,21 +5,58 @@
#ifndef BITCOIN_INTERFACES_CHAIN_H
#define BITCOIN_INTERFACES_CHAIN_H
-#include <optional.h>
+#include <optional.h> // For Optional and nullopt
+#include <primitives/transaction.h> // For CTransactionRef
#include <memory>
+#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
class CBlock;
+class CFeeRate;
+class CRPCCommand;
class CScheduler;
+class CValidationState;
+class Coin;
class uint256;
+enum class RBFTransactionState;
struct CBlockLocator;
+struct FeeCalculation;
namespace interfaces {
-//! Interface for giving wallet processes access to blockchain state.
+class Handler;
+class Wallet;
+
+//! Interface giving clients (wallet processes, maybe other analysis tools in
+//! the future) ability to access to the chain state, receive notifications,
+//! estimate fees, and submit transactions.
+//!
+//! TODO: Current chain methods are too low level, exposing too much of the
+//! internal workings of the bitcoin node, and not being very convenient to use.
+//! Chain methods should be cleaned up and simplified over time. Examples:
+//!
+//! * The Chain::lock() method, which lets clients delay chain tip updates
+//! should be removed when clients are able to respond to updates
+//! 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).
+//!
+//! * The initMessages() and loadWallet() methods which the wallet uses to send
+//! 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).
class Chain
{
public:
@@ -102,6 +139,14 @@ public:
//! is guaranteed to be an ancestor of the block used to create the
//! locator.
virtual Optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
+
+ //! Check if transaction will be final given chain height current time.
+ virtual bool checkFinalTx(const CTransaction& tx) = 0;
+
+ //! Add transaction to memory pool if the transaction fee is below the
+ //! amount specified by absurd_fee. Returns false if the transaction
+ //! could not be added due to the fee or for another reason.
+ virtual bool submitToMemoryPool(const CTransactionRef& tx, CAmount absurd_fee, CValidationState& state) = 0;
};
//! Return Lock interface. Chain is locked when this is called, and
@@ -124,9 +169,116 @@ 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;
+
+ //! Check if transaction is RBF opt in.
+ virtual RBFTransactionState isRBFOptIn(const CTransaction& tx) = 0;
+
+ //! Check if transaction has descendants in mempool.
+ virtual bool hasDescendantsInMempool(const uint256& txid) = 0;
+
+ //! Relay transaction.
+ virtual void relayTransaction(const uint256& txid) = 0;
+
+ //! Calculate mempool ancestor and descendant counts for the given transaction.
+ virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) = 0;
+
+ //! Check if transaction will pass the mempool's chain limits.
+ virtual bool checkChainLimits(const CTransactionRef& tx) = 0;
+
+ //! Estimate smart fee.
+ virtual CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc = nullptr) = 0;
+
+ //! Fee estimator max target.
+ virtual unsigned int estimateMaxBlocks() = 0;
+
+ //! Mempool minimum fee.
+ virtual CFeeRate mempoolMinFee() = 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;
+
+ //! 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;
+
+ //! Check if pruning is enabled.
+ virtual bool getPruneMode() = 0;
+
+ //! Check if p2p enabled.
+ virtual bool p2pEnabled() = 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;
+
+ //! Send init message.
+ virtual void initMessage(const std::string& message) = 0;
+
+ //! Send init warning.
+ virtual void initWarning(const std::string& message) = 0;
+
+ //! Send init error.
+ virtual void initError(const std::string& message) = 0;
+
+ //! 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 ChainStateFlushed(const CBlockLocator& locator) {}
+ virtual void ResendWalletTransactions(Lock& locked_chain, int64_t best_block_time) {}
+ };
+
+ //! 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;
+
+ //! 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 96bde7e9f2..6f7dce0c24 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -275,7 +275,7 @@ public:
}
std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
{
- return MakeHandler(::uiInterface.LoadWallet_connect([fn](std::shared_ptr<CWallet> wallet) { fn(MakeWallet(wallet)); }));
+ return MakeHandler(::uiInterface.LoadWallet_connect([fn](std::unique_ptr<Wallet>& wallet) { fn(std::move(wallet)); }));
}
std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override
{
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 5b5430037c..60173b29ac 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -47,8 +47,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
@@ -56,7 +54,7 @@ public:
auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
CValidationState state;
- if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, g_connman.get(), state)) {
+ if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, state)) {
reject_reason = state.GetRejectReason();
return false;
}
@@ -99,17 +97,13 @@ 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 CheckFinalTx 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;
result.lock_time = wtx.tx->nLockTime;
- result.is_final = CheckFinalTx(*wtx.tx);
+ result.is_final = locked_chain.checkFinalTx(*wtx.tx);
result.is_trusted = wtx.IsTrusted(locked_chain);
result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase();
@@ -135,55 +129,55 @@ WalletTxOut MakeWalletTxOut(interfaces::Chain::Lock& locked_chain,
class WalletImpl : public Wallet
{
public:
- explicit WalletImpl(const std::shared_ptr<CWallet>& wallet) : m_shared_wallet(wallet), m_wallet(*wallet.get()) {}
+ explicit WalletImpl(const std::shared_ptr<CWallet>& wallet) : m_wallet(wallet) {}
bool encryptWallet(const SecureString& wallet_passphrase) override
{
- return m_wallet.EncryptWallet(wallet_passphrase);
+ return m_wallet->EncryptWallet(wallet_passphrase);
}
- bool isCrypted() override { return m_wallet.IsCrypted(); }
- bool lock() override { return m_wallet.Lock(); }
- bool unlock(const SecureString& wallet_passphrase) override { return m_wallet.Unlock(wallet_passphrase); }
- bool isLocked() override { return m_wallet.IsLocked(); }
+ bool isCrypted() override { return m_wallet->IsCrypted(); }
+ bool lock() override { return m_wallet->Lock(); }
+ bool unlock(const SecureString& wallet_passphrase) override { return m_wallet->Unlock(wallet_passphrase); }
+ bool isLocked() override { return m_wallet->IsLocked(); }
bool changeWalletPassphrase(const SecureString& old_wallet_passphrase,
const SecureString& new_wallet_passphrase) override
{
- return m_wallet.ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase);
+ return m_wallet->ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase);
}
- void abortRescan() override { m_wallet.AbortRescan(); }
- bool backupWallet(const std::string& filename) override { return m_wallet.BackupWallet(filename); }
- std::string getWalletName() override { return m_wallet.GetName(); }
+ void abortRescan() override { m_wallet->AbortRescan(); }
+ bool backupWallet(const std::string& filename) override { return m_wallet->BackupWallet(filename); }
+ std::string getWalletName() override { return m_wallet->GetName(); }
bool getKeyFromPool(bool internal, CPubKey& pub_key) override
{
- return m_wallet.GetKeyFromPool(pub_key, internal);
+ return m_wallet->GetKeyFromPool(pub_key, internal);
}
- bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet.GetPubKey(address, pub_key); }
- bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet.GetKey(address, key); }
- bool isSpendable(const CTxDestination& dest) override { return IsMine(m_wallet, dest) & ISMINE_SPENDABLE; }
- bool haveWatchOnly() override { return m_wallet.HaveWatchOnly(); };
+ bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet->GetPubKey(address, pub_key); }
+ bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet->GetKey(address, key); }
+ bool isSpendable(const CTxDestination& dest) override { return IsMine(*m_wallet, dest) & ISMINE_SPENDABLE; }
+ bool haveWatchOnly() override { return m_wallet->HaveWatchOnly(); };
bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::string& purpose) override
{
- return m_wallet.SetAddressBook(dest, name, purpose);
+ return m_wallet->SetAddressBook(dest, name, purpose);
}
bool delAddressBook(const CTxDestination& dest) override
{
- return m_wallet.DelAddressBook(dest);
+ return m_wallet->DelAddressBook(dest);
}
bool getAddress(const CTxDestination& dest,
std::string* name,
isminetype* is_mine,
std::string* purpose) override
{
- LOCK(m_wallet.cs_wallet);
- auto it = m_wallet.mapAddressBook.find(dest);
- if (it == m_wallet.mapAddressBook.end()) {
+ LOCK(m_wallet->cs_wallet);
+ auto it = m_wallet->mapAddressBook.find(dest);
+ if (it == m_wallet->mapAddressBook.end()) {
return false;
}
if (name) {
*name = it->second.name;
}
if (is_mine) {
- *is_mine = IsMine(m_wallet, dest);
+ *is_mine = IsMine(*m_wallet, dest);
}
if (purpose) {
*purpose = it->second.purpose;
@@ -192,52 +186,52 @@ public:
}
std::vector<WalletAddress> getAddresses() override
{
- LOCK(m_wallet.cs_wallet);
+ LOCK(m_wallet->cs_wallet);
std::vector<WalletAddress> result;
- for (const auto& item : m_wallet.mapAddressBook) {
- result.emplace_back(item.first, IsMine(m_wallet, item.first), item.second.name, item.second.purpose);
+ for (const auto& item : m_wallet->mapAddressBook) {
+ result.emplace_back(item.first, IsMine(*m_wallet, item.first), item.second.name, item.second.purpose);
}
return result;
}
- void learnRelatedScripts(const CPubKey& key, OutputType type) override { m_wallet.LearnRelatedScripts(key, type); }
+ void learnRelatedScripts(const CPubKey& key, OutputType type) override { m_wallet->LearnRelatedScripts(key, type); }
bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override
{
- LOCK(m_wallet.cs_wallet);
- return m_wallet.AddDestData(dest, key, value);
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->AddDestData(dest, key, value);
}
bool eraseDestData(const CTxDestination& dest, const std::string& key) override
{
- LOCK(m_wallet.cs_wallet);
- return m_wallet.EraseDestData(dest, key);
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->EraseDestData(dest, key);
}
std::vector<std::string> getDestValues(const std::string& prefix) override
{
- LOCK(m_wallet.cs_wallet);
- return m_wallet.GetDestValues(prefix);
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->GetDestValues(prefix);
}
void lockCoin(const COutPoint& output) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.LockCoin(output);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->LockCoin(output);
}
void unlockCoin(const COutPoint& output) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.UnlockCoin(output);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->UnlockCoin(output);
}
bool isLockedCoin(const COutPoint& output) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.IsLockedCoin(output.hash, output.n);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->IsLockedCoin(output.hash, output.n);
}
void listLockedCoins(std::vector<COutPoint>& outputs) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.ListLockedCoins(outputs);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->ListLockedCoins(outputs);
}
std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients,
const CCoinControl& coin_control,
@@ -246,25 +240,25 @@ public:
CAmount& fee,
std::string& fail_reason) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- auto pending = MakeUnique<PendingWalletTxImpl>(m_wallet);
- if (!m_wallet.CreateTransaction(*locked_chain, recipients, pending->m_tx, pending->m_key, fee, change_pos,
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ auto pending = MakeUnique<PendingWalletTxImpl>(*m_wallet);
+ if (!m_wallet->CreateTransaction(*locked_chain, recipients, pending->m_tx, pending->m_key, fee, change_pos,
fail_reason, coin_control, sign)) {
return {};
}
return std::move(pending);
}
- bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet.TransactionCanBeAbandoned(txid); }
+ bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet->TransactionCanBeAbandoned(txid); }
bool abandonTransaction(const uint256& txid) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.AbandonTransaction(*locked_chain, txid);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->AbandonTransaction(*locked_chain, txid);
}
bool transactionCanBeBumped(const uint256& txid) override
{
- return feebumper::TransactionCanBeBumped(&m_wallet, txid);
+ return feebumper::TransactionCanBeBumped(m_wallet.get(), txid);
}
bool createBumpTransaction(const uint256& txid,
const CCoinControl& coin_control,
@@ -274,46 +268,46 @@ public:
CAmount& new_fee,
CMutableTransaction& mtx) override
{
- return feebumper::CreateTransaction(&m_wallet, txid, coin_control, total_fee, errors, old_fee, new_fee, mtx) ==
+ return feebumper::CreateTransaction(m_wallet.get(), txid, coin_control, total_fee, errors, old_fee, new_fee, mtx) ==
feebumper::Result::OK;
}
- bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(&m_wallet, mtx); }
+ bool signBumpTransaction(CMutableTransaction& mtx) override { return feebumper::SignTransaction(m_wallet.get(), mtx); }
bool commitBumpTransaction(const uint256& txid,
CMutableTransaction&& mtx,
std::vector<std::string>& errors,
uint256& bumped_txid) override
{
- return feebumper::CommitTransaction(&m_wallet, txid, std::move(mtx), errors, bumped_txid) ==
+ return feebumper::CommitTransaction(m_wallet.get(), txid, std::move(mtx), errors, bumped_txid) ==
feebumper::Result::OK;
}
CTransactionRef getTx(const uint256& txid) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- auto mi = m_wallet.mapWallet.find(txid);
- if (mi != m_wallet.mapWallet.end()) {
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ auto mi = m_wallet->mapWallet.find(txid);
+ if (mi != m_wallet->mapWallet.end()) {
return mi->second.tx;
}
return {};
}
WalletTx getWalletTx(const uint256& txid) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- auto mi = m_wallet.mapWallet.find(txid);
- if (mi != m_wallet.mapWallet.end()) {
- return MakeWalletTx(*locked_chain, m_wallet, mi->second);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ auto mi = m_wallet->mapWallet.find(txid);
+ if (mi != m_wallet->mapWallet.end()) {
+ return MakeWalletTx(*locked_chain, *m_wallet, mi->second);
}
return {};
}
std::vector<WalletTx> getWalletTxs() override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
std::vector<WalletTx> result;
- result.reserve(m_wallet.mapWallet.size());
- for (const auto& entry : m_wallet.mapWallet) {
- result.emplace_back(MakeWalletTx(*locked_chain, m_wallet, entry.second));
+ result.reserve(m_wallet->mapWallet.size());
+ for (const auto& entry : m_wallet->mapWallet) {
+ result.emplace_back(MakeWalletTx(*locked_chain, *m_wallet, entry.second));
}
return result;
}
@@ -322,16 +316,16 @@ public:
int& num_blocks,
int64_t& block_time) override
{
- auto locked_chain = m_wallet.chain().lock(true /* try_lock */);
+ auto locked_chain = m_wallet->chain().lock(true /* try_lock */);
if (!locked_chain) {
return false;
}
- TRY_LOCK(m_wallet.cs_wallet, locked_wallet);
+ TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
if (!locked_wallet) {
return false;
}
- auto mi = m_wallet.mapWallet.find(txid);
- if (mi == m_wallet.mapWallet.end()) {
+ auto mi = m_wallet->mapWallet.find(txid);
+ if (mi == m_wallet->mapWallet.end()) {
return false;
}
if (Optional<int> height = locked_chain->getHeight()) {
@@ -350,37 +344,37 @@ public:
bool& in_mempool,
int& num_blocks) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- auto mi = m_wallet.mapWallet.find(txid);
- if (mi != m_wallet.mapWallet.end()) {
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ auto mi = m_wallet->mapWallet.find(txid);
+ if (mi != m_wallet->mapWallet.end()) {
num_blocks = locked_chain->getHeight().get_value_or(-1);
in_mempool = mi->second.InMempool();
order_form = mi->second.vOrderForm;
tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
- return MakeWalletTx(*locked_chain, m_wallet, mi->second);
+ return MakeWalletTx(*locked_chain, *m_wallet, mi->second);
}
return {};
}
WalletBalances getBalances() override
{
WalletBalances result;
- result.balance = m_wallet.GetBalance();
- result.unconfirmed_balance = m_wallet.GetUnconfirmedBalance();
- result.immature_balance = m_wallet.GetImmatureBalance();
- result.have_watch_only = m_wallet.HaveWatchOnly();
+ result.balance = m_wallet->GetBalance();
+ result.unconfirmed_balance = m_wallet->GetUnconfirmedBalance();
+ result.immature_balance = m_wallet->GetImmatureBalance();
+ 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 = m_wallet->GetBalance(ISMINE_WATCH_ONLY);
+ result.unconfirmed_watch_only_balance = m_wallet->GetUnconfirmedWatchOnlyBalance();
+ result.immature_watch_only_balance = m_wallet->GetImmatureWatchOnlyBalance();
}
return result;
}
bool tryGetBalances(WalletBalances& balances, int& num_blocks) override
{
- auto locked_chain = m_wallet.chain().lock(true /* try_lock */);
+ auto locked_chain = m_wallet->chain().lock(true /* try_lock */);
if (!locked_chain) return false;
- TRY_LOCK(m_wallet.cs_wallet, locked_wallet);
+ TRY_LOCK(m_wallet->cs_wallet, locked_wallet);
if (!locked_wallet) {
return false;
}
@@ -388,68 +382,68 @@ 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(); }
CAmount getAvailableBalance(const CCoinControl& coin_control) override
{
- return m_wallet.GetAvailableBalance(&coin_control);
+ return m_wallet->GetAvailableBalance(&coin_control);
}
isminetype txinIsMine(const CTxIn& txin) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.IsMine(txin);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->IsMine(txin);
}
isminetype txoutIsMine(const CTxOut& txout) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.IsMine(txout);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->IsMine(txout);
}
CAmount getDebit(const CTxIn& txin, isminefilter filter) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.GetDebit(txin, filter);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->GetDebit(txin, filter);
}
CAmount getCredit(const CTxOut& txout, isminefilter filter) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
- return m_wallet.GetCredit(txout, filter);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->GetCredit(txout, filter);
}
CoinsList listCoins() override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
CoinsList result;
- for (const auto& entry : m_wallet.ListCoins(*locked_chain)) {
+ for (const auto& entry : m_wallet->ListCoins(*locked_chain)) {
auto& group = result[entry.first];
for (const auto& coin : entry.second) {
group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i),
- MakeWalletTxOut(*locked_chain, m_wallet, *coin.tx, coin.i, coin.nDepth));
+ MakeWalletTxOut(*locked_chain, *m_wallet, *coin.tx, coin.i, coin.nDepth));
}
}
return result;
}
std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) override
{
- auto locked_chain = m_wallet.chain().lock();
- LOCK(m_wallet.cs_wallet);
+ auto locked_chain = m_wallet->chain().lock();
+ LOCK(m_wallet->cs_wallet);
std::vector<WalletTxOut> result;
result.reserve(outputs.size());
for (const auto& output : outputs) {
result.emplace_back();
- auto it = m_wallet.mapWallet.find(output.hash);
- if (it != m_wallet.mapWallet.end()) {
+ auto it = m_wallet->mapWallet.find(output.hash);
+ if (it != m_wallet->mapWallet.end()) {
int depth = it->second.GetDepthInMainChain(*locked_chain);
if (depth >= 0) {
- result.back() = MakeWalletTxOut(*locked_chain, m_wallet, it->second, output.n, depth);
+ result.back() = MakeWalletTxOut(*locked_chain, *m_wallet, it->second, output.n, depth);
}
}
}
return result;
}
- CAmount getRequiredFee(unsigned int tx_bytes) override { return GetRequiredFee(m_wallet, tx_bytes); }
+ CAmount getRequiredFee(unsigned int tx_bytes) override { return GetRequiredFee(*m_wallet, tx_bytes); }
CAmount getMinimumFee(unsigned int tx_bytes,
const CCoinControl& coin_control,
int* returned_target,
@@ -457,55 +451,54 @@ public:
{
FeeCalculation fee_calc;
CAmount result;
- result = GetMinimumFee(m_wallet, tx_bytes, coin_control, ::mempool, ::feeEstimator, &fee_calc);
+ result = GetMinimumFee(*m_wallet, tx_bytes, coin_control, &fee_calc);
if (returned_target) *returned_target = fee_calc.returnedTarget;
if (reason) *reason = fee_calc.reason;
return result;
}
- unsigned int getConfirmTarget() override { return m_wallet.m_confirm_target; }
- bool hdEnabled() override { return m_wallet.IsHDEnabled(); }
- bool canGetAddresses() override { return m_wallet.CanGetAddresses(); }
- 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; }
+ unsigned int getConfirmTarget() override { return m_wallet->m_confirm_target; }
+ bool hdEnabled() override { return m_wallet->IsHDEnabled(); }
+ bool canGetAddresses() override { return m_wallet->CanGetAddresses(); }
+ 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; }
void remove() override
{
- RemoveWallet(m_shared_wallet);
+ RemoveWallet(m_wallet);
}
std::unique_ptr<Handler> handleUnload(UnloadFn fn) override
{
- return MakeHandler(m_wallet.NotifyUnload.connect(fn));
+ return MakeHandler(m_wallet->NotifyUnload.connect(fn));
}
std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override
{
- return MakeHandler(m_wallet.ShowProgress.connect(fn));
+ return MakeHandler(m_wallet->ShowProgress.connect(fn));
}
std::unique_ptr<Handler> handleStatusChanged(StatusChangedFn fn) override
{
- return MakeHandler(m_wallet.NotifyStatusChanged.connect([fn](CCryptoKeyStore*) { fn(); }));
+ return MakeHandler(m_wallet->NotifyStatusChanged.connect([fn](CCryptoKeyStore*) { fn(); }));
}
std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) override
{
- return MakeHandler(m_wallet.NotifyAddressBookChanged.connect(
+ return MakeHandler(m_wallet->NotifyAddressBookChanged.connect(
[fn](CWallet*, const CTxDestination& address, const std::string& label, bool is_mine,
const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); }));
}
std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) override
{
- return MakeHandler(m_wallet.NotifyTransactionChanged.connect(
+ return MakeHandler(m_wallet->NotifyTransactionChanged.connect(
[fn](CWallet*, const uint256& txid, ChangeType status) { fn(txid, status); }));
}
std::unique_ptr<Handler> handleWatchOnlyChanged(WatchOnlyChangedFn fn) override
{
- return MakeHandler(m_wallet.NotifyWatchonlyChanged.connect(fn));
+ return MakeHandler(m_wallet->NotifyWatchonlyChanged.connect(fn));
}
std::unique_ptr<Handler> handleCanGetAddressesChanged(CanGetAddressesChangedFn fn) override
{
- return MakeHandler(m_wallet.NotifyCanGetAddressesChanged.connect(fn));
+ return MakeHandler(m_wallet->NotifyCanGetAddressesChanged.connect(fn));
}
- std::shared_ptr<CWallet> m_shared_wallet;
- CWallet& m_wallet;
+ std::shared_ptr<CWallet> m_wallet;
};
class WalletClientImpl : public ChainClient
@@ -515,7 +508,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); }
@@ -525,6 +518,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..cabc455b1f 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -292,9 +292,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/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..0f13d6e269 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1713,6 +1713,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 +1843,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 +1879,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 +1911,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 +2082,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 +2402,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 +2426,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 +2442,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 +2546,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 +2773,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 +2854,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 +2885,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 +2942,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 +3174,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)
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/transaction.cpp b/src/node/transaction.cpp
index c9cdd0d1cd..7b9b4310e7 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -12,7 +12,7 @@
#include <future>
-const char* TransactionErrorString(const TransactionError err)
+std::string TransactionErrorString(const TransactionError err)
{
switch (err) {
case TransactionError::OK:
@@ -33,22 +33,16 @@ const char* TransactionErrorString(const TransactionError err)
return "PSBTs not compatible (different transactions)";
case TransactionError::SIGHASH_MISMATCH:
return "Specified sighash value does not match existing value";
-
- case TransactionError::UNKNOWN_ERROR:
- default: break;
+ // no default case, so the compiler can warn about missing cases
}
- return "Unknown error";
+ assert(false);
}
-bool BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, TransactionError& error, std::string& err_string, const bool allowhighfees)
+TransactionError BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, std::string& err_string, const CAmount& highfee)
{
std::promise<void> promise;
hashTx = tx->GetHash();
- CAmount nMaxRawTxFee = maxTxFee;
- if (allowhighfees)
- nMaxRawTxFee = 0;
-
{ // cs_main scope
LOCK(cs_main);
CCoinsViewCache &view = *pcoinsTip;
@@ -63,19 +57,16 @@ bool BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, Transaction
CValidationState state;
bool fMissingInputs;
if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) {
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, highfee)) {
if (state.IsInvalid()) {
err_string = FormatStateMessage(state);
- error = TransactionError::MEMPOOL_REJECTED;
- return false;
+ return TransactionError::MEMPOOL_REJECTED;
} else {
if (fMissingInputs) {
- error = TransactionError::MISSING_INPUTS;
- return false;
+ return TransactionError::MISSING_INPUTS;
}
err_string = FormatStateMessage(state);
- error = TransactionError::MEMPOOL_ERROR;
- return false;
+ return TransactionError::MEMPOOL_ERROR;
}
} else {
// If wallet is enabled, ensure that the wallet has been made aware
@@ -88,8 +79,7 @@ bool BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, Transaction
});
}
} else if (fHaveChain) {
- error = TransactionError::ALREADY_IN_CHAIN;
- return false;
+ return TransactionError::ALREADY_IN_CHAIN;
} else {
// Make sure we don't block forever if re-sending
// a transaction already in mempool.
@@ -100,16 +90,14 @@ bool BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, Transaction
promise.get_future().wait();
- if(!g_connman) {
- error = TransactionError::P2P_DISABLED;
- return false;
+ if (!g_connman) {
+ return TransactionError::P2P_DISABLED;
}
CInv inv(MSG_TX, hashTx);
- g_connman->ForEachNode([&inv](CNode* pnode)
- {
+ g_connman->ForEachNode([&inv](CNode* pnode) {
pnode->PushInventory(inv);
});
- return true;
- }
+ return TransactionError::OK;
+}
diff --git a/src/node/transaction.h b/src/node/transaction.h
index 3b0cbba98b..3457ececa4 100644
--- a/src/node/transaction.h
+++ b/src/node/transaction.h
@@ -1,17 +1,16 @@
-// 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.
#ifndef BITCOIN_NODE_TRANSACTION_H
#define BITCOIN_NODE_TRANSACTION_H
+#include <attributes.h>
#include <primitives/transaction.h>
#include <uint256.h>
enum class TransactionError {
- OK = 0,
- UNKNOWN_ERROR,
-
+ OK, //!< No error
MISSING_INPUTS,
ALREADY_IN_CHAIN,
P2P_DISABLED,
@@ -20,24 +19,19 @@ enum class TransactionError {
INVALID_PSBT,
PSBT_MISMATCH,
SIGHASH_MISMATCH,
-
- ERROR_COUNT
};
-#define TRANSACTION_ERR_LAST TransactionError::ERROR_COUNT
-
-const char* TransactionErrorString(const TransactionError error);
+std::string TransactionErrorString(const TransactionError error);
/**
* Broadcast a transaction
*
* @param[in] tx the transaction to broadcast
* @param[out] &txid the txid of the transaction, if successfully broadcast
- * @param[out] &error reference to UniValue to fill with error info on failure
* @param[out] &err_string reference to std::string to fill with error string if available
- * @param[in] allowhighfees whether to allow fees exceeding maxTxFee
- * return true on success, false on error (and fills in `error`)
+ * @param[in] highfee Reject txs with fees higher than this (if 0, accept any fee)
+ * return error
*/
-bool BroadcastTransaction(CTransactionRef tx, uint256& txid, TransactionError& error, std::string& err_string, bool allowhighfees = false);
+NODISCARD TransactionError BroadcastTransaction(CTransactionRef tx, uint256& txid, std::string& err_string, const CAmount& highfee);
#endif // BITCOIN_NODE_TRANSACTION_H
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 0dc130d104..c73a97fd7d 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -14,7 +14,7 @@ bool SignalsOptInRBF(const CTransaction &tx)
return false;
}
-RBFTransactionState IsRBFOptIn(const CTransaction &tx, CTxMemPool &pool)
+RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
{
AssertLockHeld(pool.cs);
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index 581f489e12..a4f8777310 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -23,6 +23,6 @@ bool SignalsOptInRBF(const CTransaction &tx);
// according to BIP 125
// This involves checking sequence numbers of the transaction, as well
// as the sequence numbers of all in-mempool ancestors.
-RBFTransactionState IsRBFOptIn(const CTransaction &tx, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
+RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
#endif // BITCOIN_POLICY_RBF_H
diff --git a/src/psbt.cpp b/src/psbt.cpp
index 32fb459dec..184129e330 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();
}
@@ -309,21 +314,178 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti
return true;
}
-bool CombinePSBTs(PartiallySignedTransaction& out, TransactionError& error, const std::vector<PartiallySignedTransaction>& psbtxs)
+TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs)
{
out = psbtxs[0]; // Copy the first one
// Merge
for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) {
if (!out.Merge(*it)) {
- error = TransactionError::PSBT_MISMATCH;
- return false;
+ return TransactionError::PSBT_MISMATCH;
}
}
if (!out.IsSane()) {
- error = TransactionError::INVALID_PSBT;
+ return TransactionError::INVALID_PSBT;
+ }
+
+ 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";
+ }
+}
+
+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;
+}
+
+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 27b0aedd05..fcb3337a53 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -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.
@@ -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,42 @@ struct PartiallySignedTransaction
}
};
+enum class PSBTRole {
+ UPDATER,
+ SIGNER,
+ FINALIZER,
+ EXTRACTOR
+};
+
+/**
+ * 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
+};
+
+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);
@@ -575,10 +611,22 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti
* Combines PSBTs with the same underlying transaction, resulting in a single PSBT with all partial signatures from each input.
*
* @param[out] &out the combined PSBT, if successful
- * @param[out] &error reference to TransactionError to fill with error info on failure
* @param[in] psbtxs the PSBTs to combine
- * @return True if we successfully combined the transactions, false if they were not compatible
+ * @return error (OK if we successfully combined the transactions, other error if they were not compatible)
+ */
+NODISCARD TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs);
+
+/**
+ * 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.
*/
-bool CombinePSBTs(PartiallySignedTransaction& out, TransactionError& error, const std::vector<PartiallySignedTransaction>& psbtxs);
+PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx);
+
+//! 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/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index bc88dd5e0a..abf9136eee 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -396,6 +396,8 @@ void BitcoinGUI::createActions()
connect(activity, &OpenWalletActivity::opened, this, &BitcoinGUI::setCurrentWallet);
connect(activity, &OpenWalletActivity::finished, activity, &QObject::deleteLater);
connect(activity, &OpenWalletActivity::finished, dialog, &QObject::deleteLater);
+ bool invoked = QMetaObject::invokeMethod(activity, "open");
+ assert(invoked);
});
}
});
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index fc1e14b031..c7ced3c106 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -69,7 +69,6 @@ const QStringList historyFilter = QStringList()
<< "importmulti"
<< "sethdseed"
<< "signmessagewithprivkey"
- << "signrawtransaction"
<< "signrawtransactionwithkey"
<< "walletpassphrase"
<< "walletpassphrasechange"
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index c532ffbbfe..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)
@@ -59,7 +61,6 @@ OpenWalletActivity* WalletController::openWallet(const std::string& name, QWidge
{
OpenWalletActivity* activity = new OpenWalletActivity(this, name);
activity->moveToThread(&m_activity_thread);
- QMetaObject::invokeMethod(activity, "open", Qt::QueuedConnection);
return activity;
}
@@ -98,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/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/rest.cpp b/src/rest.cpp
index 326f7ae1d2..baad3b2ce9 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -300,7 +300,7 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
switch (rf) {
case RetFormat::JSON: {
- UniValue mempoolInfoObject = mempoolInfoToJSON();
+ UniValue mempoolInfoObject = MempoolInfoToJSON(::mempool);
std::string strJSON = mempoolInfoObject.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
@@ -322,7 +322,7 @@ static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPar
switch (rf) {
case RetFormat::JSON: {
- UniValue mempoolObject = mempoolToJSON(true);
+ UniValue mempoolObject = MempoolToJSON(::mempool, true);
std::string strJSON = mempoolObject.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 7fb9ff2eaf..d35f458b2e 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 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.
@@ -378,7 +378,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"
@@ -405,9 +407,9 @@ static std::string EntryDescriptionString()
" \"bip125-replaceable\" : true|false, (boolean) Whether this transaction could be replaced due to BIP125 (replace-by-fee)\n";
}
-static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCKS_REQUIRED(::mempool.cs)
+static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
{
- AssertLockHeld(mempool.cs);
+ AssertLockHeld(pool.cs);
UniValue fees(UniValue::VOBJ);
fees.pushKV("base", ValueFromAmount(e.GetFee()));
@@ -416,7 +418,8 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK
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());
@@ -427,12 +430,12 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK
info.pushKV("ancestorcount", e.GetCountWithAncestors());
info.pushKV("ancestorsize", e.GetSizeWithAncestors());
info.pushKV("ancestorfees", e.GetModFeesWithAncestors());
- info.pushKV("wtxid", mempool.vTxHashes[e.vTxHashesIdx].first.ToString());
+ info.pushKV("wtxid", pool.vTxHashes[e.vTxHashesIdx].first.ToString());
const CTransaction& tx = e.GetTx();
std::set<std::string> setDepends;
for (const CTxIn& txin : tx.vin)
{
- if (mempool.exists(txin.prevout.hash))
+ if (pool.exists(txin.prevout.hash))
setDepends.insert(txin.prevout.hash.ToString());
}
@@ -445,8 +448,8 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK
info.pushKV("depends", depends);
UniValue spent(UniValue::VARR);
- const CTxMemPool::txiter &it = mempool.mapTx.find(tx.GetHash());
- const CTxMemPool::setEntries &setChildren = mempool.GetMemPoolChildren(it);
+ const CTxMemPool::txiter& it = pool.mapTx.find(tx.GetHash());
+ const CTxMemPool::setEntries& setChildren = pool.GetMemPoolChildren(it);
for (CTxMemPool::txiter childiter : setChildren) {
spent.push_back(childiter->GetTx().GetHash().ToString());
}
@@ -455,7 +458,7 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK
// Add opt-in RBF status
bool rbfStatus = false;
- RBFTransactionState rbfState = IsRBFOptIn(tx, mempool);
+ RBFTransactionState rbfState = IsRBFOptIn(tx, pool);
if (rbfState == RBFTransactionState::UNKNOWN) {
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool");
} else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) {
@@ -465,25 +468,21 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK
info.pushKV("bip125-replaceable", rbfStatus);
}
-UniValue mempoolToJSON(bool fVerbose)
+UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose)
{
- if (fVerbose)
- {
- LOCK(mempool.cs);
+ if (verbose) {
+ LOCK(pool.cs);
UniValue o(UniValue::VOBJ);
- for (const CTxMemPoolEntry& e : mempool.mapTx)
- {
+ for (const CTxMemPoolEntry& e : pool.mapTx) {
const uint256& hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
- entryToJSON(info, e);
+ entryToJSON(pool, info, e);
o.pushKV(hash.ToString(), info);
}
return o;
- }
- else
- {
+ } else {
std::vector<uint256> vtxid;
- mempool.queryHashes(vtxid);
+ pool.queryHashes(vtxid);
UniValue a(UniValue::VARR);
for (const uint256& hash : vtxid)
@@ -525,7 +524,7 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
if (!request.params[0].isNull())
fVerbose = request.params[0].get_bool();
- return mempoolToJSON(fVerbose);
+ return MempoolToJSON(::mempool, fVerbose);
}
static UniValue getmempoolancestors(const JSONRPCRequest& request)
@@ -591,7 +590,7 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
const CTxMemPoolEntry &e = *ancestorIt;
const uint256& _hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
- entryToJSON(info, e);
+ entryToJSON(::mempool, info, e);
o.pushKV(_hash.ToString(), info);
}
return o;
@@ -661,7 +660,7 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
const CTxMemPoolEntry &e = *descendantIt;
const uint256& _hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ);
- entryToJSON(info, e);
+ entryToJSON(::mempool, info, e);
o.pushKV(_hash.ToString(), info);
}
return o;
@@ -700,7 +699,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
const CTxMemPoolEntry &e = *it;
UniValue info(UniValue::VOBJ);
- entryToJSON(info, e);
+ entryToJSON(::mempool, info, e);
return info;
}
@@ -1485,15 +1484,15 @@ static UniValue getchaintips(const JSONRPCRequest& request)
return res;
}
-UniValue mempoolInfoToJSON()
+UniValue MempoolInfoToJSON(const CTxMemPool& pool)
{
UniValue ret(UniValue::VOBJ);
- ret.pushKV("size", (int64_t) mempool.size());
- ret.pushKV("bytes", (int64_t) mempool.GetTotalTxSize());
- ret.pushKV("usage", (int64_t) mempool.DynamicMemoryUsage());
+ ret.pushKV("size", (int64_t)pool.size());
+ ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
+ ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
ret.pushKV("maxmempool", (int64_t) maxmempool);
- ret.pushKV("mempoolminfee", ValueFromAmount(std::max(mempool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
+ ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
return ret;
@@ -1522,7 +1521,7 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
},
}.ToString());
- return mempoolInfoToJSON();
+ return MempoolInfoToJSON(::mempool);
}
static UniValue preciousblock(const JSONRPCRequest& request)
@@ -1583,15 +1582,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());
@@ -1778,9 +1777,7 @@ static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t)
static UniValue getblockstats(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) {
- throw std::runtime_error(
- RPCHelpMan{"getblockstats",
+ const RPCHelpMan help{"getblockstats",
"\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
"It won't work for some heights with pruning.\n"
"It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n",
@@ -1836,7 +1833,9 @@ static UniValue getblockstats(const JSONRPCRequest& request)
HelpExampleCli("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'")
+ HelpExampleRpc("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'")
},
- }.ToString());
+ };
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
}
LOCK(cs_main);
@@ -2159,7 +2158,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
{
{"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
- {"range", RPCArg::Type::NUM, /* default */ "1000", "Up to what child index HD chains should be explored"},
+ {"range", RPCArg::Type::RANGE, /* default */ "1000", "The range of HD chain indexes to explore (either end or [begin,end])"},
},
},
},
@@ -2216,7 +2215,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
// loop through the scan objects
for (const UniValue& scanobject : request.params[1].get_array().getValues()) {
std::string desc_str;
- int range = 1000;
+ std::pair<int64_t, int64_t> range = {0, 1000};
if (scanobject.isStr()) {
desc_str = scanobject.get_str();
} else if (scanobject.isObject()) {
@@ -2225,8 +2224,8 @@ UniValue scantxoutset(const JSONRPCRequest& request)
desc_str = desc_uni.get_str();
UniValue range_uni = find_value(scanobject, "range");
if (!range_uni.isNull()) {
- range = range_uni.get_int();
- if (range < 0 || range > 1000000) throw JSONRPCError(RPC_INVALID_PARAMETER, "range out of range");
+ range = ParseRange(range_uni);
+ if (range.first < 0 || (range.second >> 31) != 0 || range.second >= range.first + 1000000) throw JSONRPCError(RPC_INVALID_PARAMETER, "range out of range");
}
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
@@ -2237,8 +2236,11 @@ UniValue scantxoutset(const JSONRPCRequest& request)
if (!desc) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid descriptor '%s'", desc_str));
}
- if (!desc->IsRange()) range = 0;
- for (int i = 0; i <= range; ++i) {
+ if (!desc->IsRange()) {
+ range.first = 0;
+ range.second = 0;
+ }
+ for (int i = range.first; i <= range.second; ++i) {
std::vector<CScript> scripts;
if (!desc->Expand(i, provider, scripts, provider)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 529132d033..55d1de453f 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -11,6 +11,7 @@
class CBlock;
class CBlockIndex;
+class CTxMemPool;
class UniValue;
static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
@@ -30,10 +31,10 @@ void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false);
/** Mempool information to JSON */
-UniValue mempoolInfoToJSON();
+UniValue MempoolInfoToJSON(const CTxMemPool& pool);
/** Mempool to JSON */
-UniValue mempoolToJSON(bool fVerbose = false);
+UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose = false);
/** Block header to JSON */
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex);
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 1cdc9f87a7..4144a17bc3 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -28,8 +28,6 @@ public:
static const CRPCConvertParam vRPCConvertParams[] =
{
{ "setmocktime", 0, "timestamp" },
- { "generate", 0, "nblocks" },
- { "generate", 1, "maxtries" },
{ "generatetoaddress", 0, "nblocks" },
{ "generatetoaddress", 2, "maxtries" },
{ "getnetworkhashps", 0, "nblocks" },
@@ -68,8 +66,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 4, "subtractfeefrom" },
{ "sendmany", 5 , "replaceable" },
{ "sendmany", 6 , "conf_target" },
- { "deriveaddresses", 1, "begin" },
- { "deriveaddresses", 2, "end" },
+ { "deriveaddresses", 1, "range" },
{ "scantxoutset", 1, "scanobjects" },
{ "addmultisigaddress", 0, "nrequired" },
{ "addmultisigaddress", 1, "keys" },
@@ -95,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 6625a03bbd..480d610235 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -444,10 +444,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;
@@ -843,7 +843,8 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
- unsigned int conf_target = ParseConfirmTarget(request.params[0]);
+ unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
bool conservative = true;
if (!request.params[1].isNull()) {
FeeEstimateMode fee_mode;
@@ -915,7 +916,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
- unsigned int conf_target = ParseConfirmTarget(request.params[0]);
+ unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
double threshold = 0.95;
if (!request.params[1].isNull()) {
threshold = request.params[1].get_real();
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 999a307e2b..0a97f80297 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -34,11 +34,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"},
},
@@ -185,7 +181,7 @@ UniValue getdescriptorinfo(const JSONRPCRequest& request)
UniValue deriveaddresses(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.empty() || request.params.size() > 3) {
+ if (request.fHelp || request.params.empty() || request.params.size() > 2) {
throw std::runtime_error(
RPCHelpMan{"deriveaddresses",
{"\nDerives one or more addresses corresponding to an output descriptor.\n"
@@ -199,37 +195,37 @@ UniValue deriveaddresses(const JSONRPCRequest& request)
"For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"},
{
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
- {"begin", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the beginning of the range to import."},
- {"end", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end of the range to import."}
+ {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."},
},
RPCResult{
"[ address ] (array) the derived addresses\n"
},
RPCExamples{
"First three native segwit receive addresses\n" +
- HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#trd0mf0l\" 0 2")
+ HelpExampleCli("deriveaddresses", "\"wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#trd0mf0l\" \"[0,2]\"")
}}.ToString()
);
}
- RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VNUM, UniValue::VNUM});
+ RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); // Range argument is checked later
const std::string desc_str = request.params[0].get_str();
- int range_begin = 0;
- int range_end = 0;
+ int64_t range_begin = 0;
+ int64_t range_end = 0;
- if (request.params.size() >= 2) {
- if (request.params.size() == 2) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing range end parameter");
- }
- range_begin = request.params[1].get_int();
- range_end = request.params[2].get_int();
- if (range_begin < 0) {
+ if (request.params.size() >= 2 && !request.params[1].isNull()) {
+ auto range = ParseRange(request.params[1]);
+ if (range.first < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
}
- if (range_begin > range_end) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Range end should be equal to or greater than begin");
+ if ((range.second >> 31) != 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
+ }
+ if (range.second >= range.first + 1000000) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
}
+ range_begin = range.first;
+ range_end = range.second;
}
FlatSigningProvider provider;
@@ -603,7 +599,7 @@ static const CRPCCommand commands[] =
{ "control", "logging", &logging, {"include", "exclude"}},
{ "util", "validateaddress", &validateaddress, {"address"} },
{ "util", "createmultisig", &createmultisig, {"nrequired","keys","address_type"} },
- { "util", "deriveaddresses", &deriveaddresses, {"descriptor", "begin", "end"} },
+ { "util", "deriveaddresses", &deriveaddresses, {"descriptor", "range"} },
{ "util", "getdescriptorinfo", &getdescriptorinfo, {"descriptor"} },
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} },
{ "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} },
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 6bbbbc9876..c7b3478f44 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -523,13 +523,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
static UniValue setban(const JSONRPCRequest& request)
{
- std::string strCommand;
- if (!request.params[1].isNull())
- strCommand = request.params[1].get_str();
- if (request.fHelp || request.params.size() < 2 ||
- (strCommand != "add" && strCommand != "remove"))
- throw std::runtime_error(
- RPCHelpMan{"setban",
+ const RPCHelpMan help{"setban",
"\nAttempts to add or remove an IP/Subnet from the banned list.\n",
{
{"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"},
@@ -543,7 +537,13 @@ static UniValue setban(const JSONRPCRequest& request)
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")
},
- }.ToString());
+ };
+ std::string strCommand;
+ if (!request.params[1].isNull())
+ strCommand = request.params[1].get_str();
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size()) || (strCommand != "add" && strCommand != "remove")) {
+ throw std::runtime_error(help.ToString());
+ }
if (!g_banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 38e2dc237e..8d9c207011 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -6,11 +6,12 @@
#include <chain.h>
#include <coins.h>
#include <compat/byteswap.h>
-#include <consensus/validation.h>
#include <consensus/tx_verify.h>
+#include <consensus/validation.h>
#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>
@@ -28,6 +29,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 +40,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)
{
@@ -67,9 +74,7 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
static UniValue getrawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
- throw std::runtime_error(
- RPCHelpMan{
+ const RPCHelpMan help{
"getrawtransaction",
"\nReturn the raw transaction data.\n"
@@ -79,8 +84,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
"will return the transaction if it is in the mempool, or if -txindex is enabled and the transaction\n"
"is in a block in the blockchain.\n"
- "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n"
- "Or use gettransaction for wallet transactions.\n"
+ "\nHint: Use gettransaction for wallet transactions.\n"
"\nIf verbose is 'true', returns an Object with information about 'txid'.\n"
"If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.\n",
@@ -148,7 +152,11 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"")
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"")
},
- }.ToString());
+ };
+
+ if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+ throw std::runtime_error(help.ToString());
+ }
bool in_active_chain = true;
uint256 hash = ParseHashV(request.params[0], "parameter 1");
@@ -607,33 +615,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});
@@ -645,7 +674,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");
@@ -679,7 +708,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);
}
@@ -789,23 +818,20 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
return EncodeHexTx(CTransaction(mergedTx));
}
+// TODO(https://github.com/bitcoin/bitcoin/pull/10973#discussion_r267084237):
+// This function is called from both wallet and node rpcs
+// (signrawtransactionwithwallet and signrawtransactionwithkey). It should be
+// moved to a util file so wallet code doesn't need to link against node code.
+// Also the dependency on interfaces::Chain should be removed, so
+// signrawtransactionwithkey doesn't need access to a Chain instance.
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
+ std::map<COutPoint, Coin> coins;
+ for (const CTxIn& txin : mtx.vin) {
+ coins[txin.prevout]; // Create empty map entry keyed by prevout.
}
+ chain.findCoins(coins);
// Add previous txouts given in the RPC call:
if (!prevTxsUnival.isNull()) {
@@ -837,10 +863,10 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con
CScript scriptPubKey(pkData.begin(), pkData.end());
{
- const Coin& coin = view.AccessCoin(out);
- if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) {
+ 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.out.scriptPubKey) + "\nvs:\n"+
+ err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+
ScriptToAsmStr(scriptPubKey);
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
}
@@ -851,7 +877,7 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con
newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
}
newcoin.nHeight = 1;
- view.AddCoin(out, std::move(newcoin), true);
+ coins[out] = std::move(newcoin);
}
// if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
@@ -895,15 +921,15 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con
// 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()) {
+ 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.out.scriptPubKey;
- const CAmount& amount = coin.out.nValue;
+ const CScript& prevPubKey = coin->second.out.scriptPubKey;
+ const CAmount& amount = coin->second.out.nValue;
- SignatureData sigdata = DataFromTransaction(mtx, i, coin.out);
+ 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);
@@ -913,7 +939,7 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con
// 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()));
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString()));
}
ScriptError serror = SCRIPT_ERR_OK;
@@ -964,7 +990,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"},
},
},
},
@@ -1021,24 +1047,14 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
return SignTransaction(*g_rpc_interfaces->chain, mtx, request.params[2], &keystore, true, request.params[3]);
}
-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");
-}
-
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\n"},
},
RPCResult{
"\"hex\" (string) The transaction hash in hex\n"
@@ -1053,9 +1069,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;
@@ -1063,12 +1086,25 @@ 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();
+ 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].isNum()) {
+ 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);
+ } else if (!request.params[1].isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric");
+ }
+
uint256 txid;
- TransactionError err;
std::string err_string;
- if (!BroadcastTransaction(tx, txid, err, err_string, allowhighfees)) {
+ const TransactionError err = BroadcastTransaction(tx, txid, err_string, max_raw_tx_fee);
+ if (TransactionError::OK != err) {
throw JSONRPCTransactionError(err, err_string);
}
@@ -1077,9 +1113,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",
@@ -1090,7 +1124,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"
@@ -1112,10 +1146,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");
}
@@ -1127,9 +1168,19 @@ 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].isNum()) {
+ 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);
+ } else if (!request.params[1].isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric");
}
UniValue result(UniValue::VARR);
@@ -1493,12 +1544,11 @@ UniValue combinepsbt(const JSONRPCRequest& request)
}
PartiallySignedTransaction merged_psbt;
- TransactionError error;
- if (!CombinePSBTs(merged_psbt, error, psbtxs)) {
+ const TransactionError error = CombinePSBTs(merged_psbt, psbtxs);
+ if (error != TransactionError::OK) {
throw JSONRPCTransactionError(error);
}
- UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << merged_psbt;
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
@@ -1860,21 +1910,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"
@@ -1893,148 +1943,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;
}
@@ -2046,11 +2004,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/server.cpp b/src/rpc/server.cpp
index cd90573da0..e803fabcc6 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
{
@@ -173,11 +174,11 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest&
{
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 +194,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)
{
@@ -337,32 +338,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 +544,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)
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 2d62a76f3c..e2a85887ba 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -131,10 +131,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 +164,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 +189,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,6 +197,7 @@ 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);
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 023b4b6746..10979b43b0 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -1,15 +1,12 @@
-// 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 <key_io.h>
#include <keystore.h>
-#include <policy/fees.h>
-#include <rpc/protocol.h>
#include <rpc/util.h>
#include <tinyformat.h>
#include <util/strencodings.h>
-#include <validation.h>
InitInterfaces* g_rpc_interfaces = nullptr;
@@ -131,10 +128,9 @@ UniValue DescribeAddress(const CTxDestination& dest)
return boost::apply_visitor(DescribeAddressVisitor(), dest);
}
-unsigned int ParseConfirmTarget(const UniValue& value)
+unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
{
int target = value.get_int();
- unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
if (target < 1 || (unsigned int)target > max_target) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u - %u", 1, max_target));
}
@@ -201,6 +197,7 @@ struct Sections {
case RPCArg::Type::STR:
case RPCArg::Type::NUM:
case RPCArg::Type::AMOUNT:
+ case RPCArg::Type::RANGE:
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;
@@ -315,6 +312,17 @@ std::string RPCExamples::ToDescriptionString() const
return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples;
}
+bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
+{
+ size_t num_required_args = 0;
+ for (size_t n = m_args.size(); n > 0; --n) {
+ if (!m_args.at(n - 1).IsOptional()) {
+ num_required_args = n;
+ break;
+ }
+ }
+ return num_required_args <= num_args && num_args <= m_args.size();
+}
std::string RPCHelpMan::ToString() const
{
std::string ret;
@@ -323,12 +331,7 @@ std::string RPCHelpMan::ToString() const
ret += m_name;
bool was_optional{false};
for (const auto& arg : m_args) {
- bool optional;
- if (arg.m_fallback.which() == 1) {
- optional = true;
- } else {
- optional = RPCArg::Optional::NO != boost::get<RPCArg::Optional>(arg.m_fallback);
- }
+ const bool optional = arg.IsOptional();
ret += " ";
if (optional) {
if (!was_optional) ret += "( ";
@@ -370,6 +373,15 @@ std::string RPCHelpMan::ToString() const
return ret;
}
+bool RPCArg::IsOptional() const
+{
+ if (m_fallback.which() == 1) {
+ return true;
+ } else {
+ return RPCArg::Optional::NO != boost::get<RPCArg::Optional>(m_fallback);
+ }
+}
+
std::string RPCArg::ToDescriptionString() const
{
std::string ret;
@@ -391,6 +403,10 @@ std::string RPCArg::ToDescriptionString() const
ret += "numeric or string";
break;
}
+ case Type::RANGE: {
+ ret += "numeric or array";
+ break;
+ }
case Type::BOOL: {
ret += "boolean";
break;
@@ -450,6 +466,8 @@ std::string RPCArg::ToStringObj(const bool oneline) const
return res + "\"hex\"";
case Type::NUM:
return res + "n";
+ case Type::RANGE:
+ return res + "n or [n,n]";
case Type::AMOUNT:
return res + "amount";
case Type::BOOL:
@@ -480,6 +498,7 @@ std::string RPCArg::ToString(const bool oneline) const
return "\"" + m_name + "\"";
}
case Type::NUM:
+ case Type::RANGE:
case Type::AMOUNT:
case Type::BOOL: {
return m_name;
@@ -509,3 +528,17 @@ std::string RPCArg::ToString(const bool oneline) const
}
assert(false);
}
+
+std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
+{
+ if (value.isNum()) {
+ return {0, value.get_int64()};
+ }
+ if (value.isArray() && value.size() == 2 && value[0].isNum() && value[1].isNum()) {
+ int64_t low = value[0].get_int64();
+ int64_t high = value[1].get_int64();
+ if (low > high) throw JSONRPCError(RPC_INVALID_PARAMETER, "Range specified as [begin,end] must not have begin after end");
+ return {low, high};
+ }
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]");
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 1c9ddcdf44..e4cc1fde44 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -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.
@@ -7,6 +7,7 @@
#include <node/transaction.h>
#include <pubkey.h>
+#include <rpc/protocol.h>
#include <script/standard.h>
#include <univalue.h>
@@ -32,11 +33,14 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey
UniValue DescribeAddress(const CTxDestination& dest);
//! Parse a confirm target option and raise an RPC error if it is invalid.
-unsigned int ParseConfirmTarget(const UniValue& value);
+unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target);
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr);
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = "");
+//! Parse a JSON range specified as int64, or [int64, int64]
+std::pair<int64_t, int64_t> ParseRange(const UniValue& value);
+
struct RPCArg {
enum class Type {
OBJ,
@@ -47,13 +51,14 @@ struct RPCArg {
OBJ_USER_KEYS, //!< Special type where the user must set the keys e.g. to define multiple addresses; as opposed to e.g. an options object where the keys are predefined
AMOUNT, //!< Special type representing a floating point amount (can be either NUM or STR)
STR_HEX, //!< Special type that is a STR with only hex chars
+ RANGE, //!< Special type that is a NUM or [NUM,NUM]
};
enum class Optional {
/** Required arg */
NO,
/**
- * Optinal arg that is a named argument and has a default value of
+ * Optional arg that is a named argument and has a default value of
* `null`. When possible, the default value should be specified.
*/
OMITTED_NAMED_ARG,
@@ -110,6 +115,8 @@ struct RPCArg {
assert(type == Type::ARR || type == Type::OBJ);
}
+ bool IsOptional() const;
+
/**
* Return the type string of the argument.
* Set oneline to allow it to be overridden by a custom oneline type string (m_oneline_description).
@@ -185,6 +192,8 @@ public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples);
std::string ToString() const;
+ /** If the supplied number of args is neither too small nor too high */
+ bool IsValidNumArgs(size_t num_args) const;
private:
const std::string m_name;
diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp
index 625362f446..cd0c36d802 100644
--- a/src/test/blockfilter_tests.cpp
+++ b/src/test/blockfilter_tests.cpp
@@ -112,6 +112,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)
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 86cb00a78f..2cc38a0679 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -4,6 +4,7 @@
#include <crypto/aes.h>
#include <crypto/chacha20.h>
+#include <crypto/poly1305.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
@@ -66,26 +67,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 +86,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 +140,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 +379,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 +389,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 +449,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/flatfile_tests.cpp b/src/test/flatfile_tests.cpp
new file mode 100644
index 0000000000..079a09f8f9
--- /dev/null
+++ b/src/test/flatfile_tests.cpp
@@ -0,0 +1,123 @@
+// 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 <flatfile.h>
+#include <test/test_bitcoin.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(flatfile_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(flatfile_filename)
+{
+ auto data_dir = SetDataDir("flatfile_test");
+
+ FlatFilePos pos(456, 789);
+
+ FlatFileSeq seq1(data_dir, "a", 16 * 1024);
+ BOOST_CHECK_EQUAL(seq1.FileName(pos), data_dir / "a00456.dat");
+
+ FlatFileSeq seq2(data_dir / "a", "b", 16 * 1024);
+ BOOST_CHECK_EQUAL(seq2.FileName(pos), data_dir / "a" / "b00456.dat");
+}
+
+BOOST_AUTO_TEST_CASE(flatfile_open)
+{
+ auto data_dir = SetDataDir("flatfile_test");
+ FlatFileSeq seq(data_dir, "a", 16 * 1024);
+
+ std::string line1("A purely peer-to-peer version of electronic cash would allow online "
+ "payments to be sent directly from one party to another without going "
+ "through a financial institution.");
+ std::string line2("Digital signatures provide part of the solution, but the main benefits are "
+ "lost if a trusted third party is still required to prevent double-spending.");
+
+ size_t pos1 = 0;
+ size_t pos2 = pos1 + GetSerializeSize(line1, CLIENT_VERSION);
+
+ // Write first line to file.
+ {
+ CAutoFile file(seq.Open(FlatFilePos(0, pos1)), SER_DISK, CLIENT_VERSION);
+ file << LIMITED_STRING(line1, 256);
+ }
+
+ // Attempt to append to file opened in read-only mode.
+ {
+ CAutoFile file(seq.Open(FlatFilePos(0, pos2), true), SER_DISK, CLIENT_VERSION);
+ BOOST_CHECK_THROW(file << LIMITED_STRING(line2, 256), std::ios_base::failure);
+ }
+
+ // Append second line to file.
+ {
+ CAutoFile file(seq.Open(FlatFilePos(0, pos2)), SER_DISK, CLIENT_VERSION);
+ file << LIMITED_STRING(line2, 256);
+ }
+
+ // Read text from file in read-only mode.
+ {
+ std::string text;
+ CAutoFile file(seq.Open(FlatFilePos(0, pos1), true), SER_DISK, CLIENT_VERSION);
+
+ file >> LIMITED_STRING(text, 256);
+ BOOST_CHECK_EQUAL(text, line1);
+
+ file >> LIMITED_STRING(text, 256);
+ BOOST_CHECK_EQUAL(text, line2);
+ }
+
+ // Read text from file with position offset.
+ {
+ std::string text;
+ CAutoFile file(seq.Open(FlatFilePos(0, pos2)), SER_DISK, CLIENT_VERSION);
+
+ file >> LIMITED_STRING(text, 256);
+ BOOST_CHECK_EQUAL(text, line2);
+ }
+
+ // Ensure another file in the sequence has no data.
+ {
+ std::string text;
+ CAutoFile file(seq.Open(FlatFilePos(1, pos2)), SER_DISK, CLIENT_VERSION);
+ BOOST_CHECK_THROW(file >> LIMITED_STRING(text, 256), std::ios_base::failure);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(flatfile_allocate)
+{
+ auto data_dir = SetDataDir("flatfile_test");
+ FlatFileSeq seq(data_dir, "a", 100);
+
+ bool out_of_space;
+
+ BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 0), 1, out_of_space), 100);
+ BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 0))), 100);
+ BOOST_CHECK(!out_of_space);
+
+ BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 99), 1, out_of_space), 0);
+ BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 99))), 100);
+ BOOST_CHECK(!out_of_space);
+
+ BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 99), 2, out_of_space), 101);
+ BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 99))), 200);
+ BOOST_CHECK(!out_of_space);
+}
+
+BOOST_AUTO_TEST_CASE(flatfile_flush)
+{
+ auto data_dir = SetDataDir("flatfile_test");
+ FlatFileSeq seq(data_dir, "a", 100);
+
+ bool out_of_space;
+ seq.Allocate(FlatFilePos(0, 0), 1, out_of_space);
+
+ // Flush without finalize should not truncate file.
+ seq.Flush(FlatFilePos(0, 1));
+ BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 1))), 100);
+
+ // Flush with finalize should truncate file.
+ seq.Flush(FlatFilePos(0, 1), true);
+ BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 1))), 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h
index ad62a5faf0..8b03a7e46e 100644
--- a/src/test/fuzz/fuzz.h
+++ b/src/test/fuzz/fuzz.h
@@ -10,8 +10,6 @@
#include <vector>
-const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
-
void test_one_input(std::vector<uint8_t> buffer);
#endif // BITCOIN_TEST_FUZZ_FUZZ_H
diff --git a/src/test/main.cpp b/src/test/main.cpp
new file mode 100644
index 0000000000..ff3f36b561
--- /dev/null
+++ b/src/test/main.cpp
@@ -0,0 +1,7 @@
+// 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.
+
+#define BOOST_TEST_MODULE Bitcoin Core Test Suite
+
+#include <boost/test/unit_test.hpp>
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index ff48398925..9bb2bf551b 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -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/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 0c3fb7c398..cdfd4db589 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -66,36 +66,36 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
{
SetDataDir("tempdir");
const CChainParams& chainparams = Params();
- // Ideally we'd move all the RPC tests to the functional testing framework
- // instead of unit tests, but for now we need these here.
-
- RegisterAllCoreRPCCommands(tableRPC);
- ClearDatadirCache();
-
- // We have to run a scheduler thread to prevent ActivateBestChain
- // from blocking due to queue overrun.
- threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
- GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
-
- mempool.setSanityCheck(1.0);
- pblocktree.reset(new CBlockTreeDB(1 << 20, true));
- pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
- pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
- if (!LoadGenesisBlock(chainparams)) {
- throw std::runtime_error("LoadGenesisBlock failed.");
- }
- {
- CValidationState state;
- if (!ActivateBestChain(state, chainparams)) {
- throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state)));
- }
- }
- nScriptCheckThreads = 3;
- for (int i=0; i < nScriptCheckThreads-1; i++)
- threadGroup.create_thread(&ThreadScriptCheck);
-
- g_banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
- g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
+ // Ideally we'd move all the RPC tests to the functional testing framework
+ // instead of unit tests, but for now we need these here.
+
+ RegisterAllCoreRPCCommands(tableRPC);
+ ClearDatadirCache();
+
+ // We have to run a scheduler thread to prevent ActivateBestChain
+ // from blocking due to queue overrun.
+ threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
+ GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+
+ mempool.setSanityCheck(1.0);
+ pblocktree.reset(new CBlockTreeDB(1 << 20, true));
+ pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
+ pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
+ if (!LoadGenesisBlock(chainparams)) {
+ throw std::runtime_error("LoadGenesisBlock failed.");
+ }
+
+ CValidationState state;
+ if (!ActivateBestChain(state, chainparams)) {
+ throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state)));
+ }
+
+ nScriptCheckThreads = 3;
+ for (int i = 0; i < nScriptCheckThreads - 1; i++)
+ threadGroup.create_thread(&ThreadScriptCheck);
+
+ g_banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
+ g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
}
TestingSetup::~TestingSetup()
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 4a06845683..38c6d85a8d 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -71,10 +71,6 @@ private:
/** Testing setup that configures a complete environment.
* Included are data directory, coins database, script check threads setup.
*/
-class CConnman;
-class CNode;
-
-class PeerLogicValidation;
struct TestingSetup : public BasicTestingSetup {
boost::thread_group threadGroup;
CScheduler scheduler;
diff --git a/src/test/test_bitcoin_main.cpp b/src/test/test_bitcoin_main.cpp
deleted file mode 100644
index 46b63b93b4..0000000000
--- a/src/test/test_bitcoin_main.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2011-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.
-
-#define BOOST_TEST_MODULE Bitcoin Test Suite
-
-#include <banman.h>
-#include <net.h>
-
-#include <memory>
-
-#include <boost/test/unit_test.hpp>
-
-std::unique_ptr<CConnman> g_connman;
-std::unique_ptr<BanMan> g_banman;
-
-[[noreturn]] void Shutdown(void* parg)
-{
- std::exit(EXIT_SUCCESS);
-}
-
-[[noreturn]] void StartShutdown()
-{
- std::exit(EXIT_SUCCESS);
-}
-
-bool ShutdownRequested()
-{
- return false;
-}
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 860f64bb11..e106bdeb65 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -78,80 +78,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"
@@ -180,9 +140,10 @@ struct TestArgsManager : public ArgsManager
{
LOCK(cs_args);
m_config_args.clear();
+ m_config_sections.clear();
}
std::string error;
- BOOST_REQUIRE(ReadConfigStream(streamConfig, error));
+ BOOST_REQUIRE(ReadConfigStream(streamConfig, "", error));
}
void SetNetworkOnlyArg(const std::string arg)
{
diff --git a/src/test/main_tests.cpp b/src/test/validation_tests.cpp
index 5b3f2bc578..8d06ecd3a9 100644
--- a/src/test/main_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -1,17 +1,17 @@
-// 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 <chainparams.h>
-#include <validation.h>
#include <net.h>
+#include <validation.h>
#include <test/test_bitcoin.h>
#include <boost/signals2/signal.hpp>
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(main_tests, TestingSetup)
+BOOST_FIXTURE_TEST_SUITE(validation_tests, TestingSetup)
static void TestBlockSubsidyHalvings(const Consensus::Params& consensusParams)
{
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 68f47d5cce..ca556bdc7b 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -764,7 +764,7 @@ std::vector<CTxMemPool::indexed_transaction_set::const_iterator> CTxMemPool::Get
return iters;
}
-void CTxMemPool::queryHashes(std::vector<uint256>& vtxid)
+void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) const
{
LOCK(cs);
auto iters = GetSortedDepthAndScore();
diff --git a/src/txmempool.h b/src/txmempool.h
index f7afaec8fc..a8a0f7fa45 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -184,7 +184,7 @@ private:
const LockPoints& lp;
};
-// extracts a transaction hash from CTxMempoolEntry or CTransactionRef
+// extracts a transaction hash from CTxMemPoolEntry or CTransactionRef
struct mempoolentry_txid
{
typedef uint256 result_type;
@@ -588,7 +588,7 @@ public:
void clear();
void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); //lock free
bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb);
- void queryHashes(std::vector<uint256>& vtxid);
+ void queryHashes(std::vector<uint256>& vtxid) const;
bool isSpent(const COutPoint& outpoint) const;
unsigned int GetTransactionsUpdated() const;
void AddTransactionsUpdated(unsigned int n);
diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp
index 947d7e2308..16ab24686b 100644
--- a/src/ui_interface.cpp
+++ b/src/ui_interface.cpp
@@ -52,7 +52,7 @@ void CClientUIInterface::InitMessage(const std::string& message) { return g_ui_s
void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { return g_ui_signals.NotifyNumConnectionsChanged(newNumConnections); }
void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); }
void CClientUIInterface::NotifyAlertChanged() { return g_ui_signals.NotifyAlertChanged(); }
-void CClientUIInterface::LoadWallet(std::shared_ptr<CWallet> wallet) { return g_ui_signals.LoadWallet(wallet); }
+void CClientUIInterface::LoadWallet(std::unique_ptr<interfaces::Wallet>& wallet) { return g_ui_signals.LoadWallet(wallet); }
void CClientUIInterface::ShowProgress(const std::string& title, int nProgress, bool resume_possible) { return g_ui_signals.ShowProgress(title, nProgress, resume_possible); }
void CClientUIInterface::NotifyBlockTip(bool b, const CBlockIndex* i) { return g_ui_signals.NotifyBlockTip(b, i); }
void CClientUIInterface::NotifyHeaderTip(bool b, const CBlockIndex* i) { return g_ui_signals.NotifyHeaderTip(b, i); }
diff --git a/src/ui_interface.h b/src/ui_interface.h
index fe466b3ca4..f1aebce3bb 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -11,7 +11,6 @@
#include <stdint.h>
#include <string>
-class CWallet;
class CBlockIndex;
namespace boost {
namespace signals2 {
@@ -19,6 +18,10 @@ class connection;
}
} // namespace boost
+namespace interfaces {
+class Wallet;
+} // namespace interfaces
+
/** General change type (added, updated, removed). */
enum ChangeType
{
@@ -102,7 +105,7 @@ public:
ADD_SIGNALS_DECL_WRAPPER(NotifyAlertChanged, void, );
/** A wallet has been loaded. */
- ADD_SIGNALS_DECL_WRAPPER(LoadWallet, void, std::shared_ptr<CWallet> wallet);
+ ADD_SIGNALS_DECL_WRAPPER(LoadWallet, void, std::unique_ptr<interfaces::Wallet>& wallet);
/**
* Show progress e.g. for verifychain.
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 27ed24d012..9594dd81bf 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -74,7 +74,6 @@
const int64_t nStartupTime = GetTime();
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
-const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
ArgsManager gArgs;
@@ -136,6 +135,14 @@ bool DirIsWritable(const fs::path& directory)
return true;
}
+bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
+{
+ constexpr uint64_t min_disk_space = 52428800; // 50 MiB
+
+ uint64_t free_bytes_available = fs::space(dir).available;
+ return free_bytes_available >= min_disk_space + additional_bytes;
+}
+
/**
* Interpret a string argument as a boolean.
*
@@ -354,8 +361,7 @@ const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
return unsuitables;
}
-
-const std::set<std::string> ArgsManager::GetUnrecognizedSections() const
+const std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
{
// Section names to be recognized in the config file.
static const std::set<std::string> available_sections{
@@ -363,14 +369,11 @@ const std::set<std::string> ArgsManager::GetUnrecognizedSections() const
CBaseChainParams::TESTNET,
CBaseChainParams::MAIN
};
- std::set<std::string> diff;
LOCK(cs_args);
- std::set_difference(
- m_config_sections.begin(), m_config_sections.end(),
- available_sections.begin(), available_sections.end(),
- std::inserter(diff, diff.end()));
- return diff;
+ std::list<SectionInfo> unrecognized = m_config_sections;
+ unrecognized.remove_if([](const SectionInfo& appeared){ return available_sections.find(appeared.m_name) != available_sections.end(); });
+ return unrecognized;
}
void ArgsManager::SelectConfigNetwork(const std::string& network)
@@ -794,7 +797,7 @@ static std::string TrimString(const std::string& str, const std::string& pattern
return str.substr(front, end - front + 1);
}
-static bool GetConfigOptions(std::istream& stream, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::set<std::string>& sections)
+static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::list<SectionInfo>& sections)
{
std::string str, prefix;
std::string::size_type pos;
@@ -810,7 +813,7 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
if (!str.empty()) {
if (*str.begin() == '[' && *str.rbegin() == ']') {
const std::string section = str.substr(1, str.size() - 2);
- sections.insert(section);
+ sections.emplace_back(SectionInfo{section, filepath, linenr});
prefix = section + '.';
} else if (*str.begin() == '-') {
error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
@@ -823,8 +826,8 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
return false;
}
options.emplace_back(name, value);
- if ((pos = name.rfind('.')) != std::string::npos) {
- sections.insert(name.substr(0, pos));
+ if ((pos = name.rfind('.')) != std::string::npos && prefix.length() <= pos) {
+ sections.emplace_back(SectionInfo{name.substr(0, pos), filepath, linenr});
}
} else {
error = strprintf("parse error on line %i: %s", linenr, str);
@@ -839,12 +842,11 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
return true;
}
-bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys)
+bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
{
LOCK(cs_args);
std::vector<std::pair<std::string, std::string>> options;
- m_config_sections.clear();
- if (!GetConfigOptions(stream, error, options, m_config_sections)) {
+ if (!GetConfigOptions(stream, filepath, error, options, m_config_sections)) {
return false;
}
for (const std::pair<std::string, std::string>& option : options) {
@@ -875,6 +877,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
{
LOCK(cs_args);
m_config_args.clear();
+ m_config_sections.clear();
}
const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
@@ -882,7 +885,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
// ok to not have a config file
if (stream.good()) {
- if (!ReadConfigStream(stream, error, ignore_invalid_keys)) {
+ if (!ReadConfigStream(stream, confPath, error, ignore_invalid_keys)) {
return false;
}
// if there is an -includeconf in the override args, but it is empty, that means the user
@@ -913,7 +916,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
for (const std::string& to_include : includeconf) {
fsbridge::ifstream include_config(GetConfigFile(to_include));
if (include_config.good()) {
- if (!ReadConfigStream(include_config, error, ignore_invalid_keys)) {
+ if (!ReadConfigStream(include_config, to_include, error, ignore_invalid_keys)) {
return false;
}
LogPrintf("Included configuration file %s\n", to_include.c_str());
@@ -965,23 +968,6 @@ std::string ArgsManager::GetChainName() const
return CBaseChainParams::MAIN;
}
-#ifndef WIN32
-fs::path GetPidFile()
-{
- return AbsPathForConfigVal(fs::path(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)));
-}
-
-void CreatePidFile(const fs::path &path, pid_t pid)
-{
- FILE* file = fsbridge::fopen(path, "w");
- if (file)
- {
- fprintf(file, "%d\n", pid);
- fclose(file);
- }
-}
-#endif
-
bool RenameOver(fs::path src, fs::path dest)
{
#ifdef WIN32
diff --git a/src/util/system.h b/src/util/system.h
index 8867e49478..54eb88e261 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -40,7 +40,6 @@
int64_t GetStartupTime();
extern const char * const BITCOIN_CONF_FILENAME;
-extern const char * const BITCOIN_PID_FILENAME;
/** Translate a message to the native language of the user. */
const extern std::function<std::string(const char*)> G_TRANSLATION_FUN;
@@ -73,6 +72,7 @@ bool RenameOver(fs::path src, fs::path dest);
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false);
void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name);
bool DirIsWritable(const fs::path& directory);
+bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes = 0);
/** Release all directory locks. This is used for unit testing only, at runtime
* the global destructor will take care of the locks.
@@ -86,10 +86,6 @@ const fs::path &GetBlocksDir();
const fs::path &GetDataDir(bool fNetSpecific = true);
void ClearDatadirCache();
fs::path GetConfigFile(const std::string& confPath);
-#ifndef WIN32
-fs::path GetPidFile();
-void CreatePidFile(const fs::path &path, pid_t pid);
-#endif
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
#endif
@@ -132,6 +128,13 @@ enum class OptionsCategory {
HIDDEN // Always the last option to avoid printing these in the help
};
+struct SectionInfo
+{
+ std::string m_name;
+ std::string m_file;
+ int m_line;
+};
+
class ArgsManager
{
protected:
@@ -152,9 +155,9 @@ protected:
std::string m_network GUARDED_BY(cs_args);
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
- std::set<std::string> m_config_sections GUARDED_BY(cs_args);
+ std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
- NODISCARD bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false);
+ NODISCARD bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
public:
ArgsManager();
@@ -178,7 +181,7 @@ public:
/**
* Log warnings for unrecognized section names in the config file.
*/
- const std::set<std::string> GetUnrecognizedSections() const;
+ const std::list<SectionInfo> GetUnrecognizedSections() const;
/**
* Return a vector of strings of the given argument
diff --git a/src/validation.cpp b/src/validation.cpp
index 1806bc1268..ae3985485e 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -15,6 +15,7 @@
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <cuckoocache.h>
+#include <flatfile.h>
#include <hash.h>
#include <index/txindex.h>
#include <policy/fees.h>
@@ -35,9 +36,9 @@
#include <txmempool.h>
#include <ui_interface.h>
#include <undo.h>
-#include <util/system.h>
#include <util/moneystr.h>
#include <util/strencodings.h>
+#include <util/system.h>
#include <validationinterface.h>
#include <warnings.h>
@@ -165,7 +166,7 @@ public:
* that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
*/
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view:
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view);
@@ -177,7 +178,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);
@@ -204,10 +205,12 @@ private:
void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) 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;
/**
@@ -317,7 +320,9 @@ static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &
static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
-static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
+static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false);
+static FlatFileSeq BlockFileSeq();
+static FlatFileSeq UndoFileSeq();
bool CheckFinalTx(const CTransaction &tx, int flags)
{
@@ -1042,7 +1047,7 @@ bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus
// CBlock and CBlockIndex
//
-static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart)
+static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessageHeader::MessageStartChars& messageStart)
{
// Open history file to append
CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
@@ -1063,7 +1068,7 @@ static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMes
return true;
}
-bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
+bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams)
{
block.SetNull();
@@ -1089,7 +1094,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus:
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
{
- CDiskBlockPos blockPos;
+ FlatFilePos blockPos;
{
LOCK(cs_main);
blockPos = pindex->GetBlockPos();
@@ -1103,9 +1108,9 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus
return true;
}
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start)
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start)
{
- CDiskBlockPos hpos = pos;
+ FlatFilePos hpos = pos;
hpos.nPos -= 8; // Seek back 8 bytes for meta header
CAutoFile filein(OpenBlockFile(hpos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull()) {
@@ -1140,7 +1145,7 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos,
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
{
- CDiskBlockPos block_pos;
+ FlatFilePos block_pos;
{
LOCK(cs_main);
block_pos = pindex->GetBlockPos();
@@ -1450,9 +1455,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
return true;
}
-namespace {
-
-bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& 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);
@@ -1479,9 +1482,9 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint
return true;
}
-static bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex *pindex)
+bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
{
- CDiskBlockPos pos = pindex->GetUndoPos();
+ FlatFilePos pos = pindex->GetUndoPos();
if (pos.IsNull()) {
return error("%s: no undo data available", __func__);
}
@@ -1528,8 +1531,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.
@@ -1627,37 +1628,24 @@ void static FlushBlockFile(bool fFinalize = false)
{
LOCK(cs_LastBlockFile);
- CDiskBlockPos posOld(nLastBlockFile, 0);
- bool status = true;
-
- FILE *fileOld = OpenBlockFile(posOld);
- if (fileOld) {
- if (fFinalize)
- status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize);
- status &= FileCommit(fileOld);
- fclose(fileOld);
- }
-
- fileOld = OpenUndoFile(posOld);
- if (fileOld) {
- if (fFinalize)
- status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize);
- status &= FileCommit(fileOld);
- fclose(fileOld);
- }
+ FlatFilePos block_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize);
+ FlatFilePos undo_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nUndoSize);
+ bool status = true;
+ status &= BlockFileSeq().Flush(block_pos_old, fFinalize);
+ status &= UndoFileSeq().Flush(undo_pos_old, fFinalize);
if (!status) {
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
}
}
-static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
+static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize);
static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
{
// Write undo information to disk
if (pindex->GetUndoPos().IsNull()) {
- CDiskBlockPos _pos;
+ FlatFilePos _pos;
if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, CLIENT_VERSION) + 40))
return error("ConnectBlock(): FindUndoPos failed");
if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
@@ -2134,8 +2122,9 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
// Write blocks and block index to disk.
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
- if (!CheckDiskSpace(0, true))
- return state.Error("out of disk space");
+ if (!CheckDiskSpace(GetBlocksDir())) {
+ return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!"));
+ }
// First make sure all block and undo data is flushed to disk.
FlushBlockFile();
// Then update all block file information (which may refer to block and undo files).
@@ -2168,8 +2157,9 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
// twice (once in the log, and once in the tables). This is already
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
- if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize()))
- return state.Error("out of disk space");
+ if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * pcoinsTip->GetCacheSize())) {
+ return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!"));
+ }
// Flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush())
return AbortNode(state, "Failed to write to coin database");
@@ -2259,12 +2249,6 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
}
if (nUpgraded > 0)
AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
- if (nUpgraded > 100/2)
- {
- std::string strWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect");
- // notify GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
- DoWarning(strWarning);
- }
}
LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, /* Continued */
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
@@ -2640,6 +2624,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
@@ -2668,15 +2660,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);
@@ -2787,64 +2777,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 succesful,
+ // 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);
+ }
+
+ // 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;
}
- // Mark the block itself as invalid.
- pindex->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(pindex);
- setBlockIndexCandidates.erase(pindex);
- m_failed_blocks.insert(pindex);
+ {
+ 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;
+ }
- // DisconnectTip will add transactions to disconnectpool; try to add these
- // back to the mempool.
- UpdateMempoolForReorg(disconnectpool, true);
+ // 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);
+ // 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;
}
@@ -2928,7 +2939,7 @@ CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
}
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
-void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
+void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos, const Consensus::Params& consensusParams)
{
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
@@ -2974,7 +2985,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
}
}
-static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
+static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
{
LOCK(cs_LastBlockFile);
@@ -3009,21 +3020,13 @@ static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int
vinfoBlockFile[nFile].nSize += nAddSize;
if (!fKnown) {
- unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE;
- unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE;
- if (nNewChunks > nOldChunks) {
- if (fPruneMode)
- fCheckForPruning = true;
- if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos, true)) {
- FILE *file = OpenBlockFile(pos);
- if (file) {
- LogPrintf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile);
- AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos);
- fclose(file);
- }
- }
- else
- return error("out of disk space");
+ bool out_of_space;
+ size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
+ if (out_of_space) {
+ return AbortNode("Disk space is low!", _("Error: Disk space is low!"));
+ }
+ if (bytes_allocated != 0 && fPruneMode) {
+ fCheckForPruning = true;
}
}
@@ -3031,32 +3034,23 @@ static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int
return true;
}
-static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
+static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize)
{
pos.nFile = nFile;
LOCK(cs_LastBlockFile);
- unsigned int nNewSize;
pos.nPos = vinfoBlockFile[nFile].nUndoSize;
- nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize;
+ vinfoBlockFile[nFile].nUndoSize += nAddSize;
setDirtyFileInfo.insert(nFile);
- unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
- unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
- if (nNewChunks > nOldChunks) {
- if (fPruneMode)
- fCheckForPruning = true;
- if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos, true)) {
- FILE *file = OpenUndoFile(pos);
- if (file) {
- LogPrintf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile);
- AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos);
- fclose(file);
- }
- }
- else
- return state.Error("out of disk space");
+ bool out_of_space;
+ size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
+ if (out_of_space) {
+ return AbortNode(state, "Disk space is low!", _("Error: Disk space is low!"));
+ }
+ if (bytes_allocated != 0 && fPruneMode) {
+ fCheckForPruning = true;
}
return true;
@@ -3441,26 +3435,26 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
}
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-static CDiskBlockPos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const CDiskBlockPos* dbp) {
+static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const FlatFilePos* dbp) {
unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION);
- CDiskBlockPos blockPos;
+ FlatFilePos blockPos;
if (dbp != nullptr)
blockPos = *dbp;
if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != nullptr)) {
error("%s: FindBlockPos failed", __func__);
- return CDiskBlockPos();
+ return FlatFilePos();
}
if (dbp == nullptr) {
if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) {
AbortNode("Failed to write block");
- return CDiskBlockPos();
+ return FlatFilePos();
}
}
return blockPos;
}
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
-bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock)
+bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock)
{
const CBlock& block = *pblock;
@@ -3522,7 +3516,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
// Write block to history file
if (fNewBlock) *fNewBlock = true;
try {
- CDiskBlockPos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
+ FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
if (blockPos.IsNull()) {
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
return false;
@@ -3653,9 +3647,9 @@ void PruneOneBlockFile(const int fileNumber)
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
{
for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
- CDiskBlockPos pos(*it, 0);
- fs::remove(GetBlockPosFilename(pos, "blk"));
- fs::remove(GetBlockPosFilename(pos, "rev"));
+ FlatFilePos pos(*it, 0);
+ fs::remove(BlockFileSeq().FileName(pos));
+ fs::remove(UndoFileSeq().FileName(pos));
LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
}
}
@@ -3763,52 +3757,28 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
nLastBlockWeCanPrune, count);
}
-bool CheckDiskSpace(uint64_t nAdditionalBytes, bool blocks_dir)
+static FlatFileSeq BlockFileSeq()
{
- uint64_t nFreeBytesAvailable = fs::space(blocks_dir ? GetBlocksDir() : GetDataDir()).available;
-
- // Check for nMinDiskSpace bytes (currently 50MB)
- if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
- return AbortNode("Disk space is low!", _("Error: Disk space is low!"));
-
- return true;
+ return FlatFileSeq(GetBlocksDir(), "blk", BLOCKFILE_CHUNK_SIZE);
}
-static FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
+static FlatFileSeq UndoFileSeq()
{
- if (pos.IsNull())
- return nullptr;
- fs::path path = GetBlockPosFilename(pos, prefix);
- fs::create_directories(path.parent_path());
- FILE* file = fsbridge::fopen(path, fReadOnly ? "rb": "rb+");
- if (!file && !fReadOnly)
- file = fsbridge::fopen(path, "wb+");
- if (!file) {
- LogPrintf("Unable to open file %s\n", path.string());
- return nullptr;
- }
- if (pos.nPos) {
- if (fseek(file, pos.nPos, SEEK_SET)) {
- LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string());
- fclose(file);
- return nullptr;
- }
- }
- return file;
+ return FlatFileSeq(GetBlocksDir(), "rev", UNDOFILE_CHUNK_SIZE);
}
-FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) {
- return OpenDiskFile(pos, "blk", fReadOnly);
+FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly) {
+ return BlockFileSeq().Open(pos, fReadOnly);
}
/** Open an undo file (rev?????.dat) */
-static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
- return OpenDiskFile(pos, "rev", fReadOnly);
+static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly) {
+ return UndoFileSeq().Open(pos, fReadOnly);
}
-fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
+fs::path GetBlockPosFilename(const FlatFilePos &pos)
{
- return GetBlocksDir() / strprintf("%s%05u.dat", prefix, pos.nFile);
+ return BlockFileSeq().FileName(pos);
}
CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)
@@ -3915,7 +3885,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
}
for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++)
{
- CDiskBlockPos pos(*it, 0);
+ FlatFilePos pos(*it, 0);
if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) {
return false;
}
@@ -4168,38 +4138,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));
@@ -4207,53 +4253,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;
@@ -4348,7 +4356,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
try {
const CBlock& block = chainparams.GenesisBlock();
- CDiskBlockPos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
+ FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr);
if (blockPos.IsNull())
return error("%s: writing genesis block to disk failed", __func__);
CBlockIndex *pindex = AddToBlockIndex(block);
@@ -4365,10 +4373,10 @@ bool LoadGenesisBlock(const CChainParams& chainparams)
return g_chainstate.LoadGenesisBlock(chainparams);
}
-bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp)
+bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos *dbp)
{
// Map of disk positions for blocks with unknown parent (only used for reindex)
- static std::multimap<uint256, CDiskBlockPos> mapBlocksUnknownParent;
+ static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent;
int64_t nStart = GetTimeMillis();
int nLoaded = 0;
@@ -4454,9 +4462,9 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
while (!queue.empty()) {
uint256 head = queue.front();
queue.pop_front();
- std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head);
+ std::pair<std::multimap<uint256, FlatFilePos>::iterator, std::multimap<uint256, FlatFilePos>::iterator> range = mapBlocksUnknownParent.equal_range(head);
while (range.first != range.second) {
- std::multimap<uint256, CDiskBlockPos>::iterator it = range.first;
+ std::multimap<uint256, FlatFilePos>::iterator it = range.first;
std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>();
if (ReadBlockFromDisk(*pblockrecursive, it->second, chainparams.GetConsensus()))
{
diff --git a/src/validation.h b/src/validation.h
index 1975846b69..d84e3a0dbc 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;
@@ -181,9 +181,6 @@ extern arith_uint256 nMinimumChainWork;
/** Best header we've seen so far (used for getheaders queries' starting points). */
extern CBlockIndex *pindexBestHeader;
-/** Minimum disk space required - used in CheckDiskSpace() */
-static const uint64_t nMinDiskSpace = 52428800;
-
/** Pruning-related variables and constants */
/** True if any block files have ever been pruned. */
extern bool fHavePruned;
@@ -245,14 +242,12 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
*/
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr, CBlockHeader* first_invalid = nullptr) LOCKS_EXCLUDED(cs_main);
-/** Check whether enough disk space is available for an incoming block */
-bool CheckDiskSpace(uint64_t nAdditionalBytes = 0, bool blocks_dir = false);
/** Open a block file (blk?????.dat) */
-FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
+FILE* OpenBlockFile(const FlatFilePos &pos, bool fReadOnly = false);
/** Translation to a filesystem path */
-fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix);
+fs::path GetBlockPosFilename(const FlatFilePos &pos);
/** Import blocks from an external file */
-bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskBlockPos *dbp = nullptr);
+bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos *dbp = nullptr);
/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
bool LoadGenesisBlock(const CChainParams& chainparams);
/** Load the block tree and coins database from disk,
@@ -391,11 +386,13 @@ void InitScriptExecutionCache();
/** Functions for disk access for blocks */
-bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
+bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start);
+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 */
@@ -448,7 +445,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/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 7a71aea715..ef7f3be728 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -27,9 +27,7 @@ static feebumper::Result PreconditionChecks(interfaces::Chain::Lock& locked_chai
}
{
- LOCK(mempool.cs);
- auto it_mp = mempool.mapTx.find(wtx.GetHash());
- if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) {
+ if (wallet->chain().hasDescendantsInMempool(wtx.GetHash())) {
errors.push_back("Transaction has descendants in the mempool");
return feebumper::Result::INVALID_PARAMETER;
}
@@ -125,16 +123,17 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
// 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);
+ 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(::incrementalRelayFee.GetFee(maxNewTxSize))));
+ FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(nodeIncrementalRelayFee.GetFee(maxNewTxSize))));
return Result::INVALID_PARAMETER;
}
CAmount requiredFee = GetRequiredFee(*wallet, maxNewTxSize);
@@ -146,7 +145,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
new_fee = total_fee;
nNewFeeRate = CFeeRate(total_fee, maxNewTxSize);
} else {
- new_fee = GetMinimumFee(*wallet, maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */);
+ 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
@@ -161,9 +160,10 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
}
// Check that in all cases the new fee doesn't violate maxTxFee
- if (new_fee > maxTxFee) {
+ const CAmount max_tx_fee = wallet->chain().maxTxFee();
+ 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(maxTxFee)));
+ FormatMoney(new_fee), FormatMoney(max_tx_fee)));
return Result::WALLET_ERROR;
}
@@ -197,7 +197,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
// If the output would become dust, discard it (converting the dust to fee)
poutput->nValue -= nDelta;
- if (poutput->nValue <= GetDustThreshold(*poutput, GetDiscardRate(*wallet, ::feeEstimator))) {
+ if (poutput->nValue <= GetDustThreshold(*poutput, GetDiscardRate(*wallet))) {
wallet->WalletLogPrintf("Bumping fee and discarding dust output\n");
new_fee += poutput->nValue;
mtx.vout.erase(mtx.vout.begin() + nOutput);
@@ -247,7 +247,7 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
CReserveKey reservekey(wallet);
CValidationState state;
- if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekey, g_connman.get(), state)) {
+ if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekey, state)) {
// NOTE: CommitTransaction never returns false, so this should never happen.
errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
return Result::WALLET_ERROR;
diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp
index 9e2984ff05..560c86a70a 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -6,7 +6,6 @@
#include <wallet/fees.h>
#include <policy/policy.h>
-#include <txmempool.h>
#include <util/system.h>
#include <validation.h>
#include <wallet/coincontrol.h>
@@ -19,12 +18,13 @@ CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes)
}
-CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc)
+CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc)
{
- CAmount fee_needed = GetMinimumFeeRate(wallet, coin_control, pool, estimator, feeCalc).GetFee(nTxBytes);
+ 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.chain().maxTxFee();
+ if (fee_needed > max_tx_fee) {
+ fee_needed = max_tx_fee;
if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE;
}
return fee_needed;
@@ -32,10 +32,10 @@ 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, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc)
+CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, FeeCalculation* feeCalc)
{
/* User control of how to calculate fee uses the following parameter precedence:
1. coin_control.m_feerate
@@ -64,7 +64,7 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr
if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true;
else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false;
- feerate_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate);
+ feerate_needed = wallet.chain().estimateSmartFee(target, conservative_estimate, feeCalc);
if (feerate_needed == CFeeRate(0)) {
// if we don't have enough data for estimateSmartFee, then use fallback fee
feerate_needed = wallet.m_fallback_fee;
@@ -74,7 +74,7 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr
if (wallet.m_fallback_fee == CFeeRate(0)) return feerate_needed;
}
// Obey mempool min fee when using smart fee estimation
- CFeeRate min_mempool_feerate = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ CFeeRate min_mempool_feerate = wallet.chain().mempoolMinFee();
if (feerate_needed < min_mempool_feerate) {
feerate_needed = min_mempool_feerate;
if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN;
@@ -90,13 +90,13 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr
return feerate_needed;
}
-CFeeRate GetDiscardRate(const CWallet& wallet, const CBlockPolicyEstimator& estimator)
+CFeeRate GetDiscardRate(const CWallet& wallet)
{
- unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
- CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */);
+ unsigned int highest_target = wallet.chain().estimateMaxBlocks();
+ CFeeRate discard_rate = wallet.chain().estimateSmartFee(highest_target, false /* conservative */);
// 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/fees.h b/src/wallet/fees.h
index 6bfee456c0..434f211dc2 100644
--- a/src/wallet/fees.h
+++ b/src/wallet/fees.h
@@ -8,10 +8,8 @@
#include <amount.h>
-class CBlockPolicyEstimator;
class CCoinControl;
class CFeeRate;
-class CTxMemPool;
class CWallet;
struct FeeCalculation;
@@ -25,7 +23,7 @@ CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes);
* Estimate the minimum fee considering user set parameters
* and the required fee
*/
-CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc);
+CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc);
/**
* Return the minimum required feerate taking into account the
@@ -37,11 +35,11 @@ CFeeRate GetRequiredFeeRate(const CWallet& wallet);
* Estimate the minimum fee rate considering user set parameters
* and the required fee
*/
-CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc);
+CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, FeeCalculation* feeCalc);
/**
* Return the maximum feerate for discarding change.
*/
-CFeeRate GetDiscardRate(const CWallet& wallet, const CBlockPolicyEstimator& estimator);
+CFeeRate GetDiscardRate(const CWallet& wallet);
#endif // BITCOIN_WALLET_FEES_H
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 20d540c8db..76a7eaa681 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -138,19 +138,22 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
// 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)) {
- return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
+ chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
+ return false;
} else if (!fs::is_directory(wallet_dir)) {
- return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
+ 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()) {
- return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
+ 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)..."));
+ 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
@@ -164,14 +167,15 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
WalletLocation location(wallet_file);
if (!wallet_paths.insert(location.GetPath()).second) {
- return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
+ 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()) InitError(error_string);
- if (!warning_string.empty()) InitWarning(warning_string);
+ if (!error_string.empty()) chain.initError(error_string);
+ if (!warning_string.empty()) chain.initWarning(warning_string);
if (!verify_success) return false;
}
diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp
index 761e7b7dd7..1b17b09763 100644
--- a/src/wallet/psbtwallet.cpp
+++ b/src/wallet/psbtwallet.cpp
@@ -4,7 +4,7 @@
#include <wallet/psbtwallet.h>
-bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, TransactionError& error, bool& complete, int sighash_type, bool sign, bool bip32derivs)
+TransactionError FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs)
{
LOCK(pwallet->cs_wallet);
// Get all of the previous transactions
@@ -19,8 +19,7 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, Transac
// Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
if (!input.IsSane()) {
- error = TransactionError::INVALID_PSBT;
- return false;
+ return TransactionError::INVALID_PSBT;
}
// If we have no utxo, grab it from the wallet.
@@ -37,8 +36,7 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, Transac
// Get the Sighash type
if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
- error = TransactionError::SIGHASH_MISMATCH;
- return false;
+ return TransactionError::SIGHASH_MISMATCH;
}
complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), psbtx, i, sighash_type);
@@ -58,5 +56,5 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, Transac
psbt_out.FromSignatureData(sigdata);
}
- return true;
+ return TransactionError::OK;
}
diff --git a/src/wallet/psbtwallet.h b/src/wallet/psbtwallet.h
index b679f5c6ba..a24a0967d2 100644
--- a/src/wallet/psbtwallet.h
+++ b/src/wallet/psbtwallet.h
@@ -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.
@@ -18,16 +18,14 @@
*
* @param[in] pwallet pointer to a wallet
* @param[in] &psbtx reference to PartiallySignedTransaction to fill in
- * @param[out] &error reference to UniValue to fill with error info on failure
* @param[out] &complete indicates whether the PSBT is now complete
* @param[in] sighash_type the sighash type to use when signing (if PSBT does not specify)
* @param[in] sign whether to sign or not
* @param[in] bip32derivs whether to fill in bip32 derivation information if available
- * return true on success, false on error (and fills in `error`)
+ * return error
*/
-bool FillPSBT(const CWallet* pwallet,
+NODISCARD TransactionError FillPSBT(const CWallet* pwallet,
PartiallySignedTransaction& psbtx,
- TransactionError& error,
bool& complete,
int sighash_type = 1 /* SIGHASH_ALL */,
bool sign = true,
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index f38202a2b8..9c5dae3623 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -157,8 +157,9 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
- if (fRescan && fPruneMode)
+ if (fRescan && pwallet->chain().getPruneMode()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
+ }
if (fRescan && !reserver.reserve()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
@@ -313,8 +314,9 @@ UniValue importaddress(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
- if (fRescan && fPruneMode)
+ if (fRescan && pwallet->chain().getPruneMode()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
+ }
WalletRescanReserver reserver(pwallet);
if (fRescan && !reserver.reserve()) {
@@ -346,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;
@@ -501,8 +507,9 @@ UniValue importpubkey(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
- if (fRescan && fPruneMode)
+ if (fRescan && pwallet->chain().getPruneMode()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
+ }
WalletRescanReserver reserver(pwallet);
if (fRescan && !reserver.reserve()) {
@@ -529,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;
@@ -562,8 +573,9 @@ UniValue importwallet(const JSONRPCRequest& request)
},
}.ToString());
- if (fPruneMode)
+ if (pwallet->chain().getPruneMode()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
+ }
WalletRescanReserver reserver(pwallet);
if (!reserver.reserve()) {
@@ -591,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] == '#')
@@ -633,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);
@@ -664,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);
@@ -683,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();
@@ -1132,13 +1144,10 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
if (!data.exists("range")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor is ranged, please specify the range");
}
- const UniValue& range = data["range"];
- range_start = range.exists("start") ? range["start"].get_int64() : 0;
- if (!range.exists("end")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range for descriptor must be specified");
- }
- range_end = range["end"].get_int64();
- if (range_end < range_start || range_start < 0) {
+ auto range = ParseRange(data["range"]);
+ range_start = range.first;
+ range_end = range.second;
+ if (range_start < 0 || (range_end >> 31) != 0 || range_end - range_start >= 1000000) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid descriptor range specified");
}
}
@@ -1373,12 +1382,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
{"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
}
},
- {"range", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the start and end of the range to import",
- {
- {"start", RPCArg::Type::NUM, /* default */ "0", "Start of the range to import"},
- {"end", RPCArg::Type::NUM, RPCArg::Optional::NO, "End of the range to import (inclusive)"},
- }
- },
+ {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in the form [begin,end]) to import"},
{"internal", RPCArg::Type::BOOL, /* default */ "false", "Stating whether matching outputs should be treated as not incoming payments (also known as change)"},
{"watchonly", RPCArg::Type::BOOL, /* default */ "false", "Stating whether matching outputs should be considered watchonly."},
{"label", RPCArg::Type::STR, /* default */ "''", "Label to assign to the address, only allowed with internal=false"},
@@ -1472,7 +1476,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 37fc88dfd5..f1da1fb589 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -19,7 +19,6 @@
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
-#include <rpc/mining.h>
#include <rpc/rawtransaction.h>
#include <rpc/server.h>
#include <rpc/util.h>
@@ -124,8 +123,7 @@ static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& lo
// Add opt-in RBF status
std::string rbfStatus = "no";
if (confirms <= 0) {
- LOCK(mempool.cs);
- RBFTransactionState rbfState = IsRBFOptIn(*wtx.tx, mempool);
+ RBFTransactionState rbfState = chain.isRBFOptIn(*wtx.tx);
if (rbfState == RBFTransactionState::UNKNOWN)
rbfStatus = "unknown";
else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)
@@ -319,7 +317,7 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
if (nValue > curBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
- if (pwallet->GetBroadcastTransactions() && !g_connman) {
+ if (pwallet->GetBroadcastTransactions() && !pwallet->chain().p2pEnabled()) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
@@ -341,7 +339,7 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
CValidationState state;
- if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reservekey, g_connman.get(), state)) {
+ if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reservekey, state)) {
strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
@@ -425,7 +423,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
}
if (!request.params[6].isNull()) {
- coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
+ coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
}
if (!request.params[7].isNull()) {
@@ -607,7 +605,6 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -630,8 +627,9 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
CAmount nAmount = 0;
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second;
- if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
+ if (wtx.IsCoinBase() || !locked_chain->checkFinalTx(*wtx.tx)) {
continue;
+ }
for (const CTxOut& txout : wtx.tx->vout)
if (txout.scriptPubKey == scriptPubKey)
@@ -679,7 +677,6 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@@ -696,8 +693,9 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
CAmount nAmount = 0;
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second;
- if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
+ if (wtx.IsCoinBase() || !locked_chain->checkFinalTx(*wtx.tx)) {
continue;
+ }
for (const CTxOut& txout : wtx.tx->vout)
{
@@ -809,9 +807,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",
{
@@ -821,7 +817,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"
@@ -852,7 +848,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
@@ -861,7 +861,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
- if (pwallet->GetBroadcastTransactions() && !g_connman) {
+ if (pwallet->GetBroadcastTransactions() && !pwallet->chain().p2pEnabled()) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
@@ -869,9 +869,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())
@@ -887,7 +884,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
}
if (!request.params[6].isNull()) {
- coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
+ coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
}
if (!request.params[7].isNull()) {
@@ -899,7 +896,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_);
@@ -916,7 +912,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++) {
@@ -931,11 +926,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());
@@ -949,7 +939,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
CValidationState state;
- if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, keyChange, g_connman.get(), state)) {
+ if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, keyChange, state)) {
strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
@@ -1051,8 +1041,6 @@ struct tallyitem
static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
-
// Minimum confirmations
int nMinDepth = 1;
if (!params[0].isNull())
@@ -1083,8 +1071,9 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second;
- if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
+ if (wtx.IsCoinBase() || !locked_chain.checkFinalTx(*wtx.tx)) {
continue;
+ }
int nDepth = wtx.GetDepthInMainChain(locked_chain);
if (nDepth < nMinDepth)
@@ -2370,8 +2359,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()));
}
@@ -2671,49 +2660,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 (!g_connman)
- 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(), g_connman.get());
- 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);
@@ -2994,7 +2940,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("feeRate")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate");
}
- coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"]);
+ coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"], pwallet->chain().estimateMaxBlocks());
}
if (options.exists("estimate_mode")) {
if (options.exists("feeRate")) {
@@ -3152,7 +3098,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"},
},
},
},
@@ -3284,7 +3230,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
if (options.exists("confTarget") && options.exists("totalFee")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and totalFee options should not both be set. Please provide either a confirmation target for fee estimation or an explicit total fee for the transaction.");
} else if (options.exists("confTarget")) { // TODO: alias this to conf_target
- coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"]);
+ coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"], pwallet->chain().estimateMaxBlocks());
} else if (options.exists("totalFee")) {
totalFee = options["totalFee"].get_int64();
if (totalFee <= 0) {
@@ -3358,62 +3304,6 @@ static UniValue bumpfee(const JSONRPCRequest& request)
return result;
}
-UniValue generate(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() < 1 || request.params.size() > 2) {
- throw std::runtime_error(
- RPCHelpMan{"generate",
- "\nMine up to nblocks blocks immediately (before the RPC call returns) to an address in the wallet.\n",
- {
- {"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
- {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."},
- },
- RPCResult{
- "[ blockhashes ] (array) hashes of blocks generated\n"
- },
- RPCExamples{
- "\nGenerate 11 blocks\n"
- + HelpExampleCli("generate", "11")
- },
- }.ToString());
- }
-
- if (!IsDeprecatedRPCEnabled("generate")) {
- throw JSONRPCError(RPC_METHOD_DEPRECATED, "The wallet generate rpc method is deprecated and will be fully removed in v0.19. "
- "To use generate in v0.18, restart bitcoind with -deprecatedrpc=generate.\n"
- "Clients should transition to using the node rpc method generatetoaddress\n");
- }
-
- int num_generate = request.params[0].get_int();
- uint64_t max_tries = 1000000;
- if (!request.params[1].isNull()) {
- max_tries = request.params[1].get_int();
- }
-
- std::shared_ptr<CReserveScript> coinbase_script;
- pwallet->GetScriptForMining(coinbase_script);
-
- // If the keypool is exhausted, no script is returned at all. Catch this.
- if (!coinbase_script) {
- throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
- }
-
- //throw an error if no script was provided
- if (coinbase_script->reserveScript.empty()) {
- throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available");
- }
-
- return generateBlocks(coinbase_script, num_generate, max_tries, true);
-}
-
UniValue rescanblockchain(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -3894,7 +3784,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
}.ToString());
}
- if (IsInitialBlockDownload()) {
+ if (pwallet->chain().isInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
}
@@ -3939,19 +3829,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);
@@ -4007,8 +3884,8 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
bool bip32derivs = request.params[3].isNull() ? false : request.params[3].get_bool();
bool complete = true;
- TransactionError err;
- if (!FillPSBT(pwallet, psbtx, err, complete, nHashType, sign, bip32derivs)) {
+ const TransactionError err = FillPSBT(pwallet, psbtx, complete, nHashType, sign, bip32derivs);
+ if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}
@@ -4125,8 +4002,8 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
// Fill transaction with out data but don't sign
bool bip32derivs = request.params[4].isNull() ? false : request.params[4].get_bool();
bool complete = true;
- TransactionError err;
- if (!FillPSBT(pwallet, psbtx, err, complete, 1, false, bip32derivs)) {
+ const TransactionError err = FillPSBT(pwallet, psbtx, complete, 1, false, bip32derivs);
+ if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}
@@ -4156,8 +4033,6 @@ UniValue importmulti(const JSONRPCRequest& request);
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
- { "generating", "generate", &generate, {"nblocks","maxtries"} },
- { "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} },
@@ -4215,8 +4090,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/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index 2a3149de46..789e86e21b 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -62,9 +62,8 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
ssData >> psbtx;
// Fill transaction with our data
- TransactionError err;
bool complete = true;
- FillPSBT(&m_wallet, psbtx, err, complete, SIGHASH_ALL, false, true);
+ BOOST_REQUIRE_EQUAL(TransactionError::OK, FillPSBT(&m_wallet, psbtx, complete, SIGHASH_ALL, false, true));
// Get the final tx
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index a5fb1db86c..6a051214a9 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -13,12 +13,7 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
{
bool fFirstRun;
m_wallet.LoadWallet(fFirstRun);
- RegisterValidationInterface(&m_wallet);
+ m_wallet.m_chain_notifications_handler = m_chain->handleNotifications(m_wallet);
- 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..dcfbc55f94 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.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 e674b2faea..af57dbf5f6 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -368,7 +368,7 @@ public:
CCoinControl dummy;
BOOST_CHECK(wallet->CreateTransaction(*m_locked_chain, {recipient}, tx, reservekey, fee, changePos, error, dummy));
CValidationState state;
- BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, nullptr, state));
+ BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, state));
CMutableTransaction blocktx;
{
LOCK(wallet->cs_wallet);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 388422bec8..f0499d5b32 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -12,6 +12,7 @@
#include <consensus/validation.h>
#include <fs.h>
#include <interfaces/chain.h>
+#include <interfaces/wallet.h>
#include <key.h>
#include <key_io.h>
#include <keystore.h>
@@ -95,7 +96,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.
{
@@ -192,7 +193,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;
@@ -280,9 +281,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));
@@ -344,16 +345,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;
}
@@ -366,7 +367,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;
}
@@ -568,7 +569,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;
@@ -592,7 +593,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;
@@ -876,9 +877,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);
@@ -940,7 +941,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
wtx.BindWallet(this);
bool fInsertedNew = ret.second;
if (fInsertedNew) {
- wtx.nTimeReceived = GetAdjustedTime();
+ wtx.nTimeReceived = chain().getAdjustedTime();
wtx.nOrderPos = IncOrderPosNext(&batch);
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
wtx.nTimeSmart = ComputeTimeSmart(wtx);
@@ -1242,7 +1243,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
@@ -1257,19 +1259,19 @@ 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 */);
}
}
@@ -1296,7 +1298,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();
}
@@ -1789,7 +1791,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
progress_end = chain().guessVerificationProgress(stop_block.IsNull() ? tip_hash : stop_block);
}
double progress_current = progress_begin;
- while (block_height && !fAbortRescan && !ShutdownRequested()) {
+ 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))));
}
@@ -1851,7 +1853,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
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()) {
+ } 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;
}
@@ -1859,13 +1861,11 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
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
@@ -1875,7 +1875,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));
@@ -1886,25 +1886,21 @@ void CWallet::ReacceptWalletTransactions()
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
CValidationState state;
- wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state);
+ wtx.AcceptToMemoryPool(locked_chain, state);
}
}
-bool CWalletTx::RelayWalletTransaction(interfaces::Chain::Lock& locked_chain, CConnman* connman)
+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, maxTxFee, state)) {
+ if (InMempool() || AcceptToMemoryPool(locked_chain, state)) {
pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
- if (connman) {
- CInv inv(MSG_TX, GetHash());
- connman->ForEachNode([&inv](CNode* pnode)
- {
- pnode->PushInventory(inv);
- });
+ if (pwallet->chain().p2pEnabled()) {
+ pwallet->chain().relayTransaction(GetHash());
return true;
}
}
@@ -2075,11 +2071,10 @@ bool CWalletTx::InMempool() const
bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain) const
{
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
-
// Quick answer in most cases
- if (!CheckFinalTx(*tx))
+ if (!locked_chain.checkFinalTx(*tx)) {
return false;
+ }
int nDepth = GetDepthInMainChain(locked_chain);
if (nDepth >= 1)
return true;
@@ -2115,53 +2110,37 @@ 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, CConnman* connman)
-{
- std::vector<uint256> result;
-
- 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, connman))
- result.push_back(wtx.GetHash());
- }
- return result;
-}
-
-void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman)
+void CWallet::ResendWalletTransactions(interfaces::Chain::Lock& locked_chain, int64_t nBestBlockTime)
{
// 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 (nBestBlockTime < 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, connman);
- if (!relayed.empty())
- WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size());
+ int relayed_tx_count = 0;
+
+ { // cs_wallet scope
+ LOCK(cs_wallet);
+
+ // 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 > nBestBlockTime - 5 * 60) continue;
+ relayed_tx_count += wtx.RelayWalletTransaction(locked_chain) ? 1 : 0;
+ }
+ } // cs_wallet
+
+ if (relayed_tx_count > 0) {
+ WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed_tx_count);
+ }
}
/** @} */ // end of mapWallet
@@ -2183,9 +2162,9 @@ CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) con
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);
+ const CWalletTx& wtx = entry.second;
+ if (wtx.IsTrusted(*locked_chain) && wtx.GetDepthInMainChain(*locked_chain) >= min_depth) {
+ nTotal += wtx.GetAvailableCredit(*locked_chain, true, filter);
}
}
}
@@ -2201,9 +2180,9 @@ CAmount CWallet::GetUnconfirmedBalance() const
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);
+ const CWalletTx& wtx = entry.second;
+ if (!wtx.IsTrusted(*locked_chain) && wtx.GetDepthInMainChain(*locked_chain) == 0 && wtx.InMempool())
+ nTotal += wtx.GetAvailableCredit(*locked_chain);
}
}
return nTotal;
@@ -2217,8 +2196,8 @@ CAmount CWallet::GetImmatureBalance() const
LOCK(cs_wallet);
for (const auto& entry : mapWallet)
{
- const CWalletTx* pcoin = &entry.second;
- nTotal += pcoin->GetImmatureCredit(*locked_chain);
+ const CWalletTx& wtx = entry.second;
+ nTotal += wtx.GetImmatureCredit(*locked_chain);
}
}
return nTotal;
@@ -2232,9 +2211,9 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
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);
+ const CWalletTx& wtx = entry.second;
+ if (!wtx.IsTrusted(*locked_chain) && wtx.GetDepthInMainChain(*locked_chain) == 0 && wtx.InMempool())
+ nTotal += wtx.GetAvailableCredit(*locked_chain, true, ISMINE_WATCH_ONLY);
}
}
return nTotal;
@@ -2248,54 +2227,13 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
LOCK(cs_wallet);
for (const auto& entry : mapWallet)
{
- const CWalletTx* pcoin = &entry.second;
- nTotal += pcoin->GetImmatureWatchOnlyCredit(*locked_chain);
+ const CWalletTx& wtx = entry.second;
+ nTotal += wtx.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
-{
- LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
- 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 || !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;
- }
- }
-
- // For outgoing txs, subtract amount debited.
- if (outgoing) {
- balance -= debit;
- }
- }
-
- return balance;
-}
-
CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
{
auto locked_chain = chain().lock();
@@ -2314,7 +2252,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();
@@ -2323,24 +2260,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 (!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.
@@ -2357,7 +2295,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;
}
@@ -2369,7 +2307,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;
}
@@ -2380,8 +2318,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)))
@@ -2393,20 +2331,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;
@@ -2423,7 +2361,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;
@@ -2488,10 +2425,10 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
FeeCalculation feeCalc;
CCoinControl temp;
temp.m_confirm_target = 1008;
- CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, ::mempool, ::feeEstimator, &feeCalc);
+ CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, &feeCalc);
// Calculate cost of change
- CAmount cost_of_change = GetDiscardRate(*this, ::feeEstimator).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
+ CAmount cost_of_change = GetDiscardRate(*this).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
// Filter by the min conf specs and add to utxo_pool and calculate effective value
for (OutputGroup& group : groups) {
@@ -2565,13 +2502,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
}
@@ -2617,9 +2554,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;
@@ -2695,13 +2632,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;
@@ -2711,7 +2648,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;
@@ -2735,7 +2672,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
@@ -2809,7 +2746,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;
@@ -2861,10 +2798,10 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
CTxOut change_prototype_txout(0, scriptChange);
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
- CFeeRate discard_rate = GetDiscardRate(*this, ::feeEstimator);
+ CFeeRate discard_rate = GetDiscardRate(*this);
// Get the fee rate to use effective values in coin selection
- CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, ::mempool, ::feeEstimator, &feeCalc);
+ CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
nFeeRet = 0;
bool pick_new_inputs = true;
@@ -2905,7 +2842,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)
{
@@ -2997,7 +2934,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
return false;
}
- nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
+ nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, &feeCalc);
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
// eventually allow a fallback fee
strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
@@ -3006,7 +2943,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;
@@ -3025,7 +2962,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// change output. Only try this once.
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size
- CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr);
+ CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, nullptr);
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) {
pick_new_inputs = false;
@@ -3130,16 +3067,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits
- LockPoints lp;
- CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp);
- CTxMemPool::setEntries setAncestors;
- size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
- size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
- size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
- size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
- std::string errString;
- LOCK(::mempool.cs);
- if (!::mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
+ if (!chain().checkChainLimits(tx)) {
strFailReason = _("Transaction has too long of a mempool chain");
return false;
}
@@ -3159,7 +3087,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
/**
* Call after CreateTransaction unless you want to abort
*/
-bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state)
+bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CValidationState& state)
{
{
auto locked_chain = chain().lock();
@@ -3196,11 +3124,11 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
if (fBroadcastTransactions)
{
// Broadcast
- if (!wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state)) {
+ if (!wtx.AcceptToMemoryPool(*locked_chain, state)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state));
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
} else {
- wtx.RelayWalletTransaction(*locked_chain, connman);
+ wtx.RelayWalletTransaction(*locked_chain);
}
}
}
@@ -3242,8 +3170,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);
@@ -3269,7 +3197,6 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
MarkDirty();
return DBErrors::LOAD_OK;
-
}
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
@@ -3389,7 +3316,7 @@ bool CWallet::NewKeyPool()
size_t CWallet::KeypoolCountExternalKeys()
{
- AssertLockHeld(cs_wallet); // setExternalKeyPool
+ AssertLockHeld(cs_wallet);
return setExternalKeyPool.size() + set_pre_split_keypool.size();
}
@@ -3619,27 +3546,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;
@@ -3653,19 +3580,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 */
@@ -3679,7 +3606,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;
@@ -3696,7 +3623,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;
@@ -3819,38 +3746,27 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)
}
}
-void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script)
-{
- std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this);
- CPubKey pubkey;
- if (!rKey->GetReservedKey(pubkey))
- return;
-
- script = rKey;
- script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
-}
-
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);
@@ -3858,7 +3774,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);
@@ -3868,8 +3784,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
@@ -4097,23 +4013,23 @@ 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;
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
- uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
+ chain.initMessage(_("Zapping all transactions from wallet..."));
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(chain, location, WalletDatabase::Create(location.GetPath()));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DBErrors::LOAD_OK) {
- InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ chain.initError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
}
- uiInterface.InitMessage(_("Loading wallet..."));
+ chain.initMessage(_("Loading wallet..."));
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
@@ -4124,31 +4040,31 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (nLoadWalletRet != DBErrors::LOAD_OK)
{
if (nLoadWalletRet == DBErrors::CORRUPT) {
- InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ chain.initError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
else if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR)
{
- InitWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
- " or address book entries might be missing or incorrect."),
+ chain.initWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
+ " or address book entries might be missing or incorrect."),
walletFile));
}
else if (nLoadWalletRet == DBErrors::TOO_NEW) {
- InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));
+ chain.initError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));
return nullptr;
}
else if (nLoadWalletRet == DBErrors::NEED_REWRITE)
{
- InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
+ chain.initError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
return nullptr;
}
else {
- InitError(strprintf(_("Error loading %s"), walletFile));
+ chain.initError(strprintf(_("Error loading %s"), walletFile));
return nullptr;
}
}
- int prev_version = walletInstance->nWalletVersion;
+ int prev_version = walletInstance->GetVersion();
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
{
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
@@ -4162,7 +4078,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
walletInstance->WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
if (nMaxVersion < walletInstance->GetVersion())
{
- InitError(_("Cannot downgrade wallet"));
+ chain.initError(_("Cannot downgrade wallet"));
return nullptr;
}
walletInstance->SetMaxVersion(nMaxVersion);
@@ -4173,9 +4089,9 @@ 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) {
- 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."));
+ 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;
}
@@ -4203,7 +4119,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Regenerate the keypool if upgraded to HD
if (hd_upgrade) {
if (!walletInstance->TopUpKeyPool()) {
- InitError(_("Unable to generate keys"));
+ chain.initError(_("Unable to generate keys"));
return nullptr;
}
}
@@ -4227,7 +4143,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Top up the keypool
if (walletInstance->CanGenerateKeys() && !walletInstance->TopUpKeyPool()) {
- InitError(_("Unable to generate initial keys"));
+ chain.initError(_("Unable to generate initial keys"));
return nullptr;
}
@@ -4235,34 +4151,34 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
walletInstance->ChainStateFlushed(locked_chain->getTipLocator());
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation
- InitError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
+ chain.initError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
return NULL;
} else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
LOCK(walletInstance->cs_KeyStore);
if (!walletInstance->mapKeys.empty() || !walletInstance->mapCryptedKeys.empty()) {
- InitWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
+ chain.initWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
}
}
if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
- InitError(strprintf("Unknown address type '%s'", gArgs.GetArg("-addresstype", "")));
+ chain.initError(strprintf("Unknown address type '%s'", gArgs.GetArg("-addresstype", "")));
return nullptr;
}
if (!gArgs.GetArg("-changetype", "").empty() && !ParseOutputType(gArgs.GetArg("-changetype", ""), walletInstance->m_default_change_type)) {
- InitError(strprintf("Unknown change type '%s'", gArgs.GetArg("-changetype", "")));
+ chain.initError(strprintf("Unknown change type '%s'", gArgs.GetArg("-changetype", "")));
return nullptr;
}
if (gArgs.IsArgSet("-mintxfee")) {
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) {
- InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")));
+ chain.initError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")));
return nullptr;
}
if (n > HIGH_TX_FEE_PER_KB) {
- InitWarning(AmountHighWarn("-mintxfee") + " " +
- _("This is the minimum transaction fee you pay on every transaction."));
+ chain.initWarning(AmountHighWarn("-mintxfee") + " " +
+ _("This is the minimum transaction fee you pay on every transaction."));
}
walletInstance->m_min_fee = CFeeRate(n);
}
@@ -4271,12 +4187,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-fallbackfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) {
- InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", "")));
+ chain.initError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- InitWarning(AmountHighWarn("-fallbackfee") + " " +
- _("This is the transaction fee you may pay when fee estimates are not available."));
+ chain.initWarning(AmountHighWarn("-fallbackfee") + " " +
+ _("This is the transaction fee you may pay when fee estimates are not available."));
}
walletInstance->m_fallback_fee = CFeeRate(nFeePerK);
walletInstance->m_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value
@@ -4284,29 +4200,29 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-discardfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) {
- InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")));
+ chain.initError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- InitWarning(AmountHighWarn("-discardfee") + " " +
- _("This is the transaction fee you may discard if change is smaller than dust at this level"));
+ chain.initWarning(AmountHighWarn("-discardfee") + " " +
+ _("This is the transaction fee you may discard if change is smaller than dust at this level"));
}
walletInstance->m_discard_rate = CFeeRate(nFeePerK);
}
if (gArgs.IsArgSet("-paytxfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) {
- InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")));
+ chain.initError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
- InitWarning(AmountHighWarn("-paytxfee") + " " +
- _("This is the transaction fee you will pay if you send a transaction."));
+ chain.initWarning(AmountHighWarn("-paytxfee") + " " +
+ _("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) {
- InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
- gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
+ 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", ""), chain.relayMinFee().ToString()));
return nullptr;
}
}
@@ -4346,20 +4262,19 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
//We can't rescan beyond non-pruned blocks, stop and throw an error
//this might happen if a user uses an old wallet within a pruned node
// or if he ran -disablewallet for a longer time, then decided to re-enable
- if (fPruneMode)
- {
+ if (chain.getPruneMode()) {
int block_height = *tip_height;
while (block_height > 0 && locked_chain->haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {
--block_height;
}
if (rescan_height != block_height) {
- InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
+ chain.initError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
return nullptr;
}
}
- uiInterface.InitMessage(_("Rescanning..."));
+ chain.initMessage(_("Rescanning..."));
walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height);
// No need to read and scan block if block was created before
@@ -4374,7 +4289,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
{
WalletRescanReserver reserver(walletInstance.get());
if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(locked_chain->getBlockHash(rescan_height), {} /* stop block */, reserver, true /* update */).status)) {
- InitError(_("Failed to rescan the wallet during initialization"));
+ chain.initError(_("Failed to rescan the wallet during initialization"));
return nullptr;
}
}
@@ -4407,10 +4322,10 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
}
- uiInterface.LoadWallet(walletInstance);
+ 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->m_chain_notifications_handler = chain.handleNotifications(*walletInstance);
walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
@@ -4425,9 +4340,15 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
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)
@@ -4470,8 +4391,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);
}
@@ -4490,17 +4409,14 @@ bool CMerkleTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const
return GetBlocksToMaturity(locked_chain) > 0;
}
-bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, const CAmount& nAbsurdFee, CValidationState& state)
+bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValidationState& state)
{
- LockAnnotation lock(::cs_main); // Temporary, for AcceptToMemoryPool below. Removed in upcoming commit.
-
// We must set fInMempool here - while it will be re-set to true by the
// entered-mempool callback, if we did not there would be a race where a
// 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 = ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */,
- nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee);
+ bool ret = locked_chain.submitToMemoryPool(tx, pwallet->chain().maxTxFee(), state);
fInMempool |= ret;
return ret;
}
@@ -4531,7 +4447,7 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
CInputCoin input_coin = output.GetInputCoin();
size_t ancestors, descendants;
- mempool.GetTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
+ chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
if (!single_coin && ExtractDestination(output.tx->tx->vout[output.i].scriptPubKey, dst)) {
// Limit output groups to no more than 10 entries, to protect
// against inadvertently creating a too-large transaction
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 2a5d6caaf8..3aeeaafd1f 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -8,6 +8,7 @@
#include <amount.h>
#include <interfaces/chain.h>
+#include <interfaces/handler.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <streams.h>
@@ -100,8 +101,6 @@ class CCoinControl;
class COutput;
class CReserveKey;
class CScript;
-class CTxMemPool;
-class CBlockPolicyEstimator;
class CWalletTx;
struct FeeCalculation;
enum class FeeEstimateMode;
@@ -507,7 +506,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;
@@ -537,10 +536,10 @@ public:
int64_t GetTxTime() const;
// RelayWalletTransaction may only be called if fBroadcastTransactions!
- bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain, CConnman* connman);
+ bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain);
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
- bool AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, const CAmount& nAbsurdFee, CValidationState& state);
+ bool AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValidationState& state);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
@@ -639,7 +638,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};
@@ -650,7 +649,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;
@@ -700,11 +699,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};
@@ -810,6 +809,9 @@ public:
std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet);
+ /** Registered interfaces::Chain::Notifications handler. */
+ std::unique_ptr<interfaces::Handler> m_chain_notifications_handler;
+
/** Interface for accessing chain state. */
interfaces::Chain& chain() const { return m_chain; }
@@ -922,8 +924,8 @@ 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;
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
struct ScanResult {
@@ -943,16 +945,13 @@ 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, CConnman* connman);
+ void ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void ResendWalletTransactions(interfaces::Chain::Lock& locked_chain, int64_t nBestBlockTime) override;
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;
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);
@@ -971,7 +970,7 @@ public:
*/
bool CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
- bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
+ bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CValidationState& state);
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
{
@@ -1065,11 +1064,9 @@ public:
const std::string& GetLabelName(const CScript& scriptPubKey) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void GetScriptForMining(std::shared_ptr<CReserveScript> &script);
-
unsigned int GetKeyPoolSize() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{
- AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool
+ AssertLockHeld(cs_wallet);
return setInternalKeyPool.size() + setExternalKeyPool.size();
}
@@ -1224,6 +1221,8 @@ public:
/** Add a KeyOriginInfo to the wallet */
bool AddKeyOrigin(const CPubKey& pubkey, const KeyOriginInfo& info);
+
+ friend struct WalletTestingSetup;
};
/** A key allocated from the key pool. */
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 2783f83fd6..5149bb0263 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -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/walletutil.cpp b/src/wallet/walletutil.cpp
index 6db4c63acb..2d8adf8ba6 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()
@@ -33,9 +34,11 @@ static bool IsBerkeleyBtree(const fs::path& path)
// 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;
- fs::ifstream file(path.string(), std::ios::binary);
+ fsbridge::ifstream file(path, std::ios::binary);
if (!file.is_open()) return false;
file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
@@ -54,8 +57,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);