aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am18
-rw-r--r--src/Makefile.bench.include2
-rw-r--r--src/Makefile.qt.include2
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/Makefile.test.include319
-rw-r--r--src/base58.cpp4
-rw-r--r--src/base58.h13
-rw-r--r--src/bench/bench.cpp5
-rw-r--r--src/bench/checkqueue.cpp9
-rw-r--r--src/bench/coin_selection.cpp4
-rw-r--r--src/bench/crypto_hash.cpp11
-rw-r--r--src/bench/wallet_balance.cpp2
-rw-r--r--src/bitcoin-cli.cpp219
-rw-r--r--src/bitcoind.cpp3
-rw-r--r--src/chain.h6
-rw-r--r--src/chainparams.h5
-rw-r--r--src/core_write.cpp5
-rw-r--r--src/crypto/sha3.cpp161
-rw-r--r--src/crypto/sha3.h41
-rw-r--r--src/crypto/siphash.cpp2
-rw-r--r--src/crypto/siphash.h2
-rw-r--r--src/dummywallet.cpp35
-rw-r--r--src/init.cpp84
-rw-r--r--src/init.h4
-rw-r--r--src/interfaces/chain.cpp28
-rw-r--r--src/interfaces/chain.h13
-rw-r--r--src/interfaces/node.cpp48
-rw-r--r--src/interfaces/node.h25
-rw-r--r--src/interfaces/wallet.cpp61
-rw-r--r--src/interfaces/wallet.h34
-rw-r--r--src/key.cpp4
-rw-r--r--src/miner.h2
-rw-r--r--src/net.cpp112
-rw-r--r--src/net.h91
-rw-r--r--src/net_processing.cpp525
-rw-r--r--src/net_processing.h74
-rw-r--r--src/netaddress.cpp11
-rw-r--r--src/node/context.cpp1
-rw-r--r--src/node/context.h11
-rw-r--r--src/node/transaction.cpp6
-rw-r--r--src/policy/rbf.cpp6
-rw-r--r--src/policy/rbf.h22
-rw-r--r--src/protocol.cpp6
-rw-r--r--src/protocol.h24
-rw-r--r--src/pubkey.cpp2
-rw-r--r--src/qt/bitcoin.cpp4
-rw-r--r--src/qt/bitcoingui.cpp7
-rw-r--r--src/qt/bitcoingui.h1
-rw-r--r--src/qt/clientmodel.cpp4
-rw-r--r--src/qt/forms/debugwindow.ui2
-rw-r--r--src/qt/forms/receivecoinsdialog.ui6
-rw-r--r--src/qt/receivecoinsdialog.cpp16
-rw-r--r--src/qt/receivecoinsdialog.h3
-rw-r--r--src/qt/rpcconsole.cpp4
-rw-r--r--src/qt/splashscreen.cpp16
-rw-r--r--src/qt/splashscreen.h5
-rw-r--r--src/qt/test/addressbooktests.cpp4
-rw-r--r--src/qt/test/wallettests.cpp4
-rw-r--r--src/qt/walletcontroller.cpp17
-rw-r--r--src/qt/walletframe.cpp25
-rw-r--r--src/qt/walletmodel.cpp4
-rw-r--r--src/rest.cpp4
-rw-r--r--src/rpc/blockchain.cpp6
-rw-r--r--src/rpc/client.cpp3
-rw-r--r--src/rpc/mining.cpp141
-rw-r--r--src/rpc/net.cpp12
-rw-r--r--src/rpc/rawtransaction.cpp22
-rw-r--r--src/rpc/rawtransaction_util.cpp11
-rw-r--r--src/rpc/server.cpp6
-rw-r--r--src/rpc/server.h4
-rw-r--r--src/secp256k1/.gitignore4
-rw-r--r--src/secp256k1/.travis.yml26
-rw-r--r--src/secp256k1/Makefile.am12
-rw-r--r--src/secp256k1/TODO3
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m45
-rw-r--r--src/secp256k1/configure.ac135
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.c1
-rwxr-xr-xsrc/secp256k1/contrib/travis.sh10
-rw-r--r--src/secp256k1/include/secp256k1.h2
-rw-r--r--src/secp256k1/include/secp256k1_extrakeys.h236
-rw-r--r--src/secp256k1/include/secp256k1_schnorrsig.h111
-rw-r--r--src/secp256k1/src/assumptions.h74
-rw-r--r--src/secp256k1/src/basic-config.h9
-rw-r--r--src/secp256k1/src/bench_internal.c174
-rw-r--r--src/secp256k1/src/bench_schnorrsig.c102
-rw-r--r--src/secp256k1/src/ecmult_const_impl.h16
-rw-r--r--src/secp256k1/src/field.h12
-rw-r--r--src/secp256k1/src/field_5x52.h6
-rw-r--r--src/secp256k1/src/field_impl.h8
-rw-r--r--src/secp256k1/src/gen_context.c1
-rw-r--r--src/secp256k1/src/group.h4
-rw-r--r--src/secp256k1/src/group_impl.h13
-rw-r--r--src/secp256k1/src/hash_impl.h18
-rw-r--r--src/secp256k1/src/modules/extrakeys/Makefile.am.include3
-rw-r--r--src/secp256k1/src/modules/extrakeys/main_impl.h248
-rw-r--r--src/secp256k1/src/modules/extrakeys/tests_impl.h524
-rw-r--r--src/secp256k1/src/modules/schnorrsig/Makefile.am.include8
-rw-r--r--src/secp256k1/src/modules/schnorrsig/main_impl.h238
-rw-r--r--src/secp256k1/src/modules/schnorrsig/tests_impl.h806
-rw-r--r--src/secp256k1/src/scalar.h7
-rw-r--r--src/secp256k1/src/scalar_4x64_impl.h20
-rw-r--r--src/secp256k1/src/scalar_8x32_impl.h20
-rw-r--r--src/secp256k1/src/scalar_impl.h6
-rw-r--r--src/secp256k1/src/scratch_impl.h15
-rw-r--r--src/secp256k1/src/secp256k1.c78
-rw-r--r--src/secp256k1/src/selftest.h32
-rw-r--r--src/secp256k1/src/testrand.h3
-rw-r--r--src/secp256k1/src/testrand_impl.h4
-rw-r--r--src/secp256k1/src/tests.c113
-rw-r--r--src/secp256k1/src/tests_exhaustive.c7
-rw-r--r--src/secp256k1/src/util.h58
-rw-r--r--src/secp256k1/src/valgrind_ctime_test.c40
-rw-r--r--src/sync.cpp5
-rw-r--r--src/sync.h10
-rw-r--r--src/test/crypto_tests.cpp107
-rw-r--r--src/test/denialofservice_tests.cpp54
-rw-r--r--src/test/fuzz/crypto.cpp18
-rw-r--r--src/test/fuzz/net.cpp156
-rw-r--r--src/test/fuzz/process_message.cpp9
-rw-r--r--src/test/fuzz/process_messages.cpp4
-rw-r--r--src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp38
-rw-r--r--src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp33
-rw-r--r--src/test/net_tests.cpp14
-rw-r--r--src/test/util/setup_common.cpp15
-rw-r--r--src/test/validation_chainstate_tests.cpp3
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp11
-rw-r--r--src/test/validation_flush_tests.cpp3
-rw-r--r--src/threadsafety.h6
-rw-r--r--src/txmempool.cpp191
-rw-r--r--src/txmempool.h80
-rw-r--r--src/util/system.cpp2
-rw-r--r--src/validation.cpp89
-rw-r--r--src/validation.h26
-rw-r--r--src/wallet/bdb.cpp45
-rw-r--r--src/wallet/bdb.h16
-rw-r--r--src/wallet/db.cpp8
-rw-r--r--src/wallet/db.h40
-rw-r--r--src/wallet/init.cpp36
-rw-r--r--src/wallet/load.cpp70
-rw-r--r--src/wallet/load.h10
-rw-r--r--src/wallet/rpcdump.cpp110
-rw-r--r--src/wallet/rpcwallet.cpp325
-rw-r--r--src/wallet/salvage.cpp18
-rw-r--r--src/wallet/scriptpubkeyman.h2
-rw-r--r--src/wallet/test/coinselector_tests.cpp4
-rw-r--r--src/wallet/test/init_test_fixture.cpp2
-rw-r--r--src/wallet/test/init_test_fixture.h3
-rw-r--r--src/wallet/test/init_tests.cpp14
-rw-r--r--src/wallet/test/ismine_tests.cpp40
-rw-r--r--src/wallet/test/scriptpubkeyman_tests.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp4
-rw-r--r--src/wallet/test/wallet_test_fixture.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp43
-rw-r--r--src/wallet/wallet.cpp311
-rw-r--r--src/wallet/wallet.h66
-rw-r--r--src/wallet/walletdb.cpp70
-rw-r--r--src/wallet/walletdb.h12
-rw-r--r--src/wallet/wallettool.cpp43
-rw-r--r--src/wallet/wallettool.h2
-rw-r--r--src/wallet/walletutil.cpp18
-rw-r--r--src/wallet/walletutil.h20
-rw-r--r--src/zmq/zmqabstractnotifier.cpp2
-rw-r--r--src/zmq/zmqabstractnotifier.h12
-rw-r--r--src/zmq/zmqconfig.h22
-rw-r--r--src/zmq/zmqnotificationinterface.cpp114
-rw-r--r--src/zmq/zmqnotificationinterface.h3
-rw-r--r--src/zmq/zmqpublishnotifier.cpp34
-rw-r--r--src/zmq/zmqpublishnotifier.h2
-rw-r--r--src/zmq/zmqrpc.cpp10
-rw-r--r--src/zmq/zmqutil.cpp14
-rw-r--r--src/zmq/zmqutil.h10
171 files changed, 5969 insertions, 2071 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 175501d4a6..b23bf062c5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,10 +4,11 @@
DIST_SUBDIRS = secp256k1 univalue
-AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS)
+AM_LDFLAGS = $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS)
AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(WARN_CXXFLAGS) $(NOWARN_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) $(SANITIZER_CXXFLAGS)
AM_CPPFLAGS = $(DEBUG_CPPFLAGS) $(HARDENED_CPPFLAGS)
AM_LIBTOOLFLAGS = --preserve-dup-deps
+PTHREAD_FLAGS = $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
EXTRA_LIBRARIES =
if EMBEDDED_UNIVALUE
@@ -262,10 +263,10 @@ BITCOIN_CORE_H = \
walletinitinterface.h \
warnings.h \
zmq/zmqabstractnotifier.h \
- zmq/zmqconfig.h\
zmq/zmqnotificationinterface.h \
zmq/zmqpublishnotifier.h \
- zmq/zmqrpc.h
+ zmq/zmqrpc.h \
+ zmq/zmqutil.h
obj/build.h: FORCE
@@ -344,7 +345,8 @@ libbitcoin_zmq_a_SOURCES = \
zmq/zmqabstractnotifier.cpp \
zmq/zmqnotificationinterface.cpp \
zmq/zmqpublishnotifier.cpp \
- zmq/zmqrpc.cpp
+ zmq/zmqrpc.cpp \
+ zmq/zmqutil.cpp
endif
@@ -403,6 +405,8 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/sha1.h \
crypto/sha256.cpp \
crypto/sha256.h \
+ crypto/sha3.cpp \
+ crypto/sha3.h \
crypto/sha512.cpp \
crypto/sha512.h \
crypto/siphash.cpp \
@@ -564,7 +568,7 @@ nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
bitcoin_daemon_sources = bitcoind.cpp
bitcoin_bin_cppflags = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
bitcoin_bin_cxxflags = $(AM_CXXFLAGS) $(PIE_FLAGS)
-bitcoin_bin_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_bin_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
if TARGET_WINDOWS
bitcoin_daemon_sources += bitcoind-res.rc
@@ -601,7 +605,7 @@ bitcoin_node_LDADD = $(LIBBITCOIN_SERVER) $(bitcoin_bin_ldadd)
bitcoin_cli_SOURCES = bitcoin-cli.cpp
bitcoin_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS)
bitcoin_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-bitcoin_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
if TARGET_WINDOWS
bitcoin_cli_SOURCES += bitcoin-cli-res.rc
@@ -620,7 +624,7 @@ bitcoin_cli_LDADD += $(BOOST_LIBS) $(EVENT_LIBS)
bitcoin_tx_SOURCES = bitcoin-tx.cpp
bitcoin_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
bitcoin_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
if TARGET_WINDOWS
bitcoin_tx_SOURCES += bitcoin-tx-res.rc
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index c224ca7bf6..bd9143a381 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -75,7 +75,7 @@ bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
endif
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS)
-bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 848053e841..69ff0f0251 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -322,7 +322,7 @@ endif
bitcoin_qt_ldadd += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
-bitcoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+bitcoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX
qt_bitcoin_qt_CPPFLAGS = $(bitcoin_qt_cppflags)
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 8c47fabad9..d300398b25 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -57,7 +57,7 @@ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBIT
$(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
$(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
-qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
CLEAN_BITCOIN_QT_TEST = $(TEST_QT_MOC_CPP) qt/test/*.gcda qt/test/*.gcno
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 0068c94070..77e50cf22c 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -70,6 +70,7 @@ FUZZ_TARGETS = \
test/fuzz/message \
test/fuzz/messageheader_deserialize \
test/fuzz/multiplication_overflow \
+ test/fuzz/net \
test/fuzz/net_permissions \
test/fuzz/netaddr_deserialize \
test/fuzz/netaddress \
@@ -132,6 +133,8 @@ FUZZ_TARGETS = \
test/fuzz/script_sigcache \
test/fuzz/script_sign \
test/fuzz/scriptnum_ops \
+ test/fuzz/secp256k1_ec_seckey_import_export_der \
+ test/fuzz/secp256k1_ecdsa_signature_parse_der_lax \
test/fuzz/service_deserialize \
test/fuzz/signature_checker \
test/fuzz/snapshotmetadata_deserialize \
@@ -312,7 +315,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS)
-test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
+test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) -static
if ENABLE_ZMQ
test_test_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
@@ -320,886 +323,906 @@ endif
if ENABLE_FUZZ
+FUZZ_SUITE_LDFLAGS_COMMON = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
+
test_fuzz_addition_overflow_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_addition_overflow_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_addition_overflow_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_addition_overflow_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_addition_overflow_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_addition_overflow_SOURCES = test/fuzz/addition_overflow.cpp
test_fuzz_addr_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDR_INFO_DESERIALIZE=1
test_fuzz_addr_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_addr_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_addr_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_addr_info_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_addr_info_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_addrdb_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_addrdb_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_addrdb_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_addrdb_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_addrdb_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_addrdb_SOURCES = test/fuzz/addrdb.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_address_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_address_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_address_deserialize_SOURCES = 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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_addrman_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_addrman_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_addrman_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_asmap_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_asmap_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_asmap_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_asmap_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_asmap_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_asmap_SOURCES = test/fuzz/asmap.cpp
test_fuzz_asmap_direct_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_asmap_direct_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_asmap_direct_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_asmap_direct_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_asmap_direct_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_asmap_direct_SOURCES = test/fuzz/asmap_direct.cpp
test_fuzz_autofile_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_autofile_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_autofile_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_autofile_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_autofile_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_autofile_SOURCES = test/fuzz/autofile.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_banentry_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_banentry_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_banentry_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_banman_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_banman_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_banman_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_banman_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_banman_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_banman_SOURCES = test/fuzz/banman.cpp
test_fuzz_base_encode_decode_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_base_encode_decode_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_base_encode_decode_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_base_encode_decode_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_base_encode_decode_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_base_encode_decode_SOURCES = test/fuzz/base_encode_decode.cpp
test_fuzz_bech32_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_bech32_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_bech32_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_bech32_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_bech32_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_bech32_SOURCES = test/fuzz/bech32.cpp
test_fuzz_block_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_block_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_SOURCES = test/fuzz/block.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_block_file_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_FILE_INFO_DESERIALIZE=1
test_fuzz_block_file_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_file_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_file_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_file_info_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_file_info_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_block_filter_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_FILTER_DESERIALIZE=1
test_fuzz_block_filter_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_filter_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_filter_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_filter_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_filter_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_block_header_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_block_header_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_header_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_header_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_header_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_header_SOURCES = test/fuzz/block_header.cpp
test_fuzz_block_header_and_short_txids_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_HEADER_AND_SHORT_TXIDS_DESERIALIZE=1
test_fuzz_block_header_and_short_txids_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_block_header_and_short_txids_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_block_header_and_short_txids_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_block_header_and_short_txids_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_block_header_and_short_txids_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_blockfilter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_blockfilter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_blockfilter_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockfilter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blockfilter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blockfilter_SOURCES = test/fuzz/blockfilter.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockheader_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blockheader_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blockheader_deserialize_SOURCES = 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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blocklocator_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blocklocator_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blocklocator_deserialize_SOURCES = 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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockmerkleroot_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blockmerkleroot_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blockmerkleroot_SOURCES = 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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blocktransactions_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blocktransactions_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blocktransactions_deserialize_SOURCES = 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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blocktransactionsrequest_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blocktransactionsrequest_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blocktransactionsrequest_deserialize_SOURCES = 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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_blockundo_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_blockundo_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_blockundo_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_bloom_filter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_bloom_filter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_bloom_filter_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_bloom_filter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_bloom_filter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_bloom_filter_SOURCES = test/fuzz/bloom_filter.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_bloomfilter_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_bloomfilter_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_bloomfilter_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_buffered_file_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_buffered_file_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_buffered_file_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_buffered_file_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_buffered_file_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_buffered_file_SOURCES = test/fuzz/buffered_file.cpp
test_fuzz_chain_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_chain_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_chain_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_chain_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_chain_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_chain_SOURCES = test/fuzz/chain.cpp
test_fuzz_checkqueue_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_checkqueue_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_checkqueue_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_checkqueue_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_checkqueue_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_checkqueue_SOURCES = test/fuzz/checkqueue.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_coins_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_coins_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_coins_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_coins_view_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_coins_view_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_coins_view_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_coins_view_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_coins_view_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_coins_view_SOURCES = test/fuzz/coins_view.cpp
test_fuzz_crypto_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_SOURCES = test/fuzz/crypto.cpp
test_fuzz_crypto_aes256_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_aes256_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_aes256_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_aes256_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_aes256_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_aes256_SOURCES = test/fuzz/crypto_aes256.cpp
test_fuzz_crypto_aes256cbc_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_aes256cbc_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_aes256cbc_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_aes256cbc_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_aes256cbc_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_aes256cbc_SOURCES = test/fuzz/crypto_aes256cbc.cpp
test_fuzz_crypto_chacha20_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_chacha20_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_chacha20_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_chacha20_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_chacha20_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_chacha20_SOURCES = test/fuzz/crypto_chacha20.cpp
test_fuzz_crypto_chacha20_poly1305_aead_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_chacha20_poly1305_aead_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_chacha20_poly1305_aead_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_chacha20_poly1305_aead_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_chacha20_poly1305_aead_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_chacha20_poly1305_aead_SOURCES = test/fuzz/crypto_chacha20_poly1305_aead.cpp
test_fuzz_crypto_common_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_common_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_common_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_common_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_common_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_common_SOURCES = test/fuzz/crypto_common.cpp
test_fuzz_crypto_hkdf_hmac_sha256_l32_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_hkdf_hmac_sha256_l32_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_hkdf_hmac_sha256_l32_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_hkdf_hmac_sha256_l32_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_hkdf_hmac_sha256_l32_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_hkdf_hmac_sha256_l32_SOURCES = test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp
test_fuzz_crypto_poly1305_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_poly1305_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_poly1305_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_crypto_poly1305_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_poly1305_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_crypto_poly1305_SOURCES = test/fuzz/crypto_poly1305.cpp
test_fuzz_cuckoocache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_cuckoocache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_cuckoocache_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_cuckoocache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_cuckoocache_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_cuckoocache_SOURCES = test/fuzz/cuckoocache.cpp
test_fuzz_decode_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_decode_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_decode_tx_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_decode_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_decode_tx_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_decode_tx_SOURCES = test/fuzz/decode_tx.cpp
test_fuzz_descriptor_parse_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_descriptor_parse_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_descriptor_parse_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_descriptor_parse_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_descriptor_parse_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_descriptor_parse_SOURCES = test/fuzz/descriptor_parse.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_diskblockindex_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_diskblockindex_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_diskblockindex_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_eval_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_eval_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_eval_script_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_eval_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_eval_script_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_eval_script_SOURCES = test/fuzz/eval_script.cpp
test_fuzz_fee_rate_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_fee_rate_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_fee_rate_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_fee_rate_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_fee_rate_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_fee_rate_SOURCES = test/fuzz/fee_rate.cpp
test_fuzz_fee_rate_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DFEE_RATE_DESERIALIZE=1
test_fuzz_fee_rate_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_fee_rate_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_fee_rate_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_fee_rate_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_fee_rate_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_fees_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_fees_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_fees_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_fees_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_fees_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_fees_SOURCES = test/fuzz/fees.cpp
test_fuzz_flat_file_pos_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DFLAT_FILE_POS_DESERIALIZE=1
test_fuzz_flat_file_pos_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_flat_file_pos_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_flat_file_pos_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_flat_file_pos_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_flat_file_pos_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_flatfile_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_flatfile_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_flatfile_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_flatfile_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_flatfile_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_flatfile_SOURCES = test/fuzz/flatfile.cpp
test_fuzz_float_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_float_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_float_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_float_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_float_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_float_SOURCES = test/fuzz/float.cpp
test_fuzz_golomb_rice_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_golomb_rice_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_golomb_rice_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_golomb_rice_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_golomb_rice_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_golomb_rice_SOURCES = test/fuzz/golomb_rice.cpp
test_fuzz_hex_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_hex_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_hex_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_hex_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_hex_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_hex_SOURCES = test/fuzz/hex.cpp
test_fuzz_http_request_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_http_request_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_http_request_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_http_request_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_http_request_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_http_request_SOURCES = test/fuzz/http_request.cpp
test_fuzz_integer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_integer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_integer_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_integer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_integer_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_integer_SOURCES = test/fuzz/integer.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_inv_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_inv_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_inv_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_key_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_key_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_key_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_key_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_key_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_key_SOURCES = test/fuzz/key.cpp
test_fuzz_key_io_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_key_io_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_key_io_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_key_io_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_key_io_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_key_io_SOURCES = test/fuzz/key_io.cpp
test_fuzz_key_origin_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DKEY_ORIGIN_INFO_DESERIALIZE=1
test_fuzz_key_origin_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_key_origin_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_key_origin_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_key_origin_info_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_key_origin_info_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_kitchen_sink_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_kitchen_sink_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_kitchen_sink_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_kitchen_sink_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_kitchen_sink_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_kitchen_sink_SOURCES = test/fuzz/kitchen_sink.cpp
test_fuzz_load_external_block_file_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_load_external_block_file_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_load_external_block_file_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_load_external_block_file_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_load_external_block_file_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_load_external_block_file_SOURCES = test/fuzz/load_external_block_file.cpp
test_fuzz_locale_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_locale_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_locale_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_locale_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_locale_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_locale_SOURCES = test/fuzz/locale.cpp
test_fuzz_merkle_block_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMERKLE_BLOCK_DESERIALIZE=1
test_fuzz_merkle_block_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_merkle_block_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_merkle_block_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_merkle_block_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_merkle_block_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_merkleblock_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_merkleblock_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_merkleblock_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_merkleblock_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_merkleblock_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_merkleblock_SOURCES = test/fuzz/merkleblock.cpp
test_fuzz_message_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_message_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_message_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_message_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_message_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_message_SOURCES = test/fuzz/message.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_messageheader_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_messageheader_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_messageheader_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_multiplication_overflow_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_multiplication_overflow_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_multiplication_overflow_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_multiplication_overflow_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_multiplication_overflow_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_multiplication_overflow_SOURCES = test/fuzz/multiplication_overflow.cpp
+test_fuzz_net_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_net_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_net_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_net_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
+test_fuzz_net_SOURCES = test/fuzz/net.cpp
+
test_fuzz_net_permissions_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_net_permissions_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_net_permissions_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_net_permissions_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_net_permissions_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_net_permissions_SOURCES = test/fuzz/net_permissions.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_netaddr_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_netaddr_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_netaddr_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_netaddress_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_netaddress_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_netaddress_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_netaddress_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_netaddress_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_netaddress_SOURCES = test/fuzz/netaddress.cpp
test_fuzz_out_point_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DOUT_POINT_DESERIALIZE=1
test_fuzz_out_point_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_out_point_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_out_point_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_out_point_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_out_point_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_p2p_transport_deserializer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_p2p_transport_deserializer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_p2p_transport_deserializer_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_p2p_transport_deserializer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_p2p_transport_deserializer_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_p2p_transport_deserializer_SOURCES = test/fuzz/p2p_transport_deserializer.cpp
test_fuzz_parse_hd_keypath_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_hd_keypath_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_hd_keypath_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_parse_hd_keypath_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_hd_keypath_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_parse_hd_keypath_SOURCES = test/fuzz/parse_hd_keypath.cpp
test_fuzz_parse_iso8601_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_iso8601_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_iso8601_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_parse_iso8601_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_iso8601_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_parse_iso8601_SOURCES = test/fuzz/parse_iso8601.cpp
test_fuzz_parse_numbers_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_numbers_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_numbers_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_parse_numbers_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_numbers_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_parse_numbers_SOURCES = test/fuzz/parse_numbers.cpp
test_fuzz_parse_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_script_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_parse_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_script_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_parse_script_SOURCES = test/fuzz/parse_script.cpp
test_fuzz_parse_univalue_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_parse_univalue_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_parse_univalue_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_parse_univalue_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_parse_univalue_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_parse_univalue_SOURCES = test/fuzz/parse_univalue.cpp
test_fuzz_prevector_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_prevector_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_prevector_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_prevector_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_prevector_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_prevector_SOURCES = test/fuzz/prevector.cpp
test_fuzz_partial_merkle_tree_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPARTIAL_MERKLE_TREE_DESERIALIZE=1
test_fuzz_partial_merkle_tree_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_partial_merkle_tree_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_partial_merkle_tree_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_partial_merkle_tree_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_partial_merkle_tree_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_partially_signed_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPARTIALLY_SIGNED_TRANSACTION_DESERIALIZE=1
test_fuzz_partially_signed_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_partially_signed_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_partially_signed_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_partially_signed_transaction_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_partially_signed_transaction_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_policy_estimator_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_policy_estimator_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_policy_estimator_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_policy_estimator_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_policy_estimator_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_policy_estimator_SOURCES = test/fuzz/policy_estimator.cpp
test_fuzz_policy_estimator_io_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_policy_estimator_io_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_policy_estimator_io_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_policy_estimator_io_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_policy_estimator_io_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_policy_estimator_io_SOURCES = test/fuzz/policy_estimator_io.cpp
test_fuzz_pow_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_pow_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_pow_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_pow_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_pow_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_pow_SOURCES = test/fuzz/pow.cpp
test_fuzz_prefilled_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPREFILLED_TRANSACTION_DESERIALIZE=1
test_fuzz_prefilled_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_prefilled_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_prefilled_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_prefilled_transaction_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_prefilled_transaction_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_primitives_transaction_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_primitives_transaction_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_primitives_transaction_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_primitives_transaction_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_primitives_transaction_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_primitives_transaction_SOURCES = test/fuzz/primitives_transaction.cpp
test_fuzz_process_messages_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_process_messages_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_messages_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_messages_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_messages_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_messages_SOURCES = test/fuzz/process_messages.cpp
test_fuzz_process_message_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_process_message_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_addr_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=addr
test_fuzz_process_message_addr_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_addr_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_addr_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_addr_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_addr_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_block_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=block
test_fuzz_process_message_block_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_block_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_block_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_block_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_block_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_blocktxn_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=blocktxn
test_fuzz_process_message_blocktxn_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_blocktxn_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_blocktxn_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_blocktxn_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_blocktxn_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_cmpctblock_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=cmpctblock
test_fuzz_process_message_cmpctblock_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_cmpctblock_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_cmpctblock_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_cmpctblock_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_cmpctblock_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_feefilter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=feefilter
test_fuzz_process_message_feefilter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_feefilter_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_feefilter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_feefilter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_feefilter_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_filteradd_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=filteradd
test_fuzz_process_message_filteradd_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_filteradd_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_filteradd_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_filteradd_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_filteradd_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_filterclear_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=filterclear
test_fuzz_process_message_filterclear_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_filterclear_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_filterclear_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_filterclear_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_filterclear_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_filterload_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=filterload
test_fuzz_process_message_filterload_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_filterload_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_filterload_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_filterload_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_filterload_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_getaddr_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getaddr
test_fuzz_process_message_getaddr_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_getaddr_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_getaddr_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getaddr_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_getaddr_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_getblocks_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getblocks
test_fuzz_process_message_getblocks_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_getblocks_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_getblocks_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getblocks_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_getblocks_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_getblocktxn_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getblocktxn
test_fuzz_process_message_getblocktxn_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_getblocktxn_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_getblocktxn_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getblocktxn_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_getblocktxn_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_getdata_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getdata
test_fuzz_process_message_getdata_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_getdata_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_getdata_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getdata_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_getdata_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_getheaders_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=getheaders
test_fuzz_process_message_getheaders_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_getheaders_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_getheaders_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_getheaders_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_getheaders_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_headers_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=headers
test_fuzz_process_message_headers_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_headers_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_headers_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_headers_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_headers_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_inv_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=inv
test_fuzz_process_message_inv_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_inv_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_inv_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_inv_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_inv_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_mempool_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=mempool
test_fuzz_process_message_mempool_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_mempool_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_mempool_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_mempool_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_mempool_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_notfound_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=notfound
test_fuzz_process_message_notfound_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_notfound_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_notfound_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_notfound_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_notfound_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_ping_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=ping
test_fuzz_process_message_ping_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_ping_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_ping_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_ping_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_ping_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_pong_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=pong
test_fuzz_process_message_pong_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_pong_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_pong_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_pong_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_pong_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_sendcmpct_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=sendcmpct
test_fuzz_process_message_sendcmpct_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_sendcmpct_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_sendcmpct_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_sendcmpct_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_sendcmpct_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_sendheaders_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=sendheaders
test_fuzz_process_message_sendheaders_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_sendheaders_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_sendheaders_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_sendheaders_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_sendheaders_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=tx
test_fuzz_process_message_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_tx_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_tx_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_tx_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_verack_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=verack
test_fuzz_process_message_verack_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_verack_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_verack_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_verack_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_verack_SOURCES = test/fuzz/process_message.cpp
test_fuzz_process_message_version_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMESSAGE_TYPE=version
test_fuzz_process_message_version_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_version_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_process_message_version_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_message_version_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_process_message_version_SOURCES = test/fuzz/process_message.cpp
test_fuzz_protocol_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_protocol_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_protocol_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_protocol_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_protocol_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_protocol_SOURCES = test/fuzz/protocol.cpp
test_fuzz_psbt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_psbt_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_psbt_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_psbt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_psbt_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_psbt_SOURCES = test/fuzz/psbt.cpp
test_fuzz_psbt_input_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPSBT_INPUT_DESERIALIZE=1
test_fuzz_psbt_input_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_psbt_input_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_psbt_input_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_psbt_input_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_psbt_input_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_psbt_output_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPSBT_OUTPUT_DESERIALIZE=1
test_fuzz_psbt_output_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_psbt_output_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_psbt_output_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_psbt_output_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_psbt_output_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_pub_key_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPUB_KEY_DESERIALIZE=1
test_fuzz_pub_key_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_pub_key_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_pub_key_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_pub_key_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_pub_key_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_random_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_random_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_random_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_random_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_random_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_random_SOURCES = test/fuzz/random.cpp
test_fuzz_rbf_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_rbf_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_rbf_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_rbf_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_rbf_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_rbf_SOURCES = test/fuzz/rbf.cpp
test_fuzz_rolling_bloom_filter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_rolling_bloom_filter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_rolling_bloom_filter_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_rolling_bloom_filter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_rolling_bloom_filter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_rolling_bloom_filter_SOURCES = test/fuzz/rolling_bloom_filter.cpp
test_fuzz_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_SOURCES = test/fuzz/script.cpp
test_fuzz_script_bitcoin_consensus_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_bitcoin_consensus_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_bitcoin_consensus_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_bitcoin_consensus_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_bitcoin_consensus_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_bitcoin_consensus_SOURCES = test/fuzz/script_bitcoin_consensus.cpp
test_fuzz_script_descriptor_cache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_descriptor_cache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_descriptor_cache_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_descriptor_cache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_descriptor_cache_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_descriptor_cache_SOURCES = test/fuzz/script_descriptor_cache.cpp
test_fuzz_script_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSCRIPT_DESERIALIZE=1
test_fuzz_script_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_script_flags_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_flags_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_flags_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_flags_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_flags_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_flags_SOURCES = test/fuzz/script_flags.cpp
test_fuzz_script_interpreter_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_interpreter_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_interpreter_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_interpreter_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_interpreter_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_interpreter_SOURCES = test/fuzz/script_interpreter.cpp
test_fuzz_script_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_ops_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_ops_SOURCES = test/fuzz/script_ops.cpp
test_fuzz_script_sigcache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_sigcache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_sigcache_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_sigcache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_sigcache_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_sigcache_SOURCES = test/fuzz/script_sigcache.cpp
test_fuzz_script_sign_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_script_sign_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_script_sign_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_script_sign_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_script_sign_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_script_sign_SOURCES = test/fuzz/script_sign.cpp
test_fuzz_scriptnum_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_scriptnum_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_scriptnum_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_scriptnum_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_scriptnum_ops_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_scriptnum_ops_SOURCES = test/fuzz/scriptnum_ops.cpp
+test_fuzz_secp256k1_ec_seckey_import_export_der_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_secp256k1_ec_seckey_import_export_der_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_secp256k1_ec_seckey_import_export_der_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_secp256k1_ec_seckey_import_export_der_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
+test_fuzz_secp256k1_ec_seckey_import_export_der_SOURCES = test/fuzz/secp256k1_ec_seckey_import_export_der.cpp
+
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
+test_fuzz_secp256k1_ecdsa_signature_parse_der_lax_SOURCES = test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_service_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_service_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_service_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_signature_checker_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_signature_checker_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_signature_checker_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_signature_checker_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_signature_checker_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_signature_checker_SOURCES = test/fuzz/signature_checker.cpp
test_fuzz_snapshotmetadata_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSNAPSHOTMETADATA_DESERIALIZE=1
test_fuzz_snapshotmetadata_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_snapshotmetadata_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_snapshotmetadata_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_snapshotmetadata_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_snapshotmetadata_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_span_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_span_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_span_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_span_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_span_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_span_SOURCES = test/fuzz/span.cpp
test_fuzz_spanparsing_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_spanparsing_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_spanparsing_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_spanparsing_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_spanparsing_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_spanparsing_SOURCES = test/fuzz/spanparsing.cpp
test_fuzz_string_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_string_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_string_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_string_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_string_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_string_SOURCES = test/fuzz/string.cpp
test_fuzz_strprintf_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_strprintf_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_strprintf_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_strprintf_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_strprintf_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_strprintf_SOURCES = test/fuzz/strprintf.cpp
test_fuzz_sub_net_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSUB_NET_DESERIALIZE=1
test_fuzz_sub_net_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_sub_net_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_sub_net_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_sub_net_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_sub_net_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_system_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_system_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_system_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_system_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_system_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_system_SOURCES = test/fuzz/system.cpp
test_fuzz_timedata_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_timedata_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_timedata_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_timedata_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_timedata_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_timedata_SOURCES = test/fuzz/timedata.cpp
test_fuzz_transaction_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_transaction_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_transaction_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_transaction_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_transaction_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_transaction_SOURCES = test/fuzz/transaction.cpp
test_fuzz_tx_in_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_tx_in_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_tx_in_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_tx_in_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_tx_in_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_tx_in_SOURCES = test/fuzz/tx_in.cpp
test_fuzz_tx_in_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTX_IN_DESERIALIZE=1
test_fuzz_tx_in_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_tx_in_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_tx_in_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_tx_in_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_tx_in_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_tx_out_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_tx_out_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_tx_out_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_tx_out_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_tx_out_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_tx_out_SOURCES = test/fuzz/tx_out.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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_txoutcompressor_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_txoutcompressor_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_txoutcompressor_deserialize_SOURCES = 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_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_txundo_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_txundo_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_txundo_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_uint160_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DUINT160_DESERIALIZE=1
test_fuzz_uint160_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_uint160_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_uint160_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_uint160_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_uint160_deserialize_SOURCES = test/fuzz/deserialize.cpp
test_fuzz_uint256_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DUINT256_DESERIALIZE=1
test_fuzz_uint256_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_uint256_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
-test_fuzz_uint256_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_uint256_deserialize_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_uint256_deserialize_SOURCES = test/fuzz/deserialize.cpp
endif # ENABLE_FUZZ
diff --git a/src/base58.cpp b/src/base58.cpp
index 18cd2090e0..0dc6044145 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -35,7 +35,7 @@ static const int8_t mapBase58[256] = {
-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,
};
-bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len)
+NODISCARD static bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len)
{
// Skip leading spaces.
while (*psz && IsSpace(*psz))
@@ -141,7 +141,7 @@ std::string EncodeBase58Check(Span<const unsigned char> input)
return EncodeBase58(vch);
}
-bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len)
+NODISCARD static bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len)
{
if (!DecodeBase58(psz, vchRet, max_ret_len > std::numeric_limits<int>::max() - 4 ? std::numeric_limits<int>::max() : max_ret_len + 4) ||
(vchRet.size() < 4)) {
diff --git a/src/base58.h b/src/base58.h
index b87664b78b..468c3e2589 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -26,13 +26,6 @@
std::string EncodeBase58(Span<const unsigned char> input);
/**
- * Decode a base58-encoded string (psz) into a byte vector (vchRet).
- * return true if decoding is successful.
- * psz cannot be nullptr.
- */
-NODISCARD bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len);
-
-/**
* Decode a base58-encoded string (str) into a byte vector (vchRet).
* return true if decoding is successful.
*/
@@ -44,12 +37,6 @@ NODISCARD bool DecodeBase58(const std::string& str, std::vector<unsigned char>&
std::string EncodeBase58Check(Span<const unsigned char> input);
/**
- * Decode a base58-encoded string (psz) that includes a checksum into a byte
- * vector (vchRet), return true if decoding is successful
- */
-NODISCARD bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len);
-
-/**
* Decode a base58-encoded string (str) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index 01466d0b6f..012057e792 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -70,7 +70,10 @@ void benchmark::BenchRunner::RunAll(const Args& args)
}
std::cout << bench.complexityBigO() << std::endl;
}
- benchmarkResults.push_back(bench.results().back());
+
+ if (!bench.results().empty()) {
+ benchmarkResults.push_back(bench.results().back());
+ }
}
GenerateTemplateResults(benchmarkResults, args.output_csv, "# Benchmark, evals, iterations, total, min, max, median\n"
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index 19d7bc0dbc..ffa772d8c1 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -14,8 +14,6 @@
#include <vector>
-
-static const int MIN_CORES = 2;
static const size_t BATCHES = 101;
static const size_t BATCH_SIZE = 30;
static const int PREVECTOR_SIZE = 28;
@@ -26,6 +24,9 @@ static const unsigned int QUEUE_BATCH_SIZE = 128;
// and there is a little bit of work done between calls to Add.
static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
{
+ // We shouldn't ever be running with the checkqueue on a single core machine.
+ if (GetNumCores() <= 1) return;
+
const ECCVerifyHandle verify_handle;
ECC_Start();
@@ -44,7 +45,9 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
};
CCheckQueue<PrevectorJob> queue {QUEUE_BATCH_SIZE};
boost::thread_group tg;
- for (auto x = 0; x < std::max(MIN_CORES, GetNumCores()); ++x) {
+ // The main thread should be counted to prevent thread oversubscription, and
+ // to decrease the variance of benchmark results.
+ for (auto x = 0; x < GetNumCores() - 1; ++x) {
tg.create_thread([&]{queue.Thread();});
}
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 3a71a6ca03..99aafd8dfc 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -31,7 +31,7 @@ static void CoinSelection(benchmark::Bench& bench)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
wallet.SetupLegacyScriptPubKeyMan();
std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
@@ -65,7 +65,7 @@ static void CoinSelection(benchmark::Bench& bench)
typedef std::set<CInputCoin> CoinSet;
static NodeContext testNode;
static auto testChain = interfaces::MakeChain(testNode);
-static CWallet testWallet(testChain.get(), WalletLocation(), CreateDummyWalletDatabase());
+static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 36be86bcc8..65d16d47d8 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -7,6 +7,7 @@
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
+#include <crypto/sha3.h>
#include <crypto/sha512.h>
#include <crypto/siphash.h>
#include <hash.h>
@@ -43,6 +44,15 @@ static void SHA256(benchmark::Bench& bench)
});
}
+static void SHA3_256_1M(benchmark::Bench& bench)
+{
+ uint8_t hash[SHA3_256::OUTPUT_SIZE];
+ std::vector<uint8_t> in(BUFFER_SIZE,0);
+ bench.batch(in.size()).unit("byte").run([&] {
+ SHA3_256().Write(in).Finalize(hash);
+ });
+}
+
static void SHA256_32b(benchmark::Bench& bench)
{
std::vector<uint8_t> in(32,0);
@@ -99,6 +109,7 @@ BENCHMARK(RIPEMD160);
BENCHMARK(SHA1);
BENCHMARK(SHA256);
BENCHMARK(SHA512);
+BENCHMARK(SHA3_256_1M);
BENCHMARK(SHA256_32b);
BENCHMARK(SipHash_32b);
diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp
index e16182b48e..b3b73284d8 100644
--- a/src/bench/wallet_balance.cpp
+++ b/src/bench/wallet_balance.cpp
@@ -26,7 +26,7 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b
NodeContext node;
std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
- CWallet wallet{chain.get(), WalletLocation(), CreateMockWalletDatabase()};
+ CWallet wallet{chain.get(), "", CreateMockWalletDatabase()};
{
wallet.SetupLegacyScriptPubKeyMan();
bool first_run;
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index cf52b710cb..198cd5eb52 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -39,6 +39,8 @@ static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
static const bool DEFAULT_NAMED=false;
static const int CONTINUE_EXECUTION=-1;
+static const std::string ONION{".onion"};
+static const size_t ONION_LEN{ONION.size()};
/** Default number of blocks to generate for RPC generatetoaddress. */
static const std::string DEFAULT_NBLOCKS = "1";
@@ -56,6 +58,8 @@ static void SetupCliArgs(ArgsManager& argsman)
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC generatenewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument from 0 to 4 can be passed for different peers listings (default: 0).", ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
+
SetupChainParamsBaseOptions(argsman);
argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -271,7 +275,13 @@ public:
result.pushKV("headers", batch[ID_BLOCKCHAININFO]["result"]["headers"]);
result.pushKV("verificationprogress", batch[ID_BLOCKCHAININFO]["result"]["verificationprogress"]);
result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]);
- result.pushKV("connections", batch[ID_NETWORKINFO]["result"]["connections"]);
+
+ UniValue connections(UniValue::VOBJ);
+ connections.pushKV("in", batch[ID_NETWORKINFO]["result"]["connections_in"]);
+ connections.pushKV("out", batch[ID_NETWORKINFO]["result"]["connections_out"]);
+ connections.pushKV("total", batch[ID_NETWORKINFO]["result"]["connections"]);
+ result.pushKV("connections", connections);
+
result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]);
result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
@@ -291,6 +301,211 @@ public:
}
};
+/** Process netinfo requests */
+class NetinfoRequestHandler : public BaseRequestHandler
+{
+private:
+ bool IsAddrIPv6(const std::string& addr) const
+ {
+ return !addr.empty() && addr.front() == '[';
+ }
+ bool IsInboundOnion(const std::string& addr_local, int mapped_as) const
+ {
+ return mapped_as == 0 && addr_local.find(ONION) != std::string::npos;
+ }
+ bool IsOutboundOnion(const std::string& addr, int mapped_as) const
+ {
+ const size_t addr_len{addr.size()};
+ const size_t onion_pos{addr.rfind(ONION)};
+ return mapped_as == 0 && onion_pos != std::string::npos && addr_len > ONION_LEN &&
+ (onion_pos == addr_len - ONION_LEN || onion_pos == addr.find_last_of(":") - ONION_LEN);
+ }
+ uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level
+ bool DetailsRequested() const { return m_details_level > 0 && m_details_level < 5; }
+ bool IsAddressSelected() const { return m_details_level == 2 || m_details_level == 4; }
+ bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; }
+ enum struct NetType {
+ ipv4,
+ ipv6,
+ onion,
+ };
+ struct Peer {
+ int id;
+ int mapped_as;
+ int version;
+ int64_t conn_time;
+ int64_t last_blck;
+ int64_t last_recv;
+ int64_t last_send;
+ int64_t last_trxn;
+ double min_ping;
+ double ping;
+ std::string addr;
+ std::string sub_version;
+ NetType net_type;
+ bool is_block_relay;
+ bool is_outbound;
+ bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
+ };
+ std::string NetTypeEnumToString(NetType t)
+ {
+ switch (t) {
+ case NetType::ipv4: return "ipv4";
+ case NetType::ipv6: return "ipv6";
+ case NetType::onion: return "onion";
+ } // no default case, so the compiler can warn about missing cases
+ assert(false);
+ }
+ std::string ChainToString() const
+ {
+ if (gArgs.GetChainName() == CBaseChainParams::TESTNET) return " testnet";
+ if (gArgs.GetChainName() == CBaseChainParams::REGTEST) return " regtest";
+ return "";
+ }
+public:
+ const int ID_PEERINFO = 0;
+ const int ID_NETWORKINFO = 1;
+
+ UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
+ {
+ if (!args.empty()) {
+ uint8_t n{0};
+ if (ParseUInt8(args.at(0), &n)) {
+ m_details_level = n;
+ }
+ }
+ UniValue result(UniValue::VARR);
+ result.push_back(JSONRPCRequestObj("getpeerinfo", NullUniValue, ID_PEERINFO));
+ result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
+ return result;
+ }
+
+ UniValue ProcessReply(const UniValue& batch_in) override
+ {
+ const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)};
+ if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO];
+ if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
+
+ const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]};
+ if (networkinfo["version"].get_int() < 209900) {
+ throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up");
+ }
+
+ // Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs.
+ const int64_t time_now{GetSystemTimeInSeconds()};
+ int ipv4_i{0}, ipv6_i{0}, onion_i{0}, block_relay_i{0}, total_i{0}; // inbound conn counters
+ int ipv4_o{0}, ipv6_o{0}, onion_o{0}, block_relay_o{0}, total_o{0}; // outbound conn counters
+ size_t max_peer_id_length{2}, max_addr_length{0};
+ bool is_asmap_on{false};
+ std::vector<Peer> peers;
+ const UniValue& getpeerinfo{batch[ID_PEERINFO]["result"]};
+
+ for (const UniValue& peer : getpeerinfo.getValues()) {
+ const std::string addr{peer["addr"].get_str()};
+ const std::string addr_local{peer["addrlocal"].isNull() ? "" : peer["addrlocal"].get_str()};
+ const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].get_int()};
+ const bool is_block_relay{!peer["relaytxes"].get_bool()};
+ const bool is_inbound{peer["inbound"].get_bool()};
+ NetType net_type{NetType::ipv4};
+ if (is_inbound) {
+ if (IsAddrIPv6(addr)) {
+ net_type = NetType::ipv6;
+ ++ipv6_i;
+ } else if (IsInboundOnion(addr_local, mapped_as)) {
+ net_type = NetType::onion;
+ ++onion_i;
+ } else {
+ ++ipv4_i;
+ }
+ if (is_block_relay) ++block_relay_i;
+ } else {
+ if (IsAddrIPv6(addr)) {
+ net_type = NetType::ipv6;
+ ++ipv6_o;
+ } else if (IsOutboundOnion(addr, mapped_as)) {
+ net_type = NetType::onion;
+ ++onion_o;
+ } else {
+ ++ipv4_o;
+ }
+ if (is_block_relay) ++block_relay_o;
+ }
+ if (DetailsRequested()) {
+ // Push data for this peer to the peers vector.
+ const int peer_id{peer["id"].get_int()};
+ const int version{peer["version"].get_int()};
+ const std::string sub_version{peer["subver"].get_str()};
+ const int64_t conn_time{peer["conntime"].get_int64()};
+ const int64_t last_blck{peer["last_block"].get_int64()};
+ const int64_t last_recv{peer["lastrecv"].get_int64()};
+ const int64_t last_send{peer["lastsend"].get_int64()};
+ const int64_t last_trxn{peer["last_transaction"].get_int64()};
+ const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()};
+ const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()};
+ peers.push_back({peer_id, mapped_as, version, conn_time, last_blck, last_recv, last_send, last_trxn, min_ping, ping, addr, sub_version, net_type, is_block_relay, !is_inbound});
+ max_peer_id_length = std::max(ToString(peer_id).length(), max_peer_id_length);
+ max_addr_length = std::max(addr.length() + 1, max_addr_length);
+ is_asmap_on |= (mapped_as != 0);
+ }
+ }
+
+ // Generate report header.
+ std::string result{strprintf("%s %s%s - %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())};
+
+ // Report detailed peer connections list sorted by direction and minimum ping time.
+ if (DetailsRequested() && !peers.empty()) {
+ std::sort(peers.begin(), peers.end());
+ result += "Peer connections sorted by direction and min ping\n<-> relay net mping ping send recv txn blk uptime ";
+ if (is_asmap_on) result += " asmap ";
+ result += strprintf("%*s %-*s%s\n", max_peer_id_length, "id", IsAddressSelected() ? max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : "");
+ for (const Peer& peer : peers) {
+ std::string version{ToString(peer.version) + peer.sub_version};
+ result += strprintf(
+ "%3s %5s %5s%6s%7s%5s%5s%5s%5s%7s%*i %*s %-*s%s\n",
+ peer.is_outbound ? "out" : "in",
+ peer.is_block_relay ? "block" : "full",
+ NetTypeEnumToString(peer.net_type),
+ peer.min_ping == -1 ? "" : ToString(round(1000 * peer.min_ping)),
+ peer.ping == -1 ? "" : ToString(round(1000 * peer.ping)),
+ peer.last_send == 0 ? "" : ToString(time_now - peer.last_send),
+ peer.last_recv == 0 ? "" : ToString(time_now - peer.last_recv),
+ peer.last_trxn == 0 ? "" : ToString((time_now - peer.last_trxn) / 60),
+ peer.last_blck == 0 ? "" : ToString((time_now - peer.last_blck) / 60),
+ peer.conn_time == 0 ? "" : ToString((time_now - peer.conn_time) / 60),
+ is_asmap_on ? 7 : 0, // variable spacing
+ is_asmap_on && peer.mapped_as != 0 ? ToString(peer.mapped_as) : "",
+ max_peer_id_length, // variable spacing
+ peer.id,
+ IsAddressSelected() ? max_addr_length : 0, // variable spacing
+ IsAddressSelected() ? peer.addr : "",
+ IsVersionSelected() && version != "0" ? version : "");
+ }
+ result += " ms ms sec sec min min min\n\n";
+ }
+
+ // Report peer connection totals by type.
+ total_i = ipv4_i + ipv6_i + onion_i;
+ total_o = ipv4_o + ipv6_o + onion_o;
+ result += " ipv4 ipv6 onion total block-relay\n";
+ result += strprintf("in %5i %5i %5i %5i %5i\n", ipv4_i, ipv6_i, onion_i, total_i, block_relay_i);
+ result += strprintf("out %5i %5i %5i %5i %5i\n", ipv4_o, ipv6_o, onion_o, total_o, block_relay_o);
+ result += strprintf("total %5i %5i %5i %5i %5i\n", ipv4_i + ipv4_o, ipv6_i + ipv6_o, onion_i + onion_o, total_i + total_o, block_relay_i + block_relay_o);
+
+ // Report local addresses, ports, and scores.
+ result += "\nLocal addresses";
+ const UniValue& local_addrs{networkinfo["localaddresses"]};
+ if (local_addrs.empty()) {
+ result += ": n/a\n";
+ } else {
+ for (const UniValue& addr : local_addrs.getValues()) {
+ result += strprintf("\n%-40i port %5i score %6i", addr["address"].get_str(), addr["port"].get_int(), addr["score"].get_int());
+ }
+ }
+
+ return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1);
+ }
+};
+
/** Process RPC generatetoaddress request. */
class GenerateToAddressRequestHandler : public BaseRequestHandler
{
@@ -618,6 +833,8 @@ static int CommandLineRPC(int argc, char *argv[])
std::string method;
if (gArgs.IsArgSet("-getinfo")) {
rh.reset(new GetinfoRequestHandler());
+ } else if (gArgs.GetBoolArg("-netinfo", false)) {
+ rh.reset(new NetinfoRequestHandler());
} else if (gArgs.GetBoolArg("-generate", false)) {
const UniValue getnewaddress{GetNewAddress()};
const UniValue& error{find_value(getnewaddress, "error")};
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 227626f40f..02074f820a 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -44,7 +44,6 @@ static void WaitForShutdown(NodeContext& node)
static bool AppInit(int argc, char* argv[])
{
NodeContext node;
- node.chain = interfaces::MakeChain(node);
bool fRet = false;
@@ -144,7 +143,7 @@ static bool AppInit(int argc, char* argv[])
// If locking the data directory failed, exit immediately
return false;
}
- fRet = AppInitMain(context, node);
+ fRet = AppInitInterfaces(node) && AppInitMain(context, node);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()");
diff --git a/src/chain.h b/src/chain.h
index 802e23f775..43e8a39f36 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -398,12 +398,6 @@ public:
return vChain[nHeight];
}
- /** Compare two chains efficiently. */
- friend bool operator==(const CChain &a, const CChain &b) {
- return a.vChain.size() == b.vChain.size() &&
- a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1];
- }
-
/** Efficiently check whether a block is present in this chain. */
bool Contains(const CBlockIndex *pindex) const {
return (*this)[pindex->nHeight] == pindex;
diff --git a/src/chainparams.h b/src/chainparams.h
index 542ef329da..7aa999fb75 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -23,6 +23,11 @@ typedef std::map<int, uint256> MapCheckpoints;
struct CCheckpointData {
MapCheckpoints mapCheckpoints;
+
+ int GetHeight() const {
+ const auto& final_checkpoint = mapCheckpoints.rbegin();
+ return final_checkpoint->first /* height */;
+ }
};
/**
diff --git a/src/core_write.cpp b/src/core_write.cpp
index f9d918cb6d..3980d8cb2e 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -111,8 +111,9 @@ std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDeco
// checks in CheckSignatureEncoding.
if (CheckSignatureEncoding(vch, SCRIPT_VERIFY_STRICTENC, nullptr)) {
const unsigned char chSigHashType = vch.back();
- if (mapSigHashTypes.count(chSigHashType)) {
- strSigHashDecode = "[" + mapSigHashTypes.find(chSigHashType)->second + "]";
+ const auto it = mapSigHashTypes.find(chSigHashType);
+ if (it != mapSigHashTypes.end()) {
+ strSigHashDecode = "[" + it->second + "]";
vch.pop_back(); // remove the sighash type byte. it will be replaced by the decode.
}
}
diff --git a/src/crypto/sha3.cpp b/src/crypto/sha3.cpp
new file mode 100644
index 0000000000..9c0c42fa77
--- /dev/null
+++ b/src/crypto/sha3.cpp
@@ -0,0 +1,161 @@
+// Copyright (c) 2020 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 https://github.com/mjosaarinen/tiny_sha3/blob/master/sha3.c
+// by Markku-Juhani O. Saarinen <mjos@iki.fi>
+
+#include <crypto/sha3.h>
+#include <crypto/common.h>
+#include <span.h>
+
+#include <algorithm>
+#include <array> // For std::begin and std::end.
+
+#include <stdint.h>
+
+// Internal implementation code.
+namespace
+{
+uint64_t Rotl(uint64_t x, int n) { return (x << n) | (x >> (64 - n)); }
+} // namespace
+
+void KeccakF(uint64_t (&st)[25])
+{
+ static constexpr uint64_t RNDC[24] = {
+ 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
+ 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
+ 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
+ 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
+ 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
+ 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
+ };
+ static constexpr int ROUNDS = 24;
+
+ for (int round = 0; round < ROUNDS; ++round) {
+ uint64_t bc0, bc1, bc2, bc3, bc4, t;
+
+ // Theta
+ bc0 = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20];
+ bc1 = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21];
+ bc2 = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22];
+ bc3 = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
+ bc4 = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
+ t = bc4 ^ Rotl(bc1, 1); st[0] ^= t; st[5] ^= t; st[10] ^= t; st[15] ^= t; st[20] ^= t;
+ t = bc0 ^ Rotl(bc2, 1); st[1] ^= t; st[6] ^= t; st[11] ^= t; st[16] ^= t; st[21] ^= t;
+ t = bc1 ^ Rotl(bc3, 1); st[2] ^= t; st[7] ^= t; st[12] ^= t; st[17] ^= t; st[22] ^= t;
+ t = bc2 ^ Rotl(bc4, 1); st[3] ^= t; st[8] ^= t; st[13] ^= t; st[18] ^= t; st[23] ^= t;
+ t = bc3 ^ Rotl(bc0, 1); st[4] ^= t; st[9] ^= t; st[14] ^= t; st[19] ^= t; st[24] ^= t;
+
+ // Rho Pi
+ t = st[1];
+ bc0 = st[10]; st[10] = Rotl(t, 1); t = bc0;
+ bc0 = st[7]; st[7] = Rotl(t, 3); t = bc0;
+ bc0 = st[11]; st[11] = Rotl(t, 6); t = bc0;
+ bc0 = st[17]; st[17] = Rotl(t, 10); t = bc0;
+ bc0 = st[18]; st[18] = Rotl(t, 15); t = bc0;
+ bc0 = st[3]; st[3] = Rotl(t, 21); t = bc0;
+ bc0 = st[5]; st[5] = Rotl(t, 28); t = bc0;
+ bc0 = st[16]; st[16] = Rotl(t, 36); t = bc0;
+ bc0 = st[8]; st[8] = Rotl(t, 45); t = bc0;
+ bc0 = st[21]; st[21] = Rotl(t, 55); t = bc0;
+ bc0 = st[24]; st[24] = Rotl(t, 2); t = bc0;
+ bc0 = st[4]; st[4] = Rotl(t, 14); t = bc0;
+ bc0 = st[15]; st[15] = Rotl(t, 27); t = bc0;
+ bc0 = st[23]; st[23] = Rotl(t, 41); t = bc0;
+ bc0 = st[19]; st[19] = Rotl(t, 56); t = bc0;
+ bc0 = st[13]; st[13] = Rotl(t, 8); t = bc0;
+ bc0 = st[12]; st[12] = Rotl(t, 25); t = bc0;
+ bc0 = st[2]; st[2] = Rotl(t, 43); t = bc0;
+ bc0 = st[20]; st[20] = Rotl(t, 62); t = bc0;
+ bc0 = st[14]; st[14] = Rotl(t, 18); t = bc0;
+ bc0 = st[22]; st[22] = Rotl(t, 39); t = bc0;
+ bc0 = st[9]; st[9] = Rotl(t, 61); t = bc0;
+ bc0 = st[6]; st[6] = Rotl(t, 20); t = bc0;
+ st[1] = Rotl(t, 44);
+
+ // Chi Iota
+ bc0 = st[0]; bc1 = st[1]; bc2 = st[2]; bc3 = st[3]; bc4 = st[4];
+ st[0] = bc0 ^ (~bc1 & bc2) ^ RNDC[round];
+ st[1] = bc1 ^ (~bc2 & bc3);
+ st[2] = bc2 ^ (~bc3 & bc4);
+ st[3] = bc3 ^ (~bc4 & bc0);
+ st[4] = bc4 ^ (~bc0 & bc1);
+ bc0 = st[5]; bc1 = st[6]; bc2 = st[7]; bc3 = st[8]; bc4 = st[9];
+ st[5] = bc0 ^ (~bc1 & bc2);
+ st[6] = bc1 ^ (~bc2 & bc3);
+ st[7] = bc2 ^ (~bc3 & bc4);
+ st[8] = bc3 ^ (~bc4 & bc0);
+ st[9] = bc4 ^ (~bc0 & bc1);
+ bc0 = st[10]; bc1 = st[11]; bc2 = st[12]; bc3 = st[13]; bc4 = st[14];
+ st[10] = bc0 ^ (~bc1 & bc2);
+ st[11] = bc1 ^ (~bc2 & bc3);
+ st[12] = bc2 ^ (~bc3 & bc4);
+ st[13] = bc3 ^ (~bc4 & bc0);
+ st[14] = bc4 ^ (~bc0 & bc1);
+ bc0 = st[15]; bc1 = st[16]; bc2 = st[17]; bc3 = st[18]; bc4 = st[19];
+ st[15] = bc0 ^ (~bc1 & bc2);
+ st[16] = bc1 ^ (~bc2 & bc3);
+ st[17] = bc2 ^ (~bc3 & bc4);
+ st[18] = bc3 ^ (~bc4 & bc0);
+ st[19] = bc4 ^ (~bc0 & bc1);
+ bc0 = st[20]; bc1 = st[21]; bc2 = st[22]; bc3 = st[23]; bc4 = st[24];
+ st[20] = bc0 ^ (~bc1 & bc2);
+ st[21] = bc1 ^ (~bc2 & bc3);
+ st[22] = bc2 ^ (~bc3 & bc4);
+ st[23] = bc3 ^ (~bc4 & bc0);
+ st[24] = bc4 ^ (~bc0 & bc1);
+ }
+}
+
+SHA3_256& SHA3_256::Write(Span<const unsigned char> data)
+{
+ if (m_bufsize && m_bufsize + data.size() >= sizeof(m_buffer)) {
+ // Fill the buffer and process it.
+ std::copy(data.begin(), data.begin() + sizeof(m_buffer) - m_bufsize, m_buffer + m_bufsize);
+ data = data.subspan(sizeof(m_buffer) - m_bufsize);
+ m_state[m_pos++] ^= ReadLE64(m_buffer);
+ m_bufsize = 0;
+ if (m_pos == RATE_BUFFERS) {
+ KeccakF(m_state);
+ m_pos = 0;
+ }
+ }
+ while (data.size() >= sizeof(m_buffer)) {
+ // Process chunks directly from the buffer.
+ m_state[m_pos++] ^= ReadLE64(data.data());
+ data = data.subspan(8);
+ if (m_pos == RATE_BUFFERS) {
+ KeccakF(m_state);
+ m_pos = 0;
+ }
+ }
+ if (data.size()) {
+ // Keep the remainder in the buffer.
+ std::copy(data.begin(), data.end(), m_buffer + m_bufsize);
+ m_bufsize += data.size();
+ }
+ return *this;
+}
+
+SHA3_256& SHA3_256::Finalize(Span<unsigned char> output)
+{
+ assert(output.size() == OUTPUT_SIZE);
+ std::fill(m_buffer + m_bufsize, m_buffer + sizeof(m_buffer), 0);
+ m_buffer[m_bufsize] ^= 0x06;
+ m_state[m_pos] ^= ReadLE64(m_buffer);
+ m_state[RATE_BUFFERS - 1] ^= 0x8000000000000000;
+ KeccakF(m_state);
+ for (unsigned i = 0; i < 4; ++i) {
+ WriteLE64(output.data() + 8 * i, m_state[i]);
+ }
+ return *this;
+}
+
+SHA3_256& SHA3_256::Reset()
+{
+ m_bufsize = 0;
+ m_pos = 0;
+ std::fill(std::begin(m_state), std::end(m_state), 0);
+ return *this;
+}
diff --git a/src/crypto/sha3.h b/src/crypto/sha3.h
new file mode 100644
index 0000000000..88d8c1204d
--- /dev/null
+++ b/src/crypto/sha3.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 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_SHA3_H
+#define BITCOIN_CRYPTO_SHA3_H
+
+#include <span.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+//! The Keccak-f[1600] transform.
+void KeccakF(uint64_t (&st)[25]);
+
+class SHA3_256
+{
+private:
+ uint64_t m_state[25] = {0};
+ unsigned char m_buffer[8];
+ unsigned m_bufsize = 0;
+ unsigned m_pos = 0;
+
+ //! Sponge rate in bits.
+ static constexpr unsigned RATE_BITS = 1088;
+
+ //! Sponge rate expressed as a multiple of the buffer size.
+ static constexpr unsigned RATE_BUFFERS = RATE_BITS / (8 * sizeof(m_buffer));
+
+ static_assert(RATE_BITS % (8 * sizeof(m_buffer)) == 0, "Rate must be a multiple of 8 bytes");
+
+public:
+ static constexpr size_t OUTPUT_SIZE = 32;
+
+ SHA3_256() {}
+ SHA3_256& Write(Span<const unsigned char> data);
+ SHA3_256& Finalize(Span<unsigned char> output);
+ SHA3_256& Reset();
+};
+
+#endif // BITCOIN_CRYPTO_SHA3_H
diff --git a/src/crypto/siphash.cpp b/src/crypto/siphash.cpp
index e81957111a..2e0106b165 100644
--- a/src/crypto/siphash.cpp
+++ b/src/crypto/siphash.cpp
@@ -49,7 +49,7 @@ CSipHasher& CSipHasher::Write(const unsigned char* data, size_t size)
{
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
uint64_t t = tmp;
- int c = count;
+ uint8_t c = count;
while (size--) {
t |= ((uint64_t)(*(data++))) << (8 * (c % 8));
diff --git a/src/crypto/siphash.h b/src/crypto/siphash.h
index b312f913f9..6b38950f8e 100644
--- a/src/crypto/siphash.h
+++ b/src/crypto/siphash.h
@@ -15,7 +15,7 @@ class CSipHasher
private:
uint64_t v[4];
uint64_t tmp;
- int count;
+ uint8_t count; // Only the low 8 bits of the input size matter.
public:
/** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
index 380d4eb8ac..8d2dcd0279 100644
--- a/src/dummywallet.cpp
+++ b/src/dummywallet.cpp
@@ -4,11 +4,8 @@
#include <util/system.h>
#include <walletinitinterface.h>
-#include <support/allocators/secure.h>
class CWallet;
-enum class WalletCreationStatus;
-struct bilingual_str;
namespace interfaces {
class Chain;
@@ -49,7 +46,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
"-walletdir=<dir>",
"-walletnotify=<cmd>",
"-walletrbf",
- "-zapwallettxes=<mode>",
"-dblogsize=<n>",
"-flushwallet",
"-privdb",
@@ -59,37 +55,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
-fs::path GetWalletDir()
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-std::vector<fs::path> ListWalletDir()
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-std::vector<std::shared_ptr<CWallet>> GetWallets()
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings)
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result)
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
-using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wallet)>;
-std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet)
-{
- throw std::logic_error("Wallet function called in non-wallet build.");
-}
-
namespace interfaces {
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet)
diff --git a/src/init.cpp b/src/init.cpp
index ecd57960ad..023aa9aba5 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -187,7 +187,7 @@ void Shutdown(NodeContext& node)
/// Be sure that anything that writes files or flushes caches only does this if the respective
/// module was initialized.
util::ThreadRename("shutoff");
- mempool.AddTransactionsUpdated(1);
+ if (node.mempool) node.mempool->AddTransactionsUpdated(1);
StopHTTPRPC();
StopREST();
@@ -200,7 +200,7 @@ void Shutdown(NodeContext& node)
// Because these depend on each-other, we make sure that neither can be
// using the other before destroying them.
- if (node.peer_logic) UnregisterValidationInterface(node.peer_logic.get());
+ if (node.peerman) UnregisterValidationInterface(node.peerman.get());
// Follow the lock order requirements:
// * CheckForStaleTipAndEvictPeers locks cs_main before indirectly calling GetExtraOutboundCount
// which locks cs_vNodes.
@@ -227,12 +227,12 @@ void Shutdown(NodeContext& node)
// After the threads that potentially access these pointers have been stopped,
// destruct and reset all to nullptr.
- node.peer_logic.reset();
+ node.peerman.reset();
node.connman.reset();
node.banman.reset();
- if (::mempool.IsLoaded() && node.args->GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- DumpMempool(::mempool);
+ if (node.mempool && node.mempool->IsLoaded() && node.args->GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ DumpMempool(*node.mempool);
}
if (fFeeEstimatesInitialized)
@@ -302,7 +302,7 @@ void Shutdown(NodeContext& node)
GetMainSignals().UnregisterBackgroundSignalScheduler();
globalVerifyHandle.reset();
ECC_Stop();
- node.mempool = nullptr;
+ node.mempool.reset();
node.chainman = nullptr;
node.scheduler.reset();
@@ -507,7 +507,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: %s (0-4, default: %u)", Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkblockindex", strprintf("Do a consistency check for the block tree, chainstate, and other validation data structures occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- argsman.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block 295000 (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block %s (default: %u)", defaultChainParams->Checkpoints().GetHeight(), DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
@@ -738,10 +738,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImp
return;
}
} // End scope of CImportingNow
- if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- LoadMempool(::mempool);
- }
- ::mempool.SetIsLoaded(!ShutdownRequested());
+ chainman.ActiveChainstate().LoadMempool(args);
}
/** Sanity checks
@@ -1054,11 +1051,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
}
}
- // Checkmempool and checkblockindex default to true in regtest mode
- int ratio = std::min<int>(std::max<int>(args.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
- if (ratio != 0) {
- mempool.setSanityCheck(1.0 / ratio);
- }
fCheckBlockIndex = args.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
fCheckpointsEnabled = args.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
@@ -1229,6 +1221,17 @@ bool AppInitLockDataDirectory()
return true;
}
+bool AppInitInterfaces(NodeContext& node)
+{
+ node.chain = interfaces::MakeChain(node);
+ // Create client interfaces for wallets that are supposed to be loaded
+ // according to -wallet and -disablewallet options. This only constructs
+ // the interfaces, it doesn't load wallet data. Wallets actually get loaded
+ // when load() and start() interface methods are called below.
+ g_wallet_init_interface.Construct(node);
+ return true;
+}
+
bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
{
const ArgsManager& args = *Assert(node.args);
@@ -1318,12 +1321,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
- // Create client interfaces for wallets that are supposed to be loaded
- // according to -wallet and -disablewallet options. This only constructs
- // the interfaces, it doesn't load wallet data. Wallets actually get loaded
- // when load() and start() interface methods are called below.
- g_wallet_init_interface.Construct(node);
-
/* Register RPC commands regardless of -server setting so they will be
* available in the GUI RPC console even if external calls are disabled.
*/
@@ -1363,16 +1360,24 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
node.connman = MakeUnique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), args.GetBoolArg("-networkactive", true));
+
// Make mempool generally available in the node context. For example the connection manager, wallet, or RPC threads,
// which are all started after this, may use it from the node context.
assert(!node.mempool);
- node.mempool = &::mempool;
+ node.mempool = MakeUnique<CTxMemPool>(&::feeEstimator);
+ if (node.mempool) {
+ int ratio = std::min<int>(std::max<int>(args.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
+ if (ratio != 0) {
+ node.mempool->setSanityCheck(1.0 / ratio);
+ }
+ }
+
assert(!node.chainman);
node.chainman = &g_chainman;
ChainstateManager& chainman = *Assert(node.chainman);
- node.peer_logic.reset(new PeerLogicValidation(*node.connman, node.banman.get(), *node.scheduler, chainman, *node.mempool));
- RegisterValidationInterface(node.peer_logic.get());
+ node.peerman.reset(new PeerManager(chainparams, *node.connman, node.banman.get(), *node.scheduler, chainman, *node.mempool));
+ RegisterValidationInterface(node.peerman.get());
// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
@@ -1550,11 +1555,11 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
const int64_t load_block_index_start_time = GetTimeMillis();
try {
LOCK(cs_main);
- chainman.InitializeChainstate();
+ chainman.InitializeChainstate(*Assert(node.mempool));
chainman.m_total_coinstip_cache = nCoinCacheUsage;
chainman.m_total_coinsdb_cache = nCoinDBCache;
- UnloadBlockIndex(node.mempool);
+ UnloadBlockIndex(node.mempool.get());
// new CBlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it first:
@@ -1828,20 +1833,15 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
}
#if HAVE_SYSTEM
- if (args.IsArgSet("-blocknotify")) {
- const std::string block_notify = args.GetArg("-blocknotify", "");
- const auto BlockNotifyCallback = [block_notify](SynchronizationState sync_state, const CBlockIndex* pBlockIndex) {
- if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex)
- return;
-
- std::string strCmd = block_notify;
- if (!strCmd.empty()) {
- boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex());
- std::thread t(runCommand, strCmd);
- t.detach(); // thread runs free
- }
- };
- uiInterface.NotifyBlockTip_connect(BlockNotifyCallback);
+ const std::string block_notify = args.GetArg("-blocknotify", "");
+ if (!block_notify.empty()) {
+ uiInterface.NotifyBlockTip_connect([block_notify](SynchronizationState sync_state, const CBlockIndex* pBlockIndex) {
+ if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex) return;
+ std::string command = block_notify;
+ boost::replace_all(command, "%s", pBlockIndex->GetBlockHash().GetHex());
+ std::thread t(runCommand, command);
+ t.detach(); // thread runs free
+ });
}
#endif
@@ -1911,7 +1911,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
connOptions.nBestHeight = chain_active_height;
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = node.banman.get();
- connOptions.m_msgproc = node.peer_logic.get();
+ connOptions.m_msgproc = node.peerman.get();
connOptions.nSendBufferMaxSize = 1000 * args.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize = 1000 * args.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.m_added_nodes = args.GetArgs("-addnode");
diff --git a/src/init.h b/src/init.h
index ce12a80dc7..679e875da1 100644
--- a/src/init.h
+++ b/src/init.h
@@ -53,6 +53,10 @@ bool AppInitSanityChecks();
*/
bool AppInitLockDataDirectory();
/**
+ * Initialize node and wallet interface pointers. Has no prerequisites or side effects besides allocating memory.
+ */
+bool AppInitInterfaces(NodeContext& node);
+/**
* Bitcoin core main initialization.
* @note This should only be done after daemonization. Call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 313c1265de..13d885a20d 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -276,13 +276,15 @@ public:
}
RBFTransactionState isRBFOptIn(const CTransaction& tx) override
{
- LOCK(::mempool.cs);
- return IsRBFOptIn(tx, ::mempool);
+ if (!m_node.mempool) return IsRBFOptInEmptyMempool(tx);
+ LOCK(m_node.mempool->cs);
+ return IsRBFOptIn(tx, *m_node.mempool);
}
bool hasDescendantsInMempool(const uint256& txid) override
{
- LOCK(::mempool.cs);
- auto it = ::mempool.GetIter(txid);
+ if (!m_node.mempool) return false;
+ LOCK(m_node.mempool->cs);
+ auto it = m_node.mempool->GetIter(txid);
return it && (*it)->GetCountWithDescendants() > 1;
}
bool broadcastTransaction(const CTransactionRef& tx,
@@ -298,7 +300,9 @@ public:
}
void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
{
- ::mempool.GetTransactionAncestry(txid, ancestors, descendants);
+ ancestors = descendants = 0;
+ if (!m_node.mempool) return;
+ m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants);
}
void getPackageLimits(unsigned int& limit_ancestor_count, unsigned int& limit_descendant_count) override
{
@@ -307,6 +311,7 @@ public:
}
bool checkChainLimits(const CTransactionRef& tx) override
{
+ if (!m_node.mempool) return true;
LockPoints lp;
CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp);
CTxMemPool::setEntries ancestors;
@@ -315,8 +320,9 @@ public:
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,
+ LOCK(m_node.mempool->cs);
+ return m_node.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
@@ -329,7 +335,8 @@ public:
}
CFeeRate mempoolMinFee() override
{
- return ::mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ if (!m_node.mempool) return {};
+ return m_node.mempool->GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CFeeRate relayMinFee() override { return ::minRelayTxFee; }
CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
@@ -395,8 +402,9 @@ public:
}
void requestMempoolTransactions(Notifications& notifications) override
{
- LOCK2(::cs_main, ::mempool.cs);
- for (const CTxMemPoolEntry& entry : ::mempool.mapTx) {
+ if (!m_node.mempool) return;
+ LOCK2(::cs_main, m_node.mempool->cs);
+ for (const CTxMemPoolEntry& entry : m_node.mempool->mapTx) {
notifications.transactionAddedToMempool(entry.GetSharedTx());
}
}
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 053d40335f..6e50ccb27a 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -314,24 +314,11 @@ public:
//! Set mock time.
virtual void setMockTime(int64_t time) = 0;
-
- //! Return interfaces for accessing wallets (if any).
- virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
};
//! Return implementation of Chain interface.
std::unique_ptr<Chain> MakeChain(NodeContext& node);
-//! Return implementation of ChainClient interface for a wallet client. This
-//! function will be undefined in builds where ENABLE_WALLET is false.
-//!
-//! Currently, wallets are the only chain clients. But in the future, other
-//! types of chain clients could be added, such as tools for monitoring,
-//! analysis, or fee estimation. These clients need to expose their own
-//! MakeXXXClient functions returning their implementations of the ChainClient
-//! interface.
-std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames);
-
} // namespace interfaces
#endif // BITCOIN_INTERFACES_CHAIN_H
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 206262eb03..2c5f8627e6 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -27,6 +27,7 @@
#include <support/allocators/secure.h>
#include <sync.h>
#include <txmempool.h>
+#include <util/check.h>
#include <util/ref.h>
#include <util/system.h>
#include <util/translation.h>
@@ -41,16 +42,7 @@
#include <boost/signals2/signal.hpp>
-class CWallet;
-fs::path GetWalletDir();
-std::vector<fs::path> ListWalletDir();
-std::vector<std::shared_ptr<CWallet>> GetWallets();
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings);
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result);
-std::unique_ptr<interfaces::Handler> HandleLoadWallet(interfaces::Node::LoadWalletFn load_wallet);
-
namespace interfaces {
-
namespace {
class NodeImpl : public Node
@@ -64,11 +56,10 @@ public:
bool baseInitialize() override
{
return AppInitBasicSetup(gArgs) && AppInitParameterInteraction(gArgs) && AppInitSanityChecks() &&
- AppInitLockDataDirectory();
+ AppInitLockDataDirectory() && AppInitInterfaces(*m_context);
}
bool appInitMain(interfaces::BlockAndHeaderTipInfo* tip_info) override
{
- m_context->chain = MakeChain(*m_context);
return AppInitMain(m_context_ref, *m_context, tip_info);
}
void appShutdown() override
@@ -240,36 +231,9 @@ public:
LOCK(::cs_main);
return ::ChainstateActive().CoinsTip().GetCoin(output, coin);
}
- std::string getWalletDir() override
- {
- return GetWalletDir().string();
- }
- std::vector<std::string> listWalletDir() override
- {
- std::vector<std::string> paths;
- for (auto& path : ListWalletDir()) {
- paths.push_back(path.string());
- }
- return paths;
- }
- std::vector<std::unique_ptr<Wallet>> getWallets() override
+ WalletClient& walletClient() override
{
- std::vector<std::unique_ptr<Wallet>> wallets;
- for (auto& client : m_context->chain_clients) {
- auto client_wallets = client->getWallets();
- std::move(client_wallets.begin(), client_wallets.end(), std::back_inserter(wallets));
- }
- return wallets;
- }
- std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
- {
- return MakeWallet(LoadWallet(*m_context->chain, name, error, warnings));
- }
- std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, WalletCreationStatus& status) override
- {
- std::shared_ptr<CWallet> wallet;
- status = CreateWallet(*m_context->chain, passphrase, wallet_creation_flags, name, error, warnings, wallet);
- return MakeWallet(wallet);
+ return *Assert(m_context->wallet_client);
}
std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override
{
@@ -287,10 +251,6 @@ public:
{
return MakeHandler(::uiInterface.ShowProgress_connect(fn));
}
- std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
- {
- return HandleLoadWallet(std::move(fn));
- }
std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override
{
return MakeHandler(::uiInterface.NotifyNumConnectionsChanged_connect(fn));
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 0cff7ae3a1..5079be038e 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -29,14 +29,13 @@ class RPCTimerInterface;
class UniValue;
class proxyType;
enum class SynchronizationState;
-enum class WalletCreationStatus;
struct CNodeStateStats;
struct NodeContext;
struct bilingual_str;
namespace interfaces {
class Handler;
-class Wallet;
+class WalletClient;
struct BlockTip;
//! Block and header tip information
@@ -173,22 +172,8 @@ public:
//! Get unspent outputs associated with a transaction.
virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0;
- //! Return default wallet directory.
- virtual std::string getWalletDir() = 0;
-
- //! Return available wallets in wallet directory.
- virtual std::vector<std::string> listWalletDir() = 0;
-
- //! Return interfaces for accessing wallets (if any).
- virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
-
- //! Attempts to load a wallet from file or directory.
- //! The loaded wallet is also notified to handlers previously registered
- //! with handleLoadWallet.
- virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
-
- //! Create a wallet from file
- virtual std::unique_ptr<Wallet> createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, WalletCreationStatus& status) = 0;
+ //! Get wallet client.
+ virtual WalletClient& walletClient() = 0;
//! Register handler for init messages.
using InitMessageFn = std::function<void(const std::string& message)>;
@@ -210,10 +195,6 @@ public:
using ShowProgressFn = std::function<void(const std::string& title, int progress, bool resume_possible)>;
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
- //! Register handler for load wallet messages.
- using LoadWalletFn = std::function<void(std::unique_ptr<Wallet> wallet)>;
- virtual std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) = 0;
-
//! Register handler for number of connections changed messages.
using NotifyNumConnectionsChangedFn = std::function<void(int new_num_connections)>;
virtual std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 7fd24425cf..94c63da84e 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -37,6 +37,7 @@ namespace {
//! Construct wallet tx struct.
WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
{
+ LOCK(wallet.cs_wallet);
WalletTx result;
result.tx = wtx.tx;
result.txin_is_mine.reserve(wtx.tx->vin.size());
@@ -132,7 +133,11 @@ public:
{
return m_wallet->SignMessage(message, pkhash, str_sig);
}
- bool isSpendable(const CTxDestination& dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; }
+ bool isSpendable(const CTxDestination& dest) override
+ {
+ LOCK(m_wallet->cs_wallet);
+ return m_wallet->IsMine(dest) & ISMINE_SPENDABLE;
+ }
bool haveWatchOnly() override
{
auto spk_man = m_wallet->GetLegacyScriptPubKeyMan();
@@ -441,7 +446,7 @@ public:
CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; }
void remove() override
{
- RemoveWallet(m_wallet);
+ RemoveWallet(m_wallet, false /* load_on_start */);
}
bool isLegacy() override { return m_wallet->IsLegacy(); }
std::unique_ptr<Handler> handleUnload(UnloadFn fn) override
@@ -480,15 +485,17 @@ public:
std::shared_ptr<CWallet> m_wallet;
};
-class WalletClientImpl : public ChainClient
+class WalletClientImpl : public WalletClient
{
public:
- WalletClientImpl(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
- : m_wallet_filenames(std::move(wallet_filenames))
+ WalletClientImpl(Chain& chain, ArgsManager& args)
{
m_context.chain = &chain;
m_context.args = &args;
}
+ ~WalletClientImpl() override { UnloadWallets(); }
+
+ //! ChainClient methods
void registerRpcs() override
{
for (const CRPCCommand& command : GetWalletRPCCommands()) {
@@ -498,12 +505,43 @@ public:
m_rpc_handlers.emplace_back(m_context.chain->handleRpc(m_rpc_commands.back()));
}
}
- bool verify() override { return VerifyWallets(*m_context.chain, m_wallet_filenames); }
- bool load() override { return LoadWallets(*m_context.chain, m_wallet_filenames); }
+ bool verify() override { return VerifyWallets(*m_context.chain); }
+ bool load() override { return LoadWallets(*m_context.chain); }
void start(CScheduler& scheduler) override { return StartWallets(scheduler, *Assert(m_context.args)); }
void flush() override { return FlushWallets(); }
void stop() override { return StopWallets(); }
void setMockTime(int64_t time) override { return SetMockTime(time); }
+
+ //! WalletClient methods
+ std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) override
+ {
+ std::shared_ptr<CWallet> wallet;
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.require_create = true;
+ options.create_flags = wallet_creation_flags;
+ options.create_passphrase = passphrase;
+ return MakeWallet(CreateWallet(*m_context.chain, name, true /* load_on_start */, options, status, error, warnings));
+ }
+ std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) override
+ {
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.require_existing = true;
+ return MakeWallet(LoadWallet(*m_context.chain, name, true /* load_on_start */, options, status, error, warnings));
+ }
+ std::string getWalletDir() override
+ {
+ return GetWalletDir().string();
+ }
+ std::vector<std::string> listWalletDir() override
+ {
+ std::vector<std::string> paths;
+ for (auto& path : ListWalletDir()) {
+ paths.push_back(path.string());
+ }
+ return paths;
+ }
std::vector<std::unique_ptr<Wallet>> getWallets() override
{
std::vector<std::unique_ptr<Wallet>> wallets;
@@ -512,7 +550,10 @@ public:
}
return wallets;
}
- ~WalletClientImpl() override { UnloadWallets(); }
+ std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
+ {
+ return HandleLoadWallet(std::move(fn));
+ }
WalletContext m_context;
const std::vector<std::string> m_wallet_filenames;
@@ -524,9 +565,9 @@ public:
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return wallet ? MakeUnique<WalletImpl>(wallet) : nullptr; }
-std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, ArgsManager& args, std::vector<std::string> wallet_filenames)
+std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args)
{
- return MakeUnique<WalletClientImpl>(chain, args, std::move(wallet_filenames));
+ return MakeUnique<WalletClientImpl>(chain, args);
}
} // namespace interfaces
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 3cdadbc72e..6ccfd7fc20 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -6,6 +6,7 @@
#define BITCOIN_INTERFACES_WALLET_H
#include <amount.h> // For CAmount
+#include <interfaces/chain.h> // For ChainClient
#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
#include <script/standard.h> // For CTxDestination
#include <support/allocators/secure.h> // For SecureString
@@ -31,6 +32,7 @@ enum class TransactionError;
enum isminetype : unsigned int;
struct CRecipient;
struct PartiallySignedTransaction;
+struct WalletContext;
struct bilingual_str;
typedef uint8_t isminefilter;
@@ -301,6 +303,34 @@ public:
virtual CWallet* wallet() { return nullptr; }
};
+//! Wallet chain client that in addition to having chain client methods for
+//! starting up, shutting down, and registering RPCs, also has additional
+//! methods (called by the GUI) to load and create wallets.
+class WalletClient : public ChainClient
+{
+public:
+ //! Create new wallet.
+ virtual std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
+
+ //! Load existing wallet.
+ virtual std::unique_ptr<Wallet> loadWallet(const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings) = 0;
+
+ //! Return default wallet directory.
+ virtual std::string getWalletDir() = 0;
+
+ //! Return available wallets in wallet directory.
+ virtual std::vector<std::string> listWalletDir() = 0;
+
+ //! Return interfaces for accessing wallets (if any).
+ virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0;
+
+ //! Register handler for load wallet messages. This callback is triggered by
+ //! createWallet and loadWallet above, and also triggered when wallets are
+ //! loaded at startup or by RPC.
+ using LoadWalletFn = std::function<void(std::unique_ptr<Wallet> wallet)>;
+ virtual std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) = 0;
+};
+
//! Information about one wallet address.
struct WalletAddress
{
@@ -379,6 +409,10 @@ struct WalletTxOut
//! dummywallet.cpp and throws if the wallet component is not compiled.
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet);
+//! Return implementation of ChainClient interface for a wallet client. This
+//! function will be undefined in builds where ENABLE_WALLET is false.
+std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args);
+
} // namespace interfaces
#endif // BITCOIN_INTERFACES_WALLET_H
diff --git a/src/key.cpp b/src/key.cpp
index 4ed74a39b1..868a8b9b0e 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -31,7 +31,7 @@ static secp256k1_context* secp256k1_context_sign = nullptr;
*
* out32 must point to an output buffer of length at least 32 bytes.
*/
-static int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *seckey, size_t seckeylen) {
+int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *seckey, size_t seckeylen) {
const unsigned char *end = seckey + seckeylen;
memset(out32, 0, 32);
/* sequence header */
@@ -88,7 +88,7 @@ static int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char *out
* will be set to the number of bytes used in the buffer.
* key32 must point to a 32-byte raw private key.
*/
-static int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, size_t *seckeylen, const unsigned char *key32, bool compressed) {
+int ec_seckey_export_der(const secp256k1_context *ctx, unsigned char *seckey, size_t *seckeylen, const unsigned char *key32, bool compressed) {
assert(*seckeylen >= CKey::SIZE);
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
diff --git a/src/miner.h b/src/miner.h
index 096585dfe4..bb7a30b184 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -84,7 +84,7 @@ struct CompareTxIterByAncestorCount {
{
if (a->GetCountWithAncestors() != b->GetCountWithAncestors())
return a->GetCountWithAncestors() < b->GetCountWithAncestors();
- return CTxMemPool::CompareIteratorByHash()(a, b);
+ return CompareIteratorByHash()(a, b);
}
};
diff --git a/src/net.cpp b/src/net.cpp
index 8ac45dbcb5..0fd95dfe3f 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -817,6 +817,7 @@ struct NodeEvictionCandidate
CAddress addr;
uint64_t nKeyedNetGroup;
bool prefer_evict;
+ bool m_is_local;
};
static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
@@ -829,6 +830,12 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
return a.nTimeConnected > b.nTimeConnected;
}
+static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
+{
+ if (a.m_is_local != b.m_is_local) return b.m_is_local;
+ return a.nTimeConnected > b.nTimeConnected;
+}
+
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
return a.nKeyedNetGroup < b.nKeyedNetGroup;
}
@@ -850,6 +857,14 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
return a.nTimeConnected > b.nTimeConnected;
}
+// Pick out the potential block-relay only peers, and sort them by last block time.
+static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
+{
+ if (a.fRelayTxes != b.fRelayTxes) return a.fRelayTxes;
+ if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime;
+ if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices;
+ return a.nTimeConnected > b.nTimeConnected;
+}
//! Sort an array by the specified comparator, then erase the last K elements.
template<typename T, typename Comparator>
@@ -892,7 +907,7 @@ bool CConnman::AttemptToEvictConnection()
node->nLastBlockTime, node->nLastTXTime,
HasAllDesirableServiceFlags(node->nServices),
peer_relay_txes, peer_filter_not_null, node->addr, node->nKeyedNetGroup,
- node->m_prefer_evict};
+ node->m_prefer_evict, node->addr.IsLocal()};
vEvictionCandidates.push_back(candidate);
}
}
@@ -905,15 +920,34 @@ bool CConnman::AttemptToEvictConnection()
// Protect the 8 nodes with the lowest minimum ping time.
// An attacker cannot manipulate this metric without physically moving nodes closer to the target.
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8);
- // Protect 4 nodes that most recently sent us transactions.
+ // Protect 4 nodes that most recently sent us novel transactions accepted into our mempool.
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
- // Protect 4 nodes that most recently sent us blocks.
+ // Protect up to 8 non-tx-relay peers that have sent us novel blocks.
+ std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockRelayOnlyTime);
+ size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
+ vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return !n.fRelayTxes && n.fRelevantServices; }), vEvictionCandidates.end());
+
+ // Protect 4 nodes that most recently sent us novel blocks.
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4);
+
// Protect the half of the remaining nodes which have been connected the longest.
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
- EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2);
+ // Reserve half of these protected spots for localhost peers, even if
+ // they're not longest-uptime overall. This helps protect tor peers, which
+ // tend to be otherwise disadvantaged under our eviction criteria.
+ size_t initial_size = vEvictionCandidates.size();
+ size_t total_protect_size = initial_size / 2;
+
+ // Pick out up to 1/4 peers that are localhost, sorted by longest uptime.
+ std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareLocalHostTimeConnected);
+ size_t local_erase_size = total_protect_size / 2;
+ vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - local_erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return n.m_is_local; }), vEvictionCandidates.end());
+ // Calculate how many we removed, and update our total number of peers that
+ // we want to protect based on uptime accordingly.
+ total_protect_size -= initial_size - vEvictionCandidates.size();
+ EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
if (vEvictionCandidates.empty()) return false;
@@ -1844,41 +1878,45 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// but inbound and manual peers do not use our outbound slots. Inbound peers
// also have the added issue that they could be attacker controlled and used
// to prevent us from connecting to particular hosts if we used them here.
- switch(pnode->m_conn_type){
+ switch (pnode->m_conn_type) {
case ConnectionType::INBOUND:
case ConnectionType::MANUAL:
break;
- case ConnectionType::OUTBOUND:
+ case ConnectionType::OUTBOUND_FULL_RELAY:
case ConnectionType::BLOCK_RELAY:
case ConnectionType::ADDR_FETCH:
case ConnectionType::FEELER:
setConnected.insert(pnode->addr.GetGroup(addrman.m_asmap));
- }
+ } // no default case, so the compiler can warn about missing cases
}
}
- // Feeler Connections
- //
- // Design goals:
- // * Increase the number of connectable addresses in the tried table.
- //
- // Method:
- // * Choose a random address from new and attempt to connect to it if we can connect
- // successfully it is added to tried.
- // * Start attempting feeler connections only after node finishes making outbound
- // connections.
- // * Only make a feeler connection once every few minutes.
- //
+ ConnectionType conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
+ int64_t nTime = GetTimeMicros();
bool fFeeler = false;
- if (nOutboundFullRelay >= m_max_outbound_full_relay && nOutboundBlockRelay >= m_max_outbound_block_relay && !GetTryNewOutboundPeer()) {
- int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
- if (nTime > nNextFeeler) {
- nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
- fFeeler = true;
- } else {
- continue;
- }
+ // Determine what type of connection to open. Opening
+ // OUTBOUND_FULL_RELAY connections gets the highest priority until we
+ // meet our full-relay capacity. Then we open BLOCK_RELAY connection
+ // until we hit our block-relay-only peer limit.
+ // GetTryNewOutboundPeer() gets set when a stale tip is detected, so we
+ // try opening an additional OUTBOUND_FULL_RELAY connection. If none of
+ // these conditions are met, check the nNextFeeler timer to decide if
+ // we should open a FEELER.
+
+ if (nOutboundFullRelay < m_max_outbound_full_relay) {
+ // OUTBOUND_FULL_RELAY
+ } else if (nOutboundBlockRelay < m_max_outbound_block_relay) {
+ conn_type = ConnectionType::BLOCK_RELAY;
+ } else if (GetTryNewOutboundPeer()) {
+ // OUTBOUND_FULL_RELAY
+ } else if (nTime > nNextFeeler) {
+ nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
+ conn_type = ConnectionType::FEELER;
+ fFeeler = true;
+ } else {
+ // skip to next iteration of while loop
+ continue;
}
addrman.ResolveCollisions();
@@ -1945,23 +1983,6 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString());
}
- ConnectionType conn_type;
- // Determine what type of connection to open. If fFeeler is not
- // set, open OUTBOUND connections until we meet our full-relay
- // capacity. Then open BLOCK_RELAY connections until we hit our
- // block-relay peer limit. Otherwise, default to opening an
- // OUTBOUND connection.
- if (fFeeler) {
- conn_type = ConnectionType::FEELER;
- } else if (nOutboundFullRelay < m_max_outbound_full_relay) {
- conn_type = ConnectionType::OUTBOUND;
- } else if (nOutboundBlockRelay < m_max_outbound_block_relay) {
- conn_type = ConnectionType::BLOCK_RELAY;
- } else {
- // GetTryNewOutboundPeer() is true
- conn_type = ConnectionType::OUTBOUND;
- }
-
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, conn_type);
}
}
@@ -2817,6 +2838,9 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
hashContinue = uint256();
if (conn_type_in != ConnectionType::BLOCK_RELAY) {
m_tx_relay = MakeUnique<TxRelay>();
+ }
+
+ if (RelayAddrsWithConn()) {
m_addr_known = MakeUnique<CRollingBloomFilter>(5000, 0.001);
}
diff --git a/src/net.h b/src/net.h
index 21faea591a..e957b636cc 100644
--- a/src/net.h
+++ b/src/net.h
@@ -118,12 +118,54 @@ struct CSerializedNetMsg
* information we have available at the time of opening or accepting the
* connection. Aside from INBOUND, all types are initiated by us. */
enum class ConnectionType {
- INBOUND, /**< peer initiated connections */
- OUTBOUND, /**< full relay connections (blocks, addrs, txns) made automatically. Addresses selected from AddrMan. */
- MANUAL, /**< connections to addresses added via addnode or the connect command line argument */
- FEELER, /**< short lived connections used to test address validity */
- BLOCK_RELAY, /**< only relay blocks to these automatic outbound connections. Addresses selected from AddrMan. */
- ADDR_FETCH, /**< short lived connections used to solicit addrs when starting the node without a populated AddrMan */
+ /**
+ * Inbound connections are those initiated by a peer. This is the only
+ * property we know at the time of connection, until P2P messages are
+ * exchanged.
+ */
+ INBOUND,
+
+ /**
+ * These are the default connections that we use to connect with the
+ * network. There is no restriction on what is relayed- by default we relay
+ * blocks, addresses & transactions. We automatically attempt to open
+ * MAX_OUTBOUND_FULL_RELAY_CONNECTIONS using addresses from our AddrMan.
+ */
+ OUTBOUND_FULL_RELAY,
+
+
+ /**
+ * We open manual connections to addresses that users explicitly inputted
+ * via the addnode RPC, or the -connect command line argument. Even if a
+ * manual connection is misbehaving, we do not automatically disconnect or
+ * add it to our discouragement filter.
+ */
+ MANUAL,
+
+ /**
+ * Feeler connections are short lived connections used to increase the
+ * number of connectable addresses in our AddrMan. Approximately every
+ * FEELER_INTERVAL, we attempt to connect to a random address from the new
+ * table. If successful, we add it to the tried table.
+ */
+ FEELER,
+
+ /**
+ * We use block-relay-only connections to help prevent against partition
+ * attacks. By not relaying transactions or addresses, these connections
+ * are harder to detect by a third party, thus helping obfuscate the
+ * network topology. We automatically attempt to open
+ * MAX_BLOCK_RELAY_ONLY_CONNECTIONS using addresses from our AddrMan.
+ */
+ BLOCK_RELAY,
+
+ /**
+ * AddrFetch connections are short lived connections used to solicit
+ * addresses from peers. These are initiated to addresses submitted via the
+ * -seednode command line argument, or under certain conditions when the
+ * AddrMan is empty.
+ */
+ ADDR_FETCH,
};
class NetEventsInterface;
@@ -209,7 +251,7 @@ public:
bool GetNetworkActive() const { return fNetworkActive; };
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
void SetNetworkActive(bool active);
- void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, ConnectionType conn_type = ConnectionType::OUTBOUND);
+ void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant* grantOutbound, const char* strDest, ConnectionType conn_type);
bool CheckIncomingNonce(uint64_t nonce);
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
@@ -827,8 +869,8 @@ public:
std::atomic_bool fPauseSend{false};
bool IsOutboundOrBlockRelayConn() const {
- switch(m_conn_type) {
- case ConnectionType::OUTBOUND:
+ switch (m_conn_type) {
+ case ConnectionType::OUTBOUND_FULL_RELAY:
case ConnectionType::BLOCK_RELAY:
return true;
case ConnectionType::INBOUND:
@@ -836,13 +878,13 @@ public:
case ConnectionType::ADDR_FETCH:
case ConnectionType::FEELER:
return false;
- }
+ } // no default case, so the compiler can warn about missing cases
assert(false);
}
bool IsFullOutboundConn() const {
- return m_conn_type == ConnectionType::OUTBOUND;
+ return m_conn_type == ConnectionType::OUTBOUND_FULL_RELAY;
}
bool IsManualConn() const {
@@ -865,17 +907,23 @@ public:
return m_conn_type == ConnectionType::INBOUND;
}
+ /* Whether we send addr messages over this connection */
+ bool RelayAddrsWithConn() const
+ {
+ return m_conn_type != ConnectionType::BLOCK_RELAY;
+ }
+
bool ExpectServicesFromConn() const {
- switch(m_conn_type) {
+ switch (m_conn_type) {
case ConnectionType::INBOUND:
case ConnectionType::MANUAL:
case ConnectionType::FEELER:
return false;
- case ConnectionType::OUTBOUND:
+ case ConnectionType::OUTBOUND_FULL_RELAY:
case ConnectionType::BLOCK_RELAY:
case ConnectionType::ADDR_FETCH:
return true;
- }
+ } // no default case, so the compiler can warn about missing cases
assert(false);
}
@@ -890,13 +938,11 @@ public:
// flood relay
std::vector<CAddress> vAddrToSend;
- std::unique_ptr<CRollingBloomFilter> m_addr_known = nullptr;
+ std::unique_ptr<CRollingBloomFilter> m_addr_known{nullptr};
bool fGetAddr{false};
std::chrono::microseconds m_next_addr_send GUARDED_BY(cs_sendProcessing){0};
std::chrono::microseconds m_next_local_addr_send GUARDED_BY(cs_sendProcessing){0};
- bool IsAddrRelayPeer() const { return m_addr_known != nullptr; }
-
// List of block ids we still have announce.
// There is no final sorting before sending, as they are always sent immediately
// and in the order requested.
@@ -936,8 +982,17 @@ public:
// Used for headers announcements - unfiltered blocks to relay
std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
- // Block and TXN accept times
+ /** UNIX epoch time of the last block received from this peer that we had
+ * not yet seen (e.g. not already received from another peer), that passed
+ * preliminary validity checks and was saved to disk, even if we don't
+ * connect the block or it eventually fails connection. Used as an inbound
+ * peer eviction criterium in CConnman::AttemptToEvictConnection. */
std::atomic<int64_t> nLastBlockTime{0};
+
+ /** UNIX epoch time of the last transaction received from this peer that we
+ * had not yet seen (e.g. not already received from another peer) and that
+ * was accepted into our mempool. Used as an inbound peer eviction criterium
+ * in CConnman::AttemptToEvictConnection. */
std::atomic<int64_t> nLastTXTime{0};
// Ping time measurement:
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index bf359a0d68..26943e59e4 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -278,12 +278,6 @@ struct CNodeState {
const CService address;
//! Whether we have a fully established connection.
bool fCurrentlyConnected;
- //! Accumulated misbehaviour score for this peer.
- int nMisbehavior;
- //! Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission).
- bool m_should_discourage;
- //! String name of this peer (debugging/logging purposes).
- const std::string name;
//! The best known block we know this peer has announced.
const CBlockIndex *pindexBestKnownBlock;
//! The hash of the last unknown block this peer has announced.
@@ -432,13 +426,10 @@ struct CNodeState {
//! Whether this peer relays txs via wtxid
bool m_wtxid_relay{false};
- CNodeState(CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
- address(addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
- m_is_manual_connection (is_manual)
+ CNodeState(CAddress addrIn, bool is_inbound, bool is_manual)
+ : address(addrIn), m_is_inbound(is_inbound), m_is_manual_connection(is_manual)
{
fCurrentlyConnected = false;
- nMisbehavior = 0;
- m_should_discourage = false;
pindexBestKnownBlock = nullptr;
hashLastUnknownBlock.SetNull();
pindexLastCommonBlock = nullptr;
@@ -476,6 +467,50 @@ static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return &it->second;
}
+/**
+ * Data structure for an individual peer. This struct is not protected by
+ * cs_main since it does not contain validation-critical data.
+ *
+ * Memory is owned by shared pointers and this object is destructed when
+ * the refcount drops to zero.
+ *
+ * TODO: move most members from CNodeState to this structure.
+ * TODO: move remaining application-layer data members from CNode to this structure.
+ */
+struct Peer {
+ /** Same id as the CNode object for this peer */
+ const NodeId m_id{0};
+
+ /** Protects misbehavior data members */
+ Mutex m_misbehavior_mutex;
+ /** Accumulated misbehavior score for this peer */
+ int m_misbehavior_score GUARDED_BY(m_misbehavior_mutex){0};
+ /** Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission). */
+ bool m_should_discourage GUARDED_BY(m_misbehavior_mutex){false};
+
+ Peer(NodeId id) : m_id(id) {}
+};
+
+using PeerRef = std::shared_ptr<Peer>;
+
+/**
+ * Map of all Peer objects, keyed by peer id. This map is protected
+ * by the global g_peer_mutex. Once a shared pointer reference is
+ * taken, the lock may be released. Individual fields are protected by
+ * their own locks.
+ */
+Mutex g_peer_mutex;
+static std::map<NodeId, PeerRef> g_peer_map GUARDED_BY(g_peer_mutex);
+
+/** Get a shared pointer to the Peer object.
+ * May return nullptr if the Peer object can't be found. */
+static PeerRef GetPeerRef(NodeId id)
+{
+ LOCK(g_peer_mutex);
+ auto it = g_peer_map.find(id);
+ return it != g_peer_map.end() ? it->second : nullptr;
+}
+
static void UpdatePreferredDownload(const CNode& node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
nPreferredDownload -= state->fPreferredDownload;
@@ -628,13 +663,12 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma
}
}
connman.ForNode(nodeid, [&connman](CNode* pfrom){
- AssertLockHeld(cs_main);
+ LockAssertion lock(::cs_main);
uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, nCMPCTBLOCKVersion](CNode* pnodeStop){
- AssertLockHeld(cs_main);
connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, /*fAnnounceUsingCMPCTBLOCK=*/false, nCMPCTBLOCKVersion));
return true;
});
@@ -835,29 +869,36 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
if (state) state->m_last_block_announcement = time_in_seconds;
}
-void PeerLogicValidation::InitializeNode(CNode *pnode) {
+void PeerManager::InitializeNode(CNode *pnode) {
CAddress addr = pnode->addr;
std::string addrName = pnode->GetAddrName();
NodeId nodeid = pnode->GetId();
{
LOCK(cs_main);
- mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName), pnode->IsInboundConn(), pnode->IsManualConn()));
+ mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, pnode->IsInboundConn(), pnode->IsManualConn()));
+ }
+ {
+ PeerRef peer = std::make_shared<Peer>(nodeid);
+ LOCK(g_peer_mutex);
+ g_peer_map.emplace_hint(g_peer_map.end(), nodeid, std::move(peer));
}
- if(!pnode->IsInboundConn())
+ if (!pnode->IsInboundConn()) {
PushNodeVersion(*pnode, m_connman, GetTime());
+ }
}
-void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
+void PeerManager::ReattemptInitialBroadcast(CScheduler& scheduler) const
{
- std::map<uint256, uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs();
+ std::set<uint256> unbroadcast_txids = m_mempool.GetUnbroadcastTxs();
- for (const auto& elem : unbroadcast_txids) {
- // Sanity check: all unbroadcast txns should exist in the mempool
- if (m_mempool.exists(elem.first)) {
+ for (const auto& txid : unbroadcast_txids) {
+ CTransactionRef tx = m_mempool.get(txid);
+
+ if (tx != nullptr) {
LOCK(cs_main);
- RelayTransaction(elem.first, elem.second, m_connman);
+ RelayTransaction(txid, tx->GetWitnessHash(), m_connman);
} else {
- m_mempool.RemoveUnbroadcastTx(elem.first, true);
+ m_mempool.RemoveUnbroadcastTx(txid, true);
}
}
@@ -867,16 +908,24 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
}
-void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
+void PeerManager::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
fUpdateConnectionTime = false;
LOCK(cs_main);
+ int misbehavior{0};
+ {
+ PeerRef peer = GetPeerRef(nodeid);
+ assert(peer != nullptr);
+ misbehavior = WITH_LOCK(peer->m_misbehavior_mutex, return peer->m_misbehavior_score);
+ LOCK(g_peer_mutex);
+ g_peer_map.erase(nodeid);
+ }
CNodeState *state = State(nodeid);
assert(state != nullptr);
if (state->fSyncStarted)
nSyncStarted--;
- if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
+ if (misbehavior == 0 && state->fCurrentlyConnected) {
fUpdateConnectionTime = true;
}
@@ -906,17 +955,23 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
}
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
- LOCK(cs_main);
- CNodeState *state = State(nodeid);
- if (state == nullptr)
- return false;
- stats.nMisbehavior = state->nMisbehavior;
- stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
- stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
- for (const QueuedBlock& queue : state->vBlocksInFlight) {
- if (queue.pindex)
- stats.vHeightInFlight.push_back(queue.pindex->nHeight);
+ {
+ LOCK(cs_main);
+ CNodeState* state = State(nodeid);
+ if (state == nullptr)
+ return false;
+ stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
+ stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
+ for (const QueuedBlock& queue : state->vBlocksInFlight) {
+ if (queue.pindex)
+ stats.vHeightInFlight.push_back(queue.pindex->nHeight);
+ }
}
+
+ PeerRef peer = GetPeerRef(nodeid);
+ if (peer == nullptr) return false;
+ stats.m_misbehavior_score = WITH_LOCK(peer->m_misbehavior_mutex, return peer->m_misbehavior_score);
+
return true;
}
@@ -1056,39 +1111,27 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
return nEvicted;
}
-/**
- * Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node
- * to be discouraged, meaning the peer might be disconnected and added to the discouragement filter.
- */
-void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+void PeerManager::Misbehaving(const NodeId pnode, const int howmuch, const std::string& message)
{
assert(howmuch > 0);
- CNodeState* const state = State(pnode);
- if (state == nullptr) return;
+ PeerRef peer = GetPeerRef(pnode);
+ if (peer == nullptr) return;
- state->nMisbehavior += howmuch;
+ LOCK(peer->m_misbehavior_mutex);
+ peer->m_misbehavior_score += howmuch;
const std::string message_prefixed = message.empty() ? "" : (": " + message);
- if (state->nMisbehavior >= DISCOURAGEMENT_THRESHOLD && state->nMisbehavior - howmuch < DISCOURAGEMENT_THRESHOLD)
- {
- LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", pnode, state->nMisbehavior - howmuch, state->nMisbehavior, message_prefixed);
- state->m_should_discourage = true;
+ if (peer->m_misbehavior_score >= DISCOURAGEMENT_THRESHOLD && peer->m_misbehavior_score - howmuch < DISCOURAGEMENT_THRESHOLD) {
+ LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", pnode, peer->m_misbehavior_score - howmuch, peer->m_misbehavior_score, message_prefixed);
+ peer->m_should_discourage = true;
} else {
- LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s\n", pnode, state->nMisbehavior - howmuch, state->nMisbehavior, message_prefixed);
+ LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s\n", pnode, peer->m_misbehavior_score - howmuch, peer->m_misbehavior_score, message_prefixed);
}
}
-/**
- * Potentially mark a node discouraged based on the contents of a BlockValidationState object
- *
- * @param[in] via_compact_block this bool is passed in because net_processing should
- * punish peers differently depending on whether the data was provided in a compact
- * block message or not. If the compact block had a valid header, but contained invalid
- * txs, the peer should not be punished. See BIP 152.
- *
- * @return Returns true if the peer was punished (probably disconnected)
- */
-static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, bool via_compact_block, const std::string& message = "") {
+bool PeerManager::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state,
+ bool via_compact_block, const std::string& message)
+{
switch (state.GetResult()) {
case BlockValidationResult::BLOCK_RESULT_UNSET:
break;
@@ -1096,7 +1139,6 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
case BlockValidationResult::BLOCK_CONSENSUS:
case BlockValidationResult::BLOCK_MUTATED:
if (!via_compact_block) {
- LOCK(cs_main);
Misbehaving(nodeid, 100, message);
return true;
}
@@ -1120,18 +1162,12 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
case BlockValidationResult::BLOCK_INVALID_HEADER:
case BlockValidationResult::BLOCK_CHECKPOINT:
case BlockValidationResult::BLOCK_INVALID_PREV:
- {
- LOCK(cs_main);
- Misbehaving(nodeid, 100, message);
- }
+ Misbehaving(nodeid, 100, message);
return true;
// Conflicting (but not necessarily invalid) data or different policy:
case BlockValidationResult::BLOCK_MISSING_PREV:
- {
- // TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
- LOCK(cs_main);
- Misbehaving(nodeid, 10, message);
- }
+ // TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
+ Misbehaving(nodeid, 10, message);
return true;
case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
case BlockValidationResult::BLOCK_TIME_FUTURE:
@@ -1143,23 +1179,15 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
return false;
}
-/**
- * Potentially disconnect and discourage a node based on the contents of a TxValidationState object
- *
- * @return Returns true if the peer was punished (probably disconnected)
- */
-static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "")
+bool PeerManager::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message)
{
switch (state.GetResult()) {
case TxValidationResult::TX_RESULT_UNSET:
break;
// The node is providing invalid data:
case TxValidationResult::TX_CONSENSUS:
- {
- LOCK(cs_main);
- Misbehaving(nodeid, 100, message);
- return true;
- }
+ Misbehaving(nodeid, 100, message);
+ return true;
// Conflicting (but not necessarily invalid) data or different policy:
case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE:
case TxValidationResult::TX_INPUTS_NOT_STANDARD:
@@ -1197,8 +1225,10 @@ static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Para
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
-PeerLogicValidation::PeerLogicValidation(CConnman& connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool)
- : m_connman(connman),
+PeerManager::PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman,
+ CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool)
+ : m_chainparams(chainparams),
+ m_connman(connman),
m_banman(banman),
m_chainman(chainman),
m_mempool(pool),
@@ -1218,13 +1248,12 @@ PeerLogicValidation::PeerLogicValidation(CConnman& connman, BanMan* banman, CSch
// same probability that we have in the reject filter).
g_recent_confirmed_transactions.reset(new CRollingBloomFilter(48000, 0.000001));
- const Consensus::Params& consensusParams = Params().GetConsensus();
// Stale tip checking and peer eviction are on two different timers, but we
// don't want them to get out of sync due to drift in the scheduler, so we
// combine them in one function and schedule at the quicker (peer-eviction)
// timer.
static_assert(EXTRA_PEER_CHECK_INTERVAL < STALE_CHECK_INTERVAL, "peer eviction timer should be less than stale tip check timer");
- scheduler.scheduleEvery([this, consensusParams] { this->CheckForStaleTipAndEvictPeers(consensusParams); }, std::chrono::seconds{EXTRA_PEER_CHECK_INTERVAL});
+ scheduler.scheduleEvery([this] { this->CheckForStaleTipAndEvictPeers(); }, std::chrono::seconds{EXTRA_PEER_CHECK_INTERVAL});
// schedule next run for 10-15 minutes in the future
const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5});
@@ -1235,7 +1264,7 @@ PeerLogicValidation::PeerLogicValidation(CConnman& connman, BanMan* banman, CSch
* Evict orphan txn pool entries (EraseOrphanTx) based on a newly connected
* block. Also save the time of the last tip update.
*/
-void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
+void PeerManager::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
{
{
LOCK(g_cs_orphans);
@@ -1279,7 +1308,7 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb
}
}
-void PeerLogicValidation::BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex)
+void PeerManager::BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex)
{
// To avoid relay problems with transactions that were previously
// confirmed, clear our filter of recently confirmed transactions whenever
@@ -1304,7 +1333,7 @@ static bool fWitnessesPresentInMostRecentCompactBlock GUARDED_BY(cs_most_recent_
* Maintain state about the best-seen block and fast-announce a compact block
* to compatible peers.
*/
-void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) {
+void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) {
std::shared_ptr<const CBlockHeaderAndShortTxIDs> pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs> (*pblock, true);
const CNetMsgMaker msgMaker(PROTOCOL_VERSION);
@@ -1315,7 +1344,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
return;
nHighestFastAnnounce = pindex->nHeight;
- bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, Params().GetConsensus());
+ bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus());
uint256 hashBlock(pblock->GetHash());
{
@@ -1327,7 +1356,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
}
m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) {
- AssertLockHeld(cs_main);
+ LockAssertion lock(::cs_main);
// TODO: Avoid the repeated-serialization here
if (pnode->nVersion < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect)
@@ -1339,7 +1368,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
if (state.fPreferHeaderAndIDs && (!fWitnessEnabled || state.fWantsCmpctWitness) &&
!PeerHasHeader(&state, pindex) && PeerHasHeader(&state, pindex->pprev)) {
- LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerLogicValidation::NewPoWValidBlock",
+ LogPrint(BCLog::NET, "%s sending header-and-ids %s to peer=%d\n", "PeerManager::NewPoWValidBlock",
hashBlock.ToString(), pnode->GetId());
m_connman.PushMessage(pnode, msgMaker.Make(NetMsgType::CMPCTBLOCK, *pcmpctblock));
state.pindexBestHeaderSent = pindex;
@@ -1351,7 +1380,7 @@ void PeerLogicValidation::NewPoWValidBlock(const CBlockIndex *pindex, const std:
* Update our best height and announce any block hashes which weren't previously
* in ::ChainActive() to our peers.
*/
-void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
+void PeerManager::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
const int nNewHeight = pindexNew->nHeight;
m_connman.SetBestHeight(nNewHeight);
@@ -1386,7 +1415,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
* Handle invalid block rejection and consequent peer discouragement, maintain which
* peers announce compact blocks.
*/
-void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidationState& state) {
+void PeerManager::BlockChecked(const CBlock& block, const BlockValidationState& state) {
LOCK(cs_main);
const uint256 hash(block.GetHash());
@@ -1422,56 +1451,51 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidatio
//
-bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+bool static AlreadyHaveTx(const GenTxid& gtxid, const CTxMemPool& mempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- switch (inv.type)
- {
- case MSG_TX:
- case MSG_WITNESS_TX:
- case MSG_WTX:
- {
- assert(recentRejects);
- if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip)
- {
- // If the chain tip has changed previously rejected transactions
- // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
- // or a double-spend. Reset the rejects filter and give those
- // txs a second chance.
- hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash();
- recentRejects->reset();
- }
-
- {
- LOCK(g_cs_orphans);
- if (!inv.IsMsgWtx() && mapOrphanTransactions.count(inv.hash)) {
- return true;
- } else if (inv.IsMsgWtx() && g_orphans_by_wtxid.count(inv.hash)) {
- return true;
- }
- }
+ assert(recentRejects);
+ if (::ChainActive().Tip()->GetBlockHash() != hashRecentRejectsChainTip) {
+ // If the chain tip has changed previously rejected transactions
+ // might be now valid, e.g. due to a nLockTime'd tx becoming valid,
+ // or a double-spend. Reset the rejects filter and give those
+ // txs a second chance.
+ hashRecentRejectsChainTip = ::ChainActive().Tip()->GetBlockHash();
+ recentRejects->reset();
+ }
- {
- LOCK(g_cs_recent_confirmed_transactions);
- if (g_recent_confirmed_transactions->contains(inv.hash)) return true;
- }
+ const uint256& hash = gtxid.GetHash();
- return recentRejects->contains(inv.hash) || mempool.exists(ToGenTxid(inv));
+ {
+ LOCK(g_cs_orphans);
+ if (!gtxid.IsWtxid() && mapOrphanTransactions.count(hash)) {
+ return true;
+ } else if (gtxid.IsWtxid() && g_orphans_by_wtxid.count(hash)) {
+ return true;
}
- case MSG_BLOCK:
- case MSG_WITNESS_BLOCK:
- return LookupBlockIndex(inv.hash) != nullptr;
}
- // Don't know what it is, just say we already got one
- return true;
+
+ {
+ LOCK(g_cs_recent_confirmed_transactions);
+ if (g_recent_confirmed_transactions->contains(hash)) return true;
+ }
+
+ return recentRejects->contains(hash) || mempool.exists(gtxid);
+}
+
+bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+{
+ return LookupBlockIndex(block_hash) != nullptr;
}
void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman)
{
connman.ForEachNode([&txid, &wtxid](CNode* pnode)
{
- AssertLockHeld(cs_main);
- CNodeState &state = *State(pnode->GetId());
- if (state.m_wtxid_relay) {
+ LockAssertion lock(::cs_main);
+
+ CNodeState* state = State(pnode->GetId());
+ if (state == nullptr) return;
+ if (state->m_wtxid_relay) {
pnode->PushTxInventory(wtxid);
} else {
pnode->PushTxInventory(txid);
@@ -1481,7 +1505,6 @@ void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman&
static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman& connman)
{
- unsigned int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
// Relay to a limited number of other nodes
// Use deterministic randomness to send to the same nodes for 24 hours
@@ -1490,11 +1513,14 @@ static void RelayAddress(const CAddress& addr, bool fReachable, const CConnman&
const CSipHasher hasher = connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24 * 60 * 60));
FastRandomContext insecure_rand;
+ // Relay reachable addresses to 2 peers. Unreachable addresses are relayed randomly to 1 or 2 peers.
+ unsigned int nRelayNodes = (fReachable || (hasher.Finalize() & 1)) ? 2 : 1;
+
std::array<std::pair<uint64_t, CNode*>,2> best{{{0, nullptr}, {0, nullptr}}};
assert(nRelayNodes <= best.size());
auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
- if (pnode->IsAddrRelayPeer()) {
+ if (pnode->RelayAddrsWithConn()) {
uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize();
for (unsigned int i = 0; i < nRelayNodes; i++) {
if (hashKey > best[i].first) {
@@ -1547,7 +1573,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
} // release cs_main before calling ActivateBestChain
if (need_activate_chain) {
BlockValidationState state;
- if (!ActivateBestChain(state, Params(), a_recent_block)) {
+ if (!ActivateBestChain(state, chainparams, a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
@@ -1564,7 +1590,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
// disconnect node in case we have reached the outbound limit for serving historical blocks
if (send &&
connman.OutboundTargetReached(true) &&
- (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) &&
+ (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&
!pfrom.HasPermission(PF_DOWNLOAD) // nodes with the download permission may exceed target
) {
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
@@ -1590,7 +1616,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
std::shared_ptr<const CBlock> pblock;
if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
pblock = a_recent_block;
- } else if (inv.type == MSG_WITNESS_BLOCK) {
+ } else if (inv.IsMsgWitnessBlk()) {
// Fast-path: in this case it is possible to serve the block directly from disk,
// as the network format matches the format on disk
std::vector<uint8_t> block_data;
@@ -1607,12 +1633,11 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
pblock = pblockRead;
}
if (pblock) {
- if (inv.type == MSG_BLOCK)
+ if (inv.IsMsgBlk()) {
connman.PushMessage(&pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock));
- else if (inv.type == MSG_WITNESS_BLOCK)
+ } else if (inv.IsMsgWitnessBlk()) {
connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
- else if (inv.type == MSG_FILTERED_BLOCK)
- {
+ } else if (inv.IsMsgFilteredBlk()) {
bool sendMerkleBlock = false;
CMerkleBlock merkleBlock;
if (pfrom.m_tx_relay != nullptr) {
@@ -1636,9 +1661,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
}
// else
// no response
- }
- else if (inv.type == MSG_CMPCT_BLOCK)
- {
+ } else if (inv.IsMsgCmpctBlk()) {
// If a peer is asking for old blocks, we're almost guaranteed
// they won't have a useful mempool to match against a compact block,
// and we don't feel like constructing the object for them, so
@@ -1673,7 +1696,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c
}
//! Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed).
-CTransactionRef static FindTxForGetData(const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
+static CTransactionRef FindTxForGetData(const CTxMemPool& mempool, const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main)
{
auto txinfo = mempool.info(gtxid);
if (txinfo.tx) {
@@ -1729,7 +1752,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
continue;
}
- CTransactionRef tx = FindTxForGetData(pfrom, ToGenTxid(inv), mempool_req, now);
+ CTransactionRef tx = FindTxForGetData(mempool, pfrom, ToGenTxid(inv), mempool_req, now);
if (tx) {
// WTX and WITNESS_TX imply we serialize with witness
int nSendFlags = (inv.IsMsgTx() ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
@@ -1741,11 +1764,11 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
LOCK(mempool.cs);
auto txiter = mempool.GetIter(tx->GetHash());
if (txiter) {
- const CTxMemPool::setEntries& parents = mempool.GetMemPoolParents(*txiter);
+ const CTxMemPoolEntry::Parents& parents = (*txiter)->GetMemPoolParentsConst();
parent_ids_to_add.reserve(parents.size());
- for (CTxMemPool::txiter parent_iter : parents) {
- if (parent_iter->GetTime() > now - UNCONDITIONAL_RELAY_DELAY) {
- parent_ids_to_add.push_back(parent_iter->GetTx().GetHash());
+ for (const CTxMemPoolEntry& parent : parents) {
+ if (parent.GetTime() > now - UNCONDITIONAL_RELAY_DELAY) {
+ parent_ids_to_add.push_back(parent.GetTx().GetHash());
}
}
}
@@ -1766,7 +1789,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
// expensive to process.
if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) {
const CInv &inv = *it++;
- if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
+ if (inv.IsGenBlkMsg()) {
ProcessGetBlockData(pfrom, chainparams, inv, connman);
}
// else: If the first item on the queue is an unknown type, we erase it
@@ -1802,11 +1825,10 @@ static uint32_t GetFetchFlags(const CNode& pfrom) EXCLUSIVE_LOCKS_REQUIRED(cs_ma
return nFetchFlags;
}
-inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode& pfrom, CConnman& connman) {
+void PeerManager::SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req) {
BlockTransactions resp(req);
for (size_t i = 0; i < req.indexes.size(); i++) {
if (req.indexes[i] >= block.vtx.size()) {
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 100, "getblocktxn with out-of-bounds tx indices");
return;
}
@@ -1815,10 +1837,10 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
LOCK(cs_main);
const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
int nSendFlags = State(pfrom.GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS;
- connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
}
-static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateManager& chainman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
+void PeerManager::ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHeader>& headers, bool via_compact_block)
{
const CNetMsgMaker msgMaker(pfrom.GetSendVersion());
size_t nCount = headers.size();
@@ -1844,7 +1866,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
// nUnconnectingHeaders gets reset back to 0.
if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) {
nodestate->nUnconnectingHeaders++;
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n",
headers[0].GetHash().ToString(),
headers[0].hashPrevBlock.ToString(),
@@ -1878,7 +1900,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
}
BlockValidationState state;
- if (!chainman.ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast)) {
+ if (!m_chainman.ProcessNewBlockHeaders(headers, state, m_chainparams, &pindexLast)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received");
return;
@@ -1909,10 +1931,10 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
// TODO: optimize: if pindexLast is an ancestor of ::ChainActive().Tip or pindexBestHeader, continue
// from there instead.
LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom.GetId(), pfrom.nStartingHeight);
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexLast), uint256()));
}
- bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
+ bool fCanDirectFetch = CanDirectFetch(m_chainparams.GetConsensus());
// If this set of headers is valid and ends in a block with at least as
// much work as our tip, download as much as possible.
if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && ::ChainActive().Tip()->nChainWork <= pindexLast->nChainWork) {
@@ -1922,7 +1944,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
while (pindexWalk && !::ChainActive().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
!mapBlocksInFlight.count(pindexWalk->GetBlockHash()) &&
- (!IsWitnessEnabled(pindexWalk->pprev, chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
+ (!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) {
// We don't have this block, and it's not yet in flight.
vToFetch.push_back(pindexWalk);
}
@@ -1946,7 +1968,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
}
uint32_t nFetchFlags = GetFetchFlags(pfrom);
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
- MarkBlockAsInFlight(mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex);
+ MarkBlockAsInFlight(m_mempool, pfrom.GetId(), pindex->GetBlockHash(), pindex);
LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
pindex->GetBlockHash().ToString(), pfrom.GetId());
}
@@ -1959,7 +1981,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
// In any case, we want to download using a compact block, not a regular one
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
}
- connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
+ m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData));
}
}
}
@@ -1984,7 +2006,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
}
}
- if (!pfrom.fDisconnect && pfrom.IsOutboundOrBlockRelayConn() && nodestate->pindexBestKnownBlock != nullptr && pfrom.m_tx_relay != nullptr) {
+ if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) {
// If this is an outbound full-relay peer, check to see if we should protect
// it from the bad/lagging chain logic.
// Note that block-relay-only peers are already implicitly protected, so we
@@ -2000,7 +2022,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan
return;
}
-void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
+void PeerManager::ProcessOrphanTx(std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn)
{
AssertLockHeld(cs_main);
AssertLockHeld(g_cs_orphans);
@@ -2022,9 +2044,9 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
TxValidationState orphan_state;
if (setMisbehaving.count(fromPeer)) continue;
- if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
+ if (AcceptToMemoryPool(m_mempool, orphan_state, porphanTx, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), connman);
+ RelayTransaction(orphanHash, porphanTx->GetWitnessHash(), m_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()) {
@@ -2082,7 +2104,7 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::set<uin
EraseOrphanTx(orphanHash);
done = true;
}
- mempool.check(&::ChainstateActive().CoinsTip());
+ m_mempool.check(&::ChainstateActive().CoinsTip());
}
}
@@ -2302,9 +2324,9 @@ static void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv, const CChainPar
connman.PushMessage(&peer, std::move(msg));
}
-void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
+void PeerManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
const std::chrono::microseconds time_received,
- const CChainParams& chainparams, const std::atomic<bool>& interruptMsgProc)
+ const std::atomic<bool>& interruptMsgProc)
{
LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
@@ -2318,7 +2340,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// Each connection can only send one version message
if (pfrom.nVersion != 0)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1, "redundant version message");
return;
}
@@ -2426,9 +2447,23 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
UpdatePreferredDownload(pfrom, State(pfrom.GetId()));
}
- if (!pfrom.IsInboundConn() && pfrom.IsAddrRelayPeer())
- {
- // Advertise our address
+ if (!pfrom.IsInboundConn() && !pfrom.IsBlockOnlyConn()) {
+ // For outbound peers, we try to relay our address (so that other
+ // nodes can try to find us more quickly, as we have no guarantee
+ // that an outbound peer is even aware of how to reach us) and do a
+ // one-time address fetch (to help populate/update our addrman). If
+ // we're starting up for the first time, our addrman may be pretty
+ // empty and no one will know who we are, so these mechanisms are
+ // important to help us connect to the network.
+ //
+ // We also update the addrman to record connection success for
+ // these peers (which include OUTBOUND_FULL_RELAY and FEELER
+ // connections) so that addrman will have an up-to-date notion of
+ // which peers are online and available.
+ //
+ // We skip these operations for BLOCK_RELAY peers to avoid
+ // potentially leaking information about our BLOCK_RELAY
+ // connections via the addrman or address relay.
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
@@ -2447,6 +2482,9 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// Get recent addresses
m_connman.PushMessage(&pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR));
pfrom.fGetAddr = true;
+
+ // Moves address from New to Tried table in Addrman, resolves
+ // tried-table collisions, etc.
m_connman.MarkAddressGood(pfrom.addr);
}
@@ -2478,7 +2516,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
if (pfrom.nVersion == 0) {
// Must have a version message before anything else
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1, "non-version message before version handshake");
return;
}
@@ -2545,7 +2582,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
if (!pfrom.fSuccessfullyConnected) {
// Must have a verack message before anything else
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 1, "non-verack message before version handshake");
return;
}
@@ -2554,12 +2590,11 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
std::vector<CAddress> vAddr;
vRecv >> vAddr;
- if (!pfrom.IsAddrRelayPeer()) {
+ if (!pfrom.RelayAddrsWithConn()) {
return;
}
if (vAddr.size() > MAX_ADDR_TO_SEND)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("addr message size = %u", vAddr.size()));
return;
}
@@ -2638,7 +2673,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("inv message size = %u", vInv.size()));
return;
}
@@ -2654,14 +2688,11 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
LOCK(cs_main);
- uint32_t nFetchFlags = GetFetchFlags(pfrom);
const auto current_time = GetTime<std::chrono::microseconds>();
uint256* best_block{nullptr};
- for (CInv &inv : vInv)
- {
- if (interruptMsgProc)
- return;
+ for (CInv& inv : vInv) {
+ if (interruptMsgProc) return;
// Ignore INVs that don't match wtxidrelay setting.
// Note that orphan parent fetching always uses MSG_TX GETDATAs regardless of the wtxidrelay setting.
@@ -2672,14 +2703,10 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
if (inv.IsMsgWtx()) continue;
}
- bool fAlreadyHave = AlreadyHave(inv, m_mempool);
- LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
+ if (inv.IsMsgBlk()) {
+ const bool fAlreadyHave = AlreadyHaveBlock(inv.hash);
+ LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
- if (inv.IsMsgTx()) {
- inv.type |= nFetchFlags;
- }
-
- if (inv.type == MSG_BLOCK) {
UpdateBlockAvailability(pfrom.GetId(), inv.hash);
if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) {
// Headers-first is the primary method of announcement on
@@ -2689,15 +2716,21 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// then fetch the blocks we need to catch up.
best_block = &inv.hash;
}
- } else {
+ } else if (inv.IsGenTxMsg()) {
+ const GenTxid gtxid = ToGenTxid(inv);
+ const bool fAlreadyHave = AlreadyHaveTx(gtxid, m_mempool);
+ LogPrint(BCLog::NET, "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom.GetId());
+
pfrom.AddKnownTx(inv.hash);
if (fBlocksOnly) {
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom.GetId());
pfrom.fDisconnect = true;
return;
} else if (!fAlreadyHave && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) {
- RequestTx(State(pfrom.GetId()), ToGenTxid(inv), current_time);
+ RequestTx(State(pfrom.GetId()), gtxid, current_time);
}
+ } else {
+ LogPrint(BCLog::NET, "Unknown inv type \"%s\" received from peer=%d\n", inv.ToString(), pfrom.GetId());
}
}
@@ -2714,7 +2747,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
{
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("getdata message size = %u", vInv.size()));
return;
}
@@ -2726,7 +2758,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
pfrom.vRecvGetData.insert(pfrom.vRecvGetData.end(), vInv.begin(), vInv.end());
- ProcessGetData(pfrom, chainparams, m_connman, m_mempool, interruptMsgProc);
+ ProcessGetData(pfrom, m_chainparams, m_connman, m_mempool, interruptMsgProc);
return;
}
@@ -2755,7 +2787,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
a_recent_block = most_recent_block;
}
BlockValidationState state;
- if (!ActivateBestChain(state, Params(), a_recent_block)) {
+ if (!ActivateBestChain(state, m_chainparams, a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());
}
}
@@ -2779,7 +2811,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
// If pruning, don't inv blocks unless we have on disk and are likely to still have
// for some reasonable time window (1 hour) that block relay might require.
- const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / chainparams.GetConsensus().nPowTargetSpacing;
+ const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / m_chainparams.GetConsensus().nPowTargetSpacing;
if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= ::ChainActive().Tip()->nHeight - nPrunedBlocksLikelyToHave))
{
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
@@ -2810,7 +2842,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// Unlock cs_most_recent_block to avoid cs_main lock inversion
}
if (recent_block) {
- SendBlockTransactions(*recent_block, req, pfrom, m_connman);
+ SendBlockTransactions(pfrom, *recent_block, req);
return;
}
@@ -2840,10 +2872,10 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
CBlock block;
- bool ret = ReadBlockFromDisk(block, pindex, chainparams.GetConsensus());
+ bool ret = ReadBlockFromDisk(block, pindex, m_chainparams.GetConsensus());
assert(ret);
- SendBlockTransactions(block, req, pfrom, m_connman);
+ SendBlockTransactions(pfrom, block, req);
return;
}
@@ -2874,7 +2906,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
return;
}
- if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) {
+ if (!BlockRequestAllowed(pindex, m_chainparams.GetConsensus())) {
LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom.GetId());
return;
}
@@ -2957,7 +2989,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
std::list<CTransactionRef> lRemovedTxn;
- // We do the AlreadyHave() check using wtxid, rather than txid - in the
+ // We do the AlreadyHaveTx() check using wtxid, rather than txid - in the
// absence of witness malleation, this is strictly better, because the
// recent rejects filter may contain the wtxid but rarely contains
// the txid of a segwit transaction that has been rejected.
@@ -2969,7 +3001,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// already; and an adversary can already relay us old transactions
// (older than our recency filter) if trying to DoS us, without any need
// for witness malleation.
- if (!AlreadyHave(CInv(MSG_WTX, wtxid), m_mempool) &&
+ if (!AlreadyHaveTx(GenTxid(/* is_wtxid=*/true, wtxid), m_mempool) &&
AcceptToMemoryPool(m_mempool, state, ptx, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
m_mempool.check(&::ChainstateActive().CoinsTip());
RelayTransaction(tx.GetHash(), tx.GetWitnessHash(), m_connman);
@@ -2990,7 +3022,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
// Recursively process any orphan transactions that depended on this one
- ProcessOrphanTx(m_connman, m_mempool, pfrom.orphan_work_set, lRemovedTxn);
+ ProcessOrphanTx(pfrom.orphan_work_set, lRemovedTxn);
}
else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
{
@@ -3013,7 +3045,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
}
if (!fRejectedParents) {
- uint32_t nFetchFlags = GetFetchFlags(pfrom);
const auto current_time = GetTime<std::chrono::microseconds>();
for (const uint256& parent_txid : unique_parents) {
@@ -3022,9 +3053,9 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// wtxidrelay peers.
// Eventually we should replace this with an improved
// protocol for getting all unconfirmed parents.
- CInv _inv(MSG_TX | nFetchFlags, parent_txid);
+ const GenTxid gtxid{/* is_wtxid=*/false, parent_txid};
pfrom.AddKnownTx(parent_txid);
- if (!AlreadyHave(_inv, m_mempool)) RequestTx(State(pfrom.GetId()), ToGenTxid(_inv), current_time);
+ if (!AlreadyHaveTx(gtxid, m_mempool)) RequestTx(State(pfrom.GetId()), gtxid, current_time);
}
AddOrphanTx(ptx, pfrom.GetId());
@@ -3076,8 +3107,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
if (RecursiveDynamicUsage(*ptx) < 100000) {
AddToCompactExtraTransactions(ptx);
}
- } else if (tx.HasWitness() && RecursiveDynamicUsage(*ptx) < 100000) {
- AddToCompactExtraTransactions(ptx);
}
if (pfrom.HasPermission(PF_FORCERELAY)) {
@@ -3153,7 +3182,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
const CBlockIndex *pindex = nullptr;
BlockValidationState state;
- if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
+ if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, m_chainparams, &pindex)) {
if (state.IsInvalid()) {
MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
return;
@@ -3209,10 +3238,10 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
// If we're not close to tip yet, give up and let parallel block fetch work its magic
- if (!fAlreadyInFlight && !CanDirectFetch(chainparams.GetConsensus()))
+ if (!fAlreadyInFlight && !CanDirectFetch(m_chainparams.GetConsensus()))
return;
- if (IsWitnessEnabled(pindex->pprev, chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
+ if (IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) {
// Don't bother trying to process compact blocks from v1 peers
// after segwit activates.
return;
@@ -3296,8 +3325,9 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
} // cs_main
- if (fProcessBLOCKTXN)
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, chainparams, interruptMsgProc);
+ if (fProcessBLOCKTXN) {
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, time_received, interruptMsgProc);
+ }
if (fRevertToHeaderProcessing) {
// Headers received from HB compact block peers are permitted to be
@@ -3305,7 +3335,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// the peer if the header turns out to be for an invalid block.
// Note that if a peer tries to build on an invalid chain, that
// will be detected and the peer will be disconnected/discouraged.
- return ProcessHeadersMessage(pfrom, m_connman, m_chainman, m_mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
+ return ProcessHeadersMessage(pfrom, {cmpctblock.header}, /*via_compact_block=*/true);
}
if (fBlockReconstructed) {
@@ -3325,7 +3355,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// we have a chain with at least nMinimumChainWork), and we ignore
// compact blocks with less work than our tip, it is safe to treat
// reconstructed compact blocks as having been requested.
- m_chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
+ m_chainman.ProcessNewBlock(m_chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
pfrom.nLastBlockTime = GetTime();
} else {
@@ -3415,7 +3445,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// disk-space attacks), but this should be safe due to the
// protections in the compact block handler -- see related comment
// in compact block optimistic reconstruction handling.
- m_chainman.ProcessNewBlock(chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
+ m_chainman.ProcessNewBlock(m_chainparams, pblock, /*fForceProcessing=*/true, &fNewBlock);
if (fNewBlock) {
pfrom.nLastBlockTime = GetTime();
} else {
@@ -3439,7 +3469,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
unsigned int nCount = ReadCompactSize(vRecv);
if (nCount > MAX_HEADERS_RESULTS) {
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
return;
}
@@ -3449,7 +3478,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
}
- return ProcessHeadersMessage(pfrom, m_connman, m_chainman, m_mempool, headers, chainparams, /*via_compact_block=*/false);
+ return ProcessHeadersMessage(pfrom, headers, /*via_compact_block=*/false);
}
if (msg_type == NetMsgType::BLOCK)
@@ -3478,7 +3507,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
}
bool fNewBlock = false;
- m_chainman.ProcessNewBlock(chainparams, pblock, forceProcessing, &fNewBlock);
+ m_chainman.ProcessNewBlock(m_chainparams, pblock, forceProcessing, &fNewBlock);
if (fNewBlock) {
pfrom.nLastBlockTime = GetTime();
} else {
@@ -3498,7 +3527,7 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom.GetId());
return;
}
- if (!pfrom.IsAddrRelayPeer()) {
+ if (!pfrom.RelayAddrsWithConn()) {
LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom.GetId());
return;
}
@@ -3641,7 +3670,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
if (!filter.IsWithinSizeConstraints())
{
// There is no excuse for sending a too-large filter
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 100, "too-large bloom filter");
}
else if (pfrom.m_tx_relay != nullptr)
@@ -3675,7 +3703,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
}
if (bad) {
- LOCK(cs_main);
Misbehaving(pfrom.GetId(), 100, "bad filteradd message");
}
return;
@@ -3709,17 +3736,17 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
}
if (msg_type == NetMsgType::GETCFILTERS) {
- ProcessGetCFilters(pfrom, vRecv, chainparams, m_connman);
+ ProcessGetCFilters(pfrom, vRecv, m_chainparams, m_connman);
return;
}
if (msg_type == NetMsgType::GETCFHEADERS) {
- ProcessGetCFHeaders(pfrom, vRecv, chainparams, m_connman);
+ ProcessGetCFHeaders(pfrom, vRecv, m_chainparams, m_connman);
return;
}
if (msg_type == NetMsgType::GETCFCHECKPT) {
- ProcessGetCFCheckPt(pfrom, vRecv, chainparams, m_connman);
+ ProcessGetCFCheckPt(pfrom, vRecv, m_chainparams, m_connman);
return;
}
@@ -3753,23 +3780,20 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
return;
}
-/** Maybe disconnect a peer and discourage future connections from its address.
- *
- * @param[in] pnode The node to check.
- * @return True if the peer was marked for disconnection in this function
- */
-bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode)
+bool PeerManager::MaybeDiscourageAndDisconnect(CNode& pnode)
{
const NodeId peer_id{pnode.GetId()};
+ PeerRef peer = GetPeerRef(peer_id);
+ if (peer == nullptr) return false;
+
{
- LOCK(cs_main);
- CNodeState& state = *State(peer_id);
+ LOCK(peer->m_misbehavior_mutex);
// There's nothing to do if the m_should_discourage flag isn't set
- if (!state.m_should_discourage) return false;
+ if (!peer->m_should_discourage) return false;
- state.m_should_discourage = false;
- } // cs_main
+ peer->m_should_discourage = false;
+ } // peer.m_misbehavior_mutex
if (pnode.HasPermission(PF_NOBAN)) {
// We never disconnect or discourage peers for bad behavior if they have the NOBAN permission flag
@@ -3798,9 +3822,8 @@ bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode)
return true;
}
-bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc)
+bool PeerManager::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc)
{
- const CChainParams& chainparams = Params();
//
// Message format
// (4) message start
@@ -3812,12 +3835,12 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
bool fMoreWork = false;
if (!pfrom->vRecvGetData.empty())
- ProcessGetData(*pfrom, chainparams, m_connman, m_mempool, interruptMsgProc);
+ ProcessGetData(*pfrom, m_chainparams, m_connman, m_mempool, interruptMsgProc);
if (!pfrom->orphan_work_set.empty()) {
std::list<CTransactionRef> removed_txn;
LOCK2(cs_main, g_cs_orphans);
- ProcessOrphanTx(m_connman, m_mempool, pfrom->orphan_work_set, removed_txn);
+ ProcessOrphanTx(pfrom->orphan_work_set, removed_txn);
for (const CTransactionRef& removedTx : removed_txn) {
AddToCompactExtraTransactions(removedTx);
}
@@ -3877,7 +3900,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
}
try {
- ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, chainparams, interruptMsgProc);
+ ProcessMessage(*pfrom, msg_type, vRecv, msg.m_time, interruptMsgProc);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
@@ -3891,7 +3914,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
return fMoreWork;
}
-void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
+void PeerManager::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
{
AssertLockHeld(cs_main);
@@ -3944,7 +3967,7 @@ void PeerLogicValidation::ConsiderEviction(CNode& pto, int64_t time_in_seconds)
}
}
-void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
+void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds)
{
// Check whether we have too many outbound peers
int extra_peers = m_connman.GetExtraOutboundCount();
@@ -3957,7 +3980,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
m_connman.ForEachNode([&](CNode* pnode) {
- AssertLockHeld(cs_main);
+ LockAssertion lock(::cs_main);
// Ignore non-outbound peers, or nodes marked for disconnect already
if (!pnode->IsOutboundOrBlockRelayConn() || pnode->fDisconnect) return;
@@ -3974,7 +3997,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
});
if (worst_peer != -1) {
bool disconnected = m_connman.ForNode(worst_peer, [&](CNode *pnode) {
- AssertLockHeld(cs_main);
+ LockAssertion lock(::cs_main);
// Only disconnect a peer that has been connected to us for
// some reasonable fraction of our check-frequency, to give
@@ -4003,7 +4026,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
}
}
-void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams)
+void PeerManager::CheckForStaleTipAndEvictPeers()
{
LOCK(cs_main);
@@ -4014,7 +4037,7 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params
if (time_in_seconds > m_stale_tip_check_time) {
// Check whether our tip is stale, and if so, allow using an extra
// outbound peer
- if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale(consensusParams)) {
+ if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale(m_chainparams.GetConsensus())) {
LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update);
m_connman.SetTryNewOutboundPeer(true);
} else if (m_connman.GetTryNewOutboundPeer()) {
@@ -4045,9 +4068,9 @@ public:
};
}
-bool PeerLogicValidation::SendMessages(CNode* pto)
+bool PeerManager::SendMessages(CNode* pto)
{
- const Consensus::Params& consensusParams = Params().GetConsensus();
+ const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
// We must call MaybeDiscourageAndDisconnect first, to ensure that we'll
// disconnect misbehaving peers even before the version handshake is complete.
@@ -4098,7 +4121,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
int64_t nNow = GetTimeMicros();
auto current_time = GetTime<std::chrono::microseconds>();
- if (pto->IsAddrRelayPeer() && !::ChainstateActive().IsInitialBlockDownload() && pto->m_next_local_addr_send < current_time) {
+ if (pto->RelayAddrsWithConn() && !::ChainstateActive().IsInitialBlockDownload() && pto->m_next_local_addr_send < current_time) {
AdvertiseLocal(pto);
pto->m_next_local_addr_send = PoissonNextSend(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
@@ -4106,7 +4129,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// Message: addr
//
- if (pto->IsAddrRelayPeer() && pto->m_next_addr_send < current_time) {
+ if (pto->RelayAddrsWithConn() && pto->m_next_addr_send < current_time) {
pto->m_next_addr_send = PoissonNextSend(current_time, AVG_ADDRESS_BROADCAST_INTERVAL);
std::vector<CAddress> vAddr;
vAddr.reserve(pto->vAddrToSend.size());
@@ -4575,7 +4598,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// processing at a later time, see below)
tx_process_time.erase(tx_process_time.begin());
CInv inv(gtxid.IsWtxid() ? MSG_WTX : (MSG_TX | GetFetchFlags(*pto)), gtxid.GetHash());
- if (!AlreadyHave(inv, m_mempool)) {
+ if (!AlreadyHaveTx(ToGenTxid(inv), m_mempool)) {
// If this transaction was last requested more than 1 minute ago,
// then request.
const auto last_request_time = GetTxRequestTime(gtxid);
diff --git a/src/net_processing.h b/src/net_processing.h
index 74d6603747..3e748c0c5b 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -11,9 +11,13 @@
#include <sync.h>
#include <validationinterface.h>
+class BlockTransactionsRequest;
+class BlockValidationState;
+class CBlockHeader;
class CChainParams;
class CTxMemPool;
class ChainstateManager;
+class TxValidationState;
extern RecursiveMutex cs_main;
extern RecursiveMutex g_cs_orphans;
@@ -27,18 +31,10 @@ static const bool DEFAULT_PEERBLOCKFILTERS = false;
/** Threshold for marking a node to be discouraged, e.g. disconnected and added to the discouragement filter. */
static const int DISCOURAGEMENT_THRESHOLD{100};
-class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
-private:
- CConnman& m_connman;
- /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */
- BanMan* const m_banman;
- ChainstateManager& m_chainman;
- CTxMemPool& m_mempool;
-
- bool MaybeDiscourageAndDisconnect(CNode& pnode);
-
+class PeerManager final : public CValidationInterface, public NetEventsInterface {
public:
- PeerLogicValidation(CConnman& connman, BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool);
+ PeerManager(const CChainParams& chainparams, CConnman& connman, BanMan* banman,
+ CScheduler& scheduler, ChainstateManager& chainman, CTxMemPool& pool);
/**
* Overridden from CValidationInterface.
@@ -80,7 +76,7 @@ public:
/** Consider evicting an outbound peer based on the amount of time they've been behind our tip */
void ConsiderEviction(CNode& pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound */
- void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams);
+ void CheckForStaleTipAndEvictPeers();
/** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */
void EvictExtraOutboundPeers(int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Retrieve unbroadcast transactions from the mempool and reattempt sending to peers */
@@ -88,16 +84,62 @@ public:
/** Process a single message from a peer. Public for fuzz testing */
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
- const std::chrono::microseconds time_received, const CChainParams& chainparams,
- const std::atomic<bool>& interruptMsgProc);
+ const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc);
+
+ /**
+ * Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node
+ * to be discouraged, meaning the peer might be disconnected and added to the discouragement filter.
+ * Public for unit testing.
+ */
+ void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message);
private:
- int64_t m_stale_tip_check_time; //!< Next time to check for stale tip
+ /**
+ * Potentially mark a node discouraged based on the contents of a BlockValidationState object
+ *
+ * @param[in] via_compact_block this bool is passed in because net_processing should
+ * punish peers differently depending on whether the data was provided in a compact
+ * block message or not. If the compact block had a valid header, but contained invalid
+ * txs, the peer should not be punished. See BIP 152.
+ *
+ * @return Returns true if the peer was punished (probably disconnected)
+ */
+ bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state,
+ bool via_compact_block, const std::string& message = "");
+ /**
+ * Potentially disconnect and discourage a node based on the contents of a TxValidationState object
+ *
+ * @return Returns true if the peer was punished (probably disconnected)
+ */
+ bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "");
+
+ /** Maybe disconnect a peer and discourage future connections from its address.
+ *
+ * @param[in] pnode The node to check.
+ * @return True if the peer was marked for disconnection in this function
+ */
+ bool MaybeDiscourageAndDisconnect(CNode& pnode);
+
+ void ProcessOrphanTx(std::set<uint256>& orphan_work_set, std::list<CTransactionRef>& removed_txn)
+ EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans);
+ /** Process a single headers message from a peer. */
+ void ProcessHeadersMessage(CNode& pfrom, const std::vector<CBlockHeader>& headers, bool via_compact_block);
+
+ void SendBlockTransactions(CNode& pfrom, const CBlock& block, const BlockTransactionsRequest& req);
+
+ const CChainParams& m_chainparams;
+ CConnman& m_connman;
+ /** Pointer to this node's banman. May be nullptr - check existence before dereferencing. */
+ BanMan* const m_banman;
+ ChainstateManager& m_chainman;
+ CTxMemPool& m_mempool;
+
+ int64_t m_stale_tip_check_time; //!< Next time to check for stale tip
};
struct CNodeStateStats {
- int nMisbehavior = 0;
+ int m_misbehavior_score = 0;
int nSyncHeight = -1;
int nCommonHeight = -1;
std::vector<int> vHeightInFlight;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 8adfe38dc9..b50cf74069 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -270,17 +270,6 @@ bool CNetAddr::IsLocal() const
*/
bool CNetAddr::IsValid() const
{
- // Cleanup 3-byte shifted addresses caused by garbage in size field
- // of addr messages from versions before 0.2.9 checksum.
- // Two consecutive addr messages look like this:
- // header20 vectorlen3 addr26 addr26 addr26 header20 vectorlen3 addr26 addr26 addr26...
- // so if the first length field is garbled, it reads the second batch
- // of addr misaligned by 3 bytes.
- if (IsIPv6() && memcmp(m_addr.data(), IPV4_IN_IPV6_PREFIX.data() + 3,
- sizeof(IPV4_IN_IPV6_PREFIX) - 3) == 0) {
- return false;
- }
-
// unspecified IPv6 address (::/128)
unsigned char ipNone6[16] = {};
if (IsIPv6() && memcmp(m_addr.data(), ipNone6, sizeof(ipNone6)) == 0) {
diff --git a/src/node/context.cpp b/src/node/context.cpp
index 0238aab0d9..49d0c37235 100644
--- a/src/node/context.cpp
+++ b/src/node/context.cpp
@@ -9,6 +9,7 @@
#include <net.h>
#include <net_processing.h>
#include <scheduler.h>
+#include <txmempool.h>
NodeContext::NodeContext() {}
NodeContext::~NodeContext() {}
diff --git a/src/node/context.h b/src/node/context.h
index be568cba36..3228831ed1 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -16,10 +16,11 @@ class CConnman;
class CScheduler;
class CTxMemPool;
class ChainstateManager;
-class PeerLogicValidation;
+class PeerManager;
namespace interfaces {
class Chain;
class ChainClient;
+class WalletClient;
} // namespace interfaces
//! NodeContext struct containing references to chain state and connection
@@ -34,13 +35,17 @@ class ChainClient;
//! be used without pulling in unwanted dependencies or functionality.
struct NodeContext {
std::unique_ptr<CConnman> connman;
- CTxMemPool* mempool{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
- std::unique_ptr<PeerLogicValidation> peer_logic;
+ std::unique_ptr<CTxMemPool> mempool;
+ std::unique_ptr<PeerManager> peerman;
ChainstateManager* chainman{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<BanMan> banman;
ArgsManager* args{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<interfaces::Chain> chain;
+ //! List of all chain clients (wallet processes or other client) connected to node.
std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
+ //! Reference to chain client that should used to load or create wallets
+ //! opened by the gui.
+ interfaces::WalletClient* wallet_client{nullptr};
std::unique_ptr<CScheduler> scheduler;
std::function<void()> rpc_interruption_point = [] {};
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 5633abe817..9ae4700743 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -38,8 +38,8 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
if (!node.mempool->exists(hashTx)) {
// Transaction is not already in the mempool. Submit it.
TxValidationState state;
- if (!AcceptToMemoryPool(*node.mempool, state, std::move(tx),
- nullptr /* plTxnReplaced */, false /* bypass_limits */, max_tx_fee)) {
+ if (!AcceptToMemoryPool(*node.mempool, state, tx,
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, max_tx_fee)) {
err_string = state.ToString();
if (state.IsInvalid()) {
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
@@ -80,7 +80,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
if (relay) {
// the mempool tracks locally submitted transactions to make a
// best-effort of initial broadcast
- node.mempool->AddUnbroadcastTx(hashTx, tx->GetWitnessHash());
+ node.mempool->AddUnbroadcastTx(hashTx);
LOCK(cs_main);
RelayTransaction(hashTx, tx->GetWitnessHash(), *node.connman);
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index f8b17d18d5..4b55934891 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -36,3 +36,9 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
}
return RBFTransactionState::FINAL;
}
+
+RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
+{
+ // If we don't have a local mempool we can only check the transaction itself.
+ return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
+}
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index d335fbbb36..f84e6e5286 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -7,16 +7,28 @@
#include <txmempool.h>
+/** The rbf state of unconfirmed transactions */
enum class RBFTransactionState {
+ /** Unconfirmed tx that does not signal rbf and is not in the mempool */
UNKNOWN,
+ /** Either this tx or a mempool ancestor signals rbf */
REPLACEABLE_BIP125,
- FINAL
+ /** Neither this tx nor a mempool ancestor signals rbf */
+ FINAL,
};
-// Determine whether an in-mempool transaction is signaling opt-in to RBF
-// according to BIP 125
-// This involves checking sequence numbers of the transaction, as well
-// as the sequence numbers of all in-mempool ancestors.
+/**
+ * Determine whether an unconfirmed transaction is signaling opt-in to RBF
+ * according to BIP 125
+ * This involves checking sequence numbers of the transaction, as well
+ * as the sequence numbers of all in-mempool ancestors.
+ *
+ * @param tx The unconfirmed transaction
+ * @param pool The mempool, which may contain the tx
+ *
+ * @return The rbf state
+ */
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
+RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx);
#endif // BITCOIN_POLICY_RBF_H
diff --git a/src/protocol.cpp b/src/protocol.cpp
index c989aa3902..48ca0c6df6 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -8,10 +8,6 @@
#include <util/strencodings.h>
#include <util/system.h>
-#ifndef WIN32
-# include <arpa/inet.h>
-#endif
-
static std::atomic<bool> g_initial_block_download_completed(false);
namespace NetMsgType {
@@ -163,7 +159,7 @@ CInv::CInv()
hash.SetNull();
}
-CInv::CInv(int typeIn, const uint256& hashIn) : type(typeIn), hash(hashIn) {}
+CInv::CInv(uint32_t typeIn, const uint256& hashIn) : type(typeIn), hash(hashIn) {}
bool operator<(const CInv& a, const CInv& b)
{
diff --git a/src/protocol.h b/src/protocol.h
index 2e6c767cdd..7fb84cddf1 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -247,7 +247,7 @@ extern const char* CFCHECKPT;
* txid.
* @since protocol version 70016 as described by BIP 339.
*/
-extern const char *WTXIDRELAY;
+extern const char* WTXIDRELAY;
}; // namespace NetMsgType
/* Get a vector of all valid message types (see above) */
@@ -408,7 +408,7 @@ class CInv
{
public:
CInv();
- CInv(int typeIn, const uint256& hashIn);
+ CInv(uint32_t typeIn, const uint256& hashIn);
SERIALIZE_METHODS(CInv, obj) { READWRITE(obj.type, obj.hash); }
@@ -418,14 +418,24 @@ public:
std::string ToString() const;
// Single-message helper methods
- bool IsMsgTx() const { return type == MSG_TX; }
- bool IsMsgWtx() const { return type == MSG_WTX; }
- bool IsMsgWitnessTx() const { return type == MSG_WITNESS_TX; }
+ bool IsMsgTx() const { return type == MSG_TX; }
+ bool IsMsgBlk() const { return type == MSG_BLOCK; }
+ bool IsMsgWtx() const { return type == MSG_WTX; }
+ bool IsMsgFilteredBlk() const { return type == MSG_FILTERED_BLOCK; }
+ bool IsMsgCmpctBlk() const { return type == MSG_CMPCT_BLOCK; }
+ bool IsMsgWitnessBlk() const { return type == MSG_WITNESS_BLOCK; }
// Combined-message helper methods
- bool IsGenTxMsg() const { return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX; }
+ bool IsGenTxMsg() const
+ {
+ return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX;
+ }
+ bool IsGenBlkMsg() const
+ {
+ return type == MSG_BLOCK || type == MSG_FILTERED_BLOCK || type == MSG_CMPCT_BLOCK || type == MSG_WITNESS_BLOCK;
+ }
- int type;
+ uint32_t type;
uint256 hash;
};
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index ef42aa5bc7..fc14f41a0c 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -24,7 +24,7 @@ secp256k1_context* secp256k1_context_verify = nullptr;
* strict DER before being passed to this module, and we know it supports all
* violations present in the blockchain before that point.
*/
-static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
+int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
size_t rpos, rlen, spos, slen;
size_t pos = 0;
size_t lenbyte;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index e63ffdfb36..7a3fb420cc 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -166,8 +166,8 @@ void BitcoinCore::initialize()
{
try
{
- qDebug() << __func__ << ": Running initialization in thread";
util::ThreadRename("qt-init");
+ qDebug() << __func__ << ": Running initialization in thread";
interfaces::BlockAndHeaderTipInfo tip_info;
bool rv = m_node.appInitMain(&tip_info);
Q_EMIT initializeResult(rv, tip_info);
@@ -206,6 +206,7 @@ BitcoinApplication::BitcoinApplication():
returnValue(0),
platformStyle(nullptr)
{
+ // Qt runs setlocale(LC_ALL, "") on initialization.
RegisterMetaTypes();
setQuitOnLastWindowClosed(false);
}
@@ -266,6 +267,7 @@ void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
// We don't hold a direct pointer to the splash screen after creation, but the splash
// screen will take care of deleting itself when finish() happens.
m_splash->show();
+ connect(this, &BitcoinApplication::requestedInitialize, m_splash, &SplashScreen::handleLoadWallet);
connect(this, &BitcoinApplication::splashFinished, m_splash, &SplashScreen::finish);
connect(this, &BitcoinApplication::requestedShutdown, m_splash, &QWidget::close);
}
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 8935ff19bf..0c2dcc3584 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -660,6 +660,11 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller)
}
}
+WalletController* BitcoinGUI::getWalletController()
+{
+ return m_wallet_controller;
+}
+
void BitcoinGUI::addWallet(WalletModel* walletModel)
{
if (!walletFrame) return;
@@ -1189,7 +1194,7 @@ void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmoun
// On new transaction, make an info balloon
QString msg = tr("Date: %1\n").arg(date) +
tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(unit, amount, true));
- if (m_node.getWallets().size() > 1 && !walletName.isEmpty()) {
+ if (m_node.walletClient().getWallets().size() > 1 && !walletName.isEmpty()) {
msg += tr("Wallet: %1\n").arg(walletName);
}
msg += tr("Type: %1\n").arg(type);
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 4c55f28693..912297a74e 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -79,6 +79,7 @@ public:
void setClientModel(ClientModel *clientModel = nullptr, interfaces::BlockAndHeaderTipInfo* tip_info = nullptr);
#ifdef ENABLE_WALLET
void setWalletController(WalletController* wallet_controller);
+ WalletController* getWalletController();
#endif
#ifdef ENABLE_WALLET
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 7822d4c5f3..a2f46c339b 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -15,6 +15,7 @@
#include <net.h>
#include <netbase.h>
#include <util/system.h>
+#include <util/threadnames.h>
#include <validation.h>
#include <stdint.h>
@@ -52,6 +53,9 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
// move timer to thread so that polling doesn't disturb main event loop
timer->moveToThread(m_thread);
m_thread->start();
+ QTimer::singleShot(0, timer, []() {
+ util::ThreadRename("qt-clientmodl");
+ });
subscribeToCoreSignals();
}
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 93840b4169..d210faec03 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -290,7 +290,7 @@
<item row="11" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
- <string>Current number of blocks</string>
+ <string>Current block height</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 7dbee6d689..06d39426c9 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -114,6 +114,12 @@
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/receiving_addresses</normaloff>:/icons/receiving_addresses</iconset>
</property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item>
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 5debded4ea..d374d610ee 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -242,22 +242,6 @@ void ReceiveCoinsDialog::resizeEvent(QResizeEvent *event)
columnResizingFixer->stretchColumnWidth(RecentRequestsTableModel::Message);
}
-void ReceiveCoinsDialog::keyPressEvent(QKeyEvent *event)
-{
- if (event->key() == Qt::Key_Return)
- {
- // press return -> submit form
- if (ui->reqLabel->hasFocus() || ui->reqAmount->hasFocus() || ui->reqMessage->hasFocus())
- {
- event->ignore();
- on_receiveButton_clicked();
- return;
- }
- }
-
- this->QDialog::keyPressEvent(event);
-}
-
QModelIndex ReceiveCoinsDialog::selectedRow()
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h
index 2f48cd58f0..27455e2906 100644
--- a/src/qt/receivecoinsdialog.h
+++ b/src/qt/receivecoinsdialog.h
@@ -49,9 +49,6 @@ public Q_SLOTS:
void reject() override;
void accept() override;
-protected:
- virtual void keyPressEvent(QKeyEvent *event) override;
-
private:
Ui::ReceiveCoinsDialog *ui;
GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer;
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index a14fae6460..4c5601242e 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -20,6 +20,7 @@
#include <rpc/client.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/threadnames.h>
#include <univalue.h>
@@ -978,6 +979,9 @@ void RPCConsole::startExecutor()
// Default implementation of QThread::run() simply spins up an event loop in the thread,
// which is what we want.
thread.start();
+ QTimer::singleShot(0, executor, []() {
+ util::ThreadRename("qt-rpcconsole");
+ });
}
void RPCConsole::on_tabWidget_currentChanged(int index)
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index bd63d6e7fb..8e381861a0 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -185,21 +185,21 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr
: _("press q to shutdown").translated) +
strprintf("\n%d", nProgress) + "%");
}
-#ifdef ENABLE_WALLET
-void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet)
-{
- m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false)));
- m_connected_wallets.emplace_back(std::move(wallet));
-}
-#endif
void SplashScreen::subscribeToCoreSignals()
{
// Connect signals to client
m_handler_init_message = m_node->handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
m_handler_show_progress = m_node->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+}
+
+void SplashScreen::handleLoadWallet()
+{
#ifdef ENABLE_WALLET
- m_handler_load_wallet = m_node->handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); });
+ m_handler_load_wallet = m_node->walletClient().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
+ m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false)));
+ m_connected_wallets.emplace_back(std::move(wallet));
+ });
#endif
}
diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h
index 2213b02c55..a0cd677d3d 100644
--- a/src/qt/splashscreen.h
+++ b/src/qt/splashscreen.h
@@ -43,6 +43,9 @@ public Q_SLOTS:
/** Show message and progress */
void showMessage(const QString &message, int alignment, const QColor &color);
+ /** Handle wallet load notifications. */
+ void handleLoadWallet();
+
protected:
bool eventFilter(QObject * obj, QEvent * ev) override;
@@ -53,8 +56,6 @@ private:
void unsubscribeFromCoreSignals();
/** Initiate shutdown */
void shutdown();
- /** Connect wallet signals to splash screen */
- void ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet);
QPixmap pixmap;
QString curMessage;
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 84f981dff3..35fcb2b0ca 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -61,7 +61,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
{
TestChain100Setup test;
node.setContext(&test.m_node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
bool firstRun;
wallet->LoadWallet(firstRun);
@@ -112,7 +112,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
ClientModel clientModel(node, &optionsModel);
AddWallet(wallet);
WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());
- RemoveWallet(wallet);
+ RemoveWallet(wallet, nullopt);
EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress);
editAddressDialog.setModel(walletModel.getAddressTableModel());
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index adcfe0d25c..d6d2d0e3df 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -139,7 +139,7 @@ void TestGUI(interfaces::Node& node)
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
node.setContext(&test.m_node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);
{
@@ -167,7 +167,7 @@ void TestGUI(interfaces::Node& node)
ClientModel clientModel(node, &optionsModel);
AddWallet(wallet);
WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());
- RemoveWallet(wallet);
+ RemoveWallet(wallet, nullopt);
sendCoinsDialog.setModel(&walletModel);
transactionView.setModel(&walletModel);
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 3aed98e0e8..d9e0274d01 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -14,6 +14,7 @@
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <util/string.h>
+#include <util/threadnames.h>
#include <util/translation.h>
#include <wallet/wallet.h>
@@ -35,16 +36,19 @@ WalletController::WalletController(ClientModel& client_model, const PlatformStyl
, m_platform_style(platform_style)
, m_options_model(client_model.getOptionsModel())
{
- m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
+ m_handler_load_wallet = m_node.walletClient().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
getOrCreateWallet(std::move(wallet));
});
- for (std::unique_ptr<interfaces::Wallet>& wallet : m_node.getWallets()) {
+ for (std::unique_ptr<interfaces::Wallet>& wallet : m_node.walletClient().getWallets()) {
getOrCreateWallet(std::move(wallet));
}
m_activity_worker->moveToThread(m_activity_thread);
m_activity_thread->start();
+ QTimer::singleShot(0, m_activity_worker, []() {
+ util::ThreadRename("qt-walletctrl");
+ });
}
// Not using the default destructor because not all member types definitions are
@@ -66,7 +70,7 @@ std::map<std::string, bool> WalletController::listWalletDir() const
{
QMutexLocker locker(&m_mutex);
std::map<std::string, bool> wallets;
- for (const std::string& name : m_node.listWalletDir()) {
+ for (const std::string& name : m_node.walletClient().listWalletDir()) {
wallets[name] = false;
}
for (WalletModel* wallet_model : m_wallets) {
@@ -249,10 +253,9 @@ void CreateWalletActivity::createWallet()
}
QTimer::singleShot(500, worker(), [this, name, flags] {
- WalletCreationStatus status;
- std::unique_ptr<interfaces::Wallet> wallet = node().createWallet(m_passphrase, flags, name, m_error_message, m_warning_message, status);
+ std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message);
- if (status == WalletCreationStatus::SUCCESS) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
+ if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
QTimer::singleShot(500, this, &CreateWalletActivity::finish);
});
@@ -321,7 +324,7 @@ void OpenWalletActivity::open(const std::string& path)
showProgressDialog(tr("Opening Wallet <b>%1</b>...").arg(name.toHtmlEscaped()));
QTimer::singleShot(0, worker(), [this, path] {
- std::unique_ptr<interfaces::Wallet> wallet = node().loadWallet(path, m_error_message, m_warning_message);
+ std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().loadWallet(path, m_error_message, m_warning_message);
if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet));
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index ec56f2755f..f16761d6b2 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -2,6 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <qt/createwalletdialog.h>
+#include <qt/walletcontroller.h>
#include <qt/walletframe.h>
#include <qt/walletmodel.h>
@@ -10,8 +12,11 @@
#include <cassert>
+#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
+#include <QPushButton>
+#include <QVBoxLayout>
WalletFrame::WalletFrame(const PlatformStyle *_platformStyle, BitcoinGUI *_gui) :
QFrame(_gui),
@@ -25,9 +30,25 @@ WalletFrame::WalletFrame(const PlatformStyle *_platformStyle, BitcoinGUI *_gui)
walletFrameLayout->setContentsMargins(0,0,0,0);
walletFrameLayout->addWidget(walletStack);
- QLabel *noWallet = new QLabel(tr("No wallet has been loaded."));
+ // hbox for no wallet
+ QGroupBox* no_wallet_group = new QGroupBox(walletStack);
+ QVBoxLayout* no_wallet_layout = new QVBoxLayout(no_wallet_group);
+
+ QLabel *noWallet = new QLabel(tr("No wallet has been loaded.\nGo to File > Open Wallet to load a wallet.\n- OR -"));
noWallet->setAlignment(Qt::AlignCenter);
- walletStack->addWidget(noWallet);
+ no_wallet_layout->addWidget(noWallet, 0, Qt::AlignHCenter | Qt::AlignBottom);
+
+ // A button for create wallet dialog
+ QPushButton* create_wallet_button = new QPushButton(tr("Create a new wallet"), walletStack);
+ connect(create_wallet_button, &QPushButton::clicked, [this] {
+ auto activity = new CreateWalletActivity(gui->getWalletController(), this);
+ connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
+ activity->create();
+ });
+ no_wallet_layout->addWidget(create_wallet_button, 0, Qt::AlignHCenter | Qt::AlignTop);
+ no_wallet_group->setLayout(no_wallet_layout);
+
+ walletStack->addWidget(no_wallet_group);
}
WalletFrame::~WalletFrame()
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index e374dd191c..6a3f903206 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -413,7 +413,7 @@ void WalletModel::subscribeToCoreSignals()
m_handler_transaction_changed = m_wallet->handleTransactionChanged(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2));
m_handler_show_progress = m_wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
m_handler_watch_only_changed = m_wallet->handleWatchOnlyChanged(std::bind(NotifyWatchonlyChanged, this, std::placeholders::_1));
- m_handler_can_get_addrs_changed = m_wallet->handleCanGetAddressesChanged(boost::bind(NotifyCanGetAddressesChanged, this));
+ m_handler_can_get_addrs_changed = m_wallet->handleCanGetAddressesChanged(std::bind(NotifyCanGetAddressesChanged, this));
}
void WalletModel::unsubscribeFromCoreSignals()
@@ -581,7 +581,7 @@ QString WalletModel::getDisplayName() const
bool WalletModel::isMultiwallet()
{
- return m_node.getWallets().size() > 1;
+ return m_node.walletClient().getWallets().size() > 1;
}
void WalletModel::refresh(bool pk_hash_only)
diff --git a/src/rest.cpp b/src/rest.cpp
index 7130625d5c..f0bcbe55f9 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -102,7 +102,7 @@ static CTxMemPool* GetMemPool(const util::Ref& context, HTTPRequest* req)
RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
return nullptr;
}
- return node->mempool;
+ return node->mempool.get();
}
static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
@@ -393,7 +393,7 @@ static bool rest_tx(const util::Ref& context, HTTPRequest* req, const std::strin
const NodeContext* const node = GetNodeContext(context, req);
if (!node) return false;
uint256 hashBlock = uint256();
- const CTransactionRef tx = GetTransaction(/* block_index */ nullptr, node->mempool, hash, Params().GetConsensus(), hashBlock);
+ const CTransactionRef tx = GetTransaction(/* block_index */ nullptr, node->mempool.get(), hash, Params().GetConsensus(), hashBlock);
if (!tx) {
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 033e00daf5..04b9c6b1cc 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -463,9 +463,9 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
UniValue spent(UniValue::VARR);
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());
+ const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
+ for (const CTxMemPoolEntry& child : children) {
+ spent.push_back(child.GetTx().GetHash().ToString());
}
info.pushKV("spentby", spent);
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 4d08671bd2..6ef3294132 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -125,6 +125,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "gettxoutproof", 0, "txids" },
{ "lockunspent", 0, "unlock" },
{ "lockunspent", 1, "transactions" },
+ { "send", 0, "outputs" },
+ { "send", 1, "conf_target" },
+ { "send", 3, "options" },
{ "importprivkey", 2, "rescan" },
{ "importaddress", 2, "rescan" },
{ "importaddress", 3, "p2sh" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 76aa9dbfc1..b04e106b2d 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -81,9 +81,9 @@ static UniValue GetNetworkHashPS(int lookup, int height) {
return workDiff.getdouble() / timeDiff;
}
-static UniValue getnetworkhashps(const JSONRPCRequest& request)
+static RPCHelpMan getnetworkhashps()
{
- RPCHelpMan{"getnetworkhashps",
+ return RPCHelpMan{"getnetworkhashps",
"\nReturns the estimated network hashes per second based on the last n blocks.\n"
"Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n"
"Pass in [height] to estimate the network speed at the time when a certain block was found.\n",
@@ -97,10 +97,12 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
HelpExampleCli("getnetworkhashps", "")
+ HelpExampleRpc("getnetworkhashps", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].get_int() : 120, !request.params[1].isNull() ? request.params[1].get_int() : -1);
+},
+ };
}
static bool GenerateBlock(ChainstateManager& chainman, CBlock& block, uint64_t& max_tries, unsigned int& extra_nonce, uint256& block_hash)
@@ -200,9 +202,9 @@ static bool getScriptFromDescriptor(const std::string& descriptor, CScript& scri
}
}
-static UniValue generatetodescriptor(const JSONRPCRequest& request)
+static RPCHelpMan generatetodescriptor()
{
- RPCHelpMan{
+ return RPCHelpMan{
"generatetodescriptor",
"\nMine blocks immediately to a specified descriptor (before the RPC call returns)\n",
{
@@ -218,9 +220,8 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
},
RPCExamples{
"\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")},
- }
- .Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const int num_blocks{request.params[0].get_int()};
const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
@@ -234,22 +235,25 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
ChainstateManager& chainman = EnsureChainman(request.context);
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
+},
+ };
}
-static UniValue generate(const JSONRPCRequest& request)
+static RPCHelpMan generate()
{
- const std::string help_str{"generate ( nblocks maxtries ) has been replaced by the -generate cli option. Refer to -help for more information."};
+ return RPCHelpMan{"generate", "has been replaced by the -generate cli option. Refer to -help for more information.", {}, {}, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
if (request.fHelp) {
- throw std::runtime_error(help_str);
+ throw std::runtime_error(self.ToString());
} else {
- throw JSONRPCError(RPC_METHOD_NOT_FOUND, help_str);
+ throw JSONRPCError(RPC_METHOD_NOT_FOUND, self.ToString());
}
+ }};
}
-static UniValue generatetoaddress(const JSONRPCRequest& request)
+static RPCHelpMan generatetoaddress()
{
- RPCHelpMan{"generatetoaddress",
+ return RPCHelpMan{"generatetoaddress",
"\nMine blocks immediately to a specified address (before the RPC call returns)\n",
{
{"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
@@ -267,8 +271,8 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
+ "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated bitcoin to with:\n"
+ HelpExampleCli("getnewaddress", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const int num_blocks{request.params[0].get_int()};
const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
@@ -283,11 +287,13 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
CScript coinbase_script = GetScriptForDestination(destination);
return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
+},
+ };
}
-static UniValue generateblock(const JSONRPCRequest& request)
+static RPCHelpMan generateblock()
{
- RPCHelpMan{"generateblock",
+ return RPCHelpMan{"generateblock",
"\nMine a block with a set of ordered transactions immediately to a specified address or descriptor (before the RPC call returns)\n",
{
{"output", RPCArg::Type::STR, RPCArg::Optional::NO, "The address or descriptor to send the newly generated bitcoin to."},
@@ -309,8 +315,8 @@ static UniValue generateblock(const JSONRPCRequest& request)
"\nGenerate a block to myaddress, with txs rawtx and mempool_txid\n"
+ HelpExampleCli("generateblock", R"("myaddress" '["rawtx", "mempool_txid"]')")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const auto address_or_descriptor = request.params[0].get_str();
CScript coinbase_script;
std::string error;
@@ -390,11 +396,13 @@ static UniValue generateblock(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
obj.pushKV("hash", block_hash.GetHex());
return obj;
+},
+ };
}
-static UniValue getmininginfo(const JSONRPCRequest& request)
+static RPCHelpMan getmininginfo()
{
- RPCHelpMan{"getmininginfo",
+ return RPCHelpMan{"getmininginfo",
"\nReturns a json object containing mining-related information.",
{},
RPCResult{
@@ -413,8 +421,8 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
HelpExampleCli("getmininginfo", "")
+ HelpExampleRpc("getmininginfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
const CTxMemPool& mempool = EnsureMemPool(request.context);
@@ -423,18 +431,20 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
obj.pushKV("difficulty", (double)GetDifficulty(::ChainActive().Tip()));
- obj.pushKV("networkhashps", getnetworkhashps(request));
+ obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", Params().NetworkIDString());
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
+},
+ };
}
// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
-static UniValue prioritisetransaction(const JSONRPCRequest& request)
+static RPCHelpMan prioritisetransaction()
{
- RPCHelpMan{"prioritisetransaction",
+ return RPCHelpMan{"prioritisetransaction",
"Accepts the transaction into mined blocks at a higher (or lower) priority\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id."},
@@ -451,8 +461,8 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
+ HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -464,6 +474,8 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
EnsureMemPool(request.context).PrioritiseTransaction(hash, nAmount);
return true;
+},
+ };
}
@@ -495,9 +507,9 @@ static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
return s;
}
-static UniValue getblocktemplate(const JSONRPCRequest& request)
+static RPCHelpMan getblocktemplate()
{
- RPCHelpMan{"getblocktemplate",
+ return RPCHelpMan{"getblocktemplate",
"\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
"It returns data needed to construct a block to work on.\n"
"For full specification, see BIPs 22, 23, 9, and 145:\n"
@@ -511,12 +523,13 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
{"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings",
{
- {"support", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
+ {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
},
},
{"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings",
{
- {"support", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported softfork deployment"},
+ {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"},
+ {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"},
},
},
},
@@ -528,7 +541,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "version", "The preferred block version"},
{RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced",
{
- {RPCResult::Type::STR, "", "rulename"},
+ {RPCResult::Type::STR, "", "name of a rule the client must understand to some extent; see BIP 9 for format"},
}},
{RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments",
{
@@ -536,7 +549,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
}},
{RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
{RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
- {RPCResult::Type::ARR, "", "contents of non-coinbase transactions that should be included in the next block",
+ {RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
{
{RPCResult::Type::OBJ, "", "",
{
@@ -552,15 +565,12 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"},
}},
}},
- {RPCResult::Type::OBJ, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
+ {RPCResult::Type::OBJ_DYN, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
{
- {RPCResult::Type::ELISION, "", ""},
+ {RPCResult::Type::STR_HEX, "key", "values must be in the coinbase (keys may be ignored)"},
}},
{RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"},
- {RPCResult::Type::OBJ, "coinbasetxn", "information for coinbase transaction",
- {
- {RPCResult::Type::ELISION, "", ""},
- }},
+ {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"},
{RPCResult::Type::STR, "target", "The hash target"},
{RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed",
@@ -574,13 +584,14 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
{RPCResult::Type::STR, "bits", "compressed target of next block"},
{RPCResult::Type::NUM, "height", "The height of the next block"},
+ {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"}
}},
RPCExamples{
HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'")
+ HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
std::string strMode = "template";
@@ -888,6 +899,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
class submitblock_StateCatcher final : public CValidationInterface
@@ -908,10 +921,10 @@ protected:
}
};
-static UniValue submitblock(const JSONRPCRequest& request)
+static RPCHelpMan submitblock()
{
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
- RPCHelpMan{"submitblock",
+ return RPCHelpMan{"submitblock",
"\nAttempts to submit new block to network.\n"
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
{
@@ -923,8 +936,8 @@ static UniValue submitblock(const JSONRPCRequest& request)
HelpExampleCli("submitblock", "\"mydata\"")
+ HelpExampleRpc("submitblock", "\"mydata\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
CBlock& block = *blockptr;
if (!DecodeHexBlk(block, request.params[0].get_str())) {
@@ -969,11 +982,13 @@ static UniValue submitblock(const JSONRPCRequest& request)
return "inconclusive";
}
return BIP22ValidationResult(sc->state);
+},
+ };
}
-static UniValue submitheader(const JSONRPCRequest& request)
+static RPCHelpMan submitheader()
{
- RPCHelpMan{"submitheader",
+ return RPCHelpMan{"submitheader",
"\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid."
"\nThrows when the header is invalid.\n",
{
@@ -985,8 +1000,8 @@ static UniValue submitheader(const JSONRPCRequest& request)
HelpExampleCli("submitheader", "\"aabbcc\"") +
HelpExampleRpc("submitheader", "\"aabbcc\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
CBlockHeader h;
if (!DecodeHexBlockHeader(h, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block header decode failed");
@@ -1005,11 +1020,13 @@ static UniValue submitheader(const JSONRPCRequest& request)
throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString());
}
throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason());
+},
+ };
}
-static UniValue estimatesmartfee(const JSONRPCRequest& request)
+static RPCHelpMan estimatesmartfee()
{
- RPCHelpMan{"estimatesmartfee",
+ return RPCHelpMan{"estimatesmartfee",
"\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
"confirmation within conf_target blocks if possible and return the number of blocks\n"
"for which the estimate is valid. Uses virtual transaction size as defined\n"
@@ -1043,8 +1060,8 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("estimatesmartfee", "6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
@@ -1070,11 +1087,13 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
}
result.pushKV("blocks", feeCalc.returnedTarget);
return result;
+},
+ };
}
-static UniValue estimaterawfee(const JSONRPCRequest& request)
+static RPCHelpMan estimaterawfee()
{
- RPCHelpMan{"estimaterawfee",
+ return RPCHelpMan{"estimaterawfee",
"\nWARNING: This interface is unstable and may disappear or change!\n"
"\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
" implementation of fee estimation. The parameters it can be called with\n"
@@ -1126,8 +1145,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("estimaterawfee", "6 0.9")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
@@ -1186,6 +1205,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
result.pushKV(StringForFeeEstimateHorizon(horizon), horizon_result);
}
return result;
+},
+ };
}
void RegisterMiningRPCCommands(CRPCTable &t)
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index e9343b3348..5af4389857 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -197,7 +197,7 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
if (fStateStats) {
if (IsDeprecatedRPCEnabled("banscore")) {
// banscore is deprecated in v0.21 for removal in v0.22
- obj.pushKV("banscore", statestats.nMisbehavior);
+ obj.pushKV("banscore", statestats.m_misbehavior_score);
}
obj.pushKV("synced_headers", statestats.nSyncHeight);
obj.pushKV("synced_blocks", statestats.nCommonHeight);
@@ -414,7 +414,7 @@ static UniValue getnettotals(const JSONRPCRequest& request)
{
{RPCResult::Type::NUM, "totalbytesrecv", "Total bytes received"},
{RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"},
- {RPCResult::Type::NUM_TIME, "timemillis", "Current UNIX time in milliseconds"},
+ {RPCResult::Type::NUM_TIME, "timemillis", "Current " + UNIX_EPOCH_TIME + " in milliseconds"},
{RPCResult::Type::OBJ, "uploadtarget", "",
{
{RPCResult::Type::NUM, "timeframe", "Length of the measuring timeframe in seconds"},
@@ -490,7 +490,9 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
}},
{RPCResult::Type::BOOL, "localrelay", "true if transaction relay is requested from peers"},
{RPCResult::Type::NUM, "timeoffset", "the time offset"},
- {RPCResult::Type::NUM, "connections", "the number of connections"},
+ {RPCResult::Type::NUM, "connections", "the total number of connections"},
+ {RPCResult::Type::NUM, "connections_in", "the number of inbound connections"},
+ {RPCResult::Type::NUM, "connections_out", "the number of outbound connections"},
{RPCResult::Type::BOOL, "networkactive", "whether p2p networking is enabled"},
{RPCResult::Type::ARR, "networks", "information per network",
{
@@ -538,7 +540,9 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.pushKV("timeoffset", GetTimeOffset());
if (node.connman) {
obj.pushKV("networkactive", node.connman->GetNetworkActive());
- obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
+ obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
+ obj.pushKV("connections_in", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_IN));
+ obj.pushKV("connections_out", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_OUT));
}
obj.pushKV("networks", GetNetworksInfo());
obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index d6988ee3ac..1a43ffcc53 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -191,7 +191,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
}
uint256 hash_block;
- const CTransactionRef tx = GetTransaction(blockindex, node.mempool, hash, Params().GetConsensus(), hash_block);
+ const CTransactionRef tx = GetTransaction(blockindex, node.mempool.get(), hash, Params().GetConsensus(), hash_block);
if (!tx) {
std::string errmsg;
if (blockindex) {
@@ -627,7 +627,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
{"txs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The hex strings of partially signed transactions",
{
- {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"},
+ {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A hex-encoded raw transaction"},
},
},
},
@@ -878,6 +878,11 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
{
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
{RPCResult::Type::BOOL, "allowed", "If the mempool allows this tx to be inserted"},
+ {RPCResult::Type::NUM, "vsize", "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"},
+ {RPCResult::Type::OBJ, "fees", "Transaction fees (only present if 'allowed' is true)",
+ {
+ {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
+ }},
{RPCResult::Type::STR, "reject-reason", "Rejection string (only present when 'allowed' is false)"},
}},
}
@@ -924,13 +929,22 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
TxValidationState state;
bool test_accept_res;
+ CAmount fee;
{
LOCK(cs_main);
test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx),
- nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true);
+ nullptr /* plTxnReplaced */, false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true, &fee);
}
result_0.pushKV("allowed", test_accept_res);
- if (!test_accept_res) {
+
+ // Only return the fee and vsize if the transaction would pass ATMP.
+ // These can be used to calculate the feerate.
+ if (test_accept_res) {
+ result_0.pushKV("vsize", virtual_size);
+ UniValue fees(UniValue::VOBJ);
+ fees.pushKV("base", ValueFromAmount(fee));
+ result_0.pushKV("fees", fees);
+ } else {
if (state.IsInvalid()) {
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
result_0.pushKV("reject-reason", "missing-inputs");
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 1031716b4a..cfe4575090 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -21,10 +21,15 @@
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf)
{
- if (inputs_in.isNull() || outputs_in.isNull())
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
+ if (outputs_in.isNull())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
+
+ UniValue inputs;
+ if (inputs_in.isNull())
+ inputs = UniValue::VARR;
+ else
+ inputs = inputs_in.get_array();
- UniValue inputs = inputs_in.get_array();
const bool outputs_is_obj = outputs_in.isObject();
UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 9c8e7fe04a..f32d9abac6 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -261,13 +261,11 @@ CRPCTable::CRPCTable()
}
}
-bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
+void CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
{
- if (IsRPCRunning())
- return false;
+ CHECK_NONFATAL(!IsRPCRunning()); // Only add commands before rpc is running
mapCommands[name].push_back(pcmd);
- return true;
}
bool CRPCTable::removeCommand(const std::string& name, const CRPCCommand* pcmd)
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 6da3e94ea2..b2358ac5b2 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -160,7 +160,7 @@ public:
/**
* Appends a CRPCCommand to the dispatch table.
*
- * Returns false if RPC server is already running (dump concurrency protection).
+ * Precondition: RPC server is not running
*
* Commands with different method names but the same unique_id will
* be considered aliases, and only the first registered method name will
@@ -169,7 +169,7 @@ public:
* between calls based on method name, and aliased commands can also
* register different names, types, and numbers of parameters.
*/
- bool appendCommand(const std::string& name, const CRPCCommand* pcmd);
+ void appendCommand(const std::string& name, const CRPCCommand* pcmd);
bool removeCommand(const std::string& name, const CRPCCommand* pcmd);
};
diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore
index cb4331aa90..ccdef02b29 100644
--- a/src/secp256k1/.gitignore
+++ b/src/secp256k1/.gitignore
@@ -1,9 +1,9 @@
bench_inv
bench_ecdh
bench_ecmult
+bench_schnorrsig
bench_sign
bench_verify
-bench_schnorr_verify
bench_recover
bench_internal
tests
@@ -31,6 +31,8 @@ libtool
*.lo
*.o
*~
+*.log
+*.trs
src/libsecp256k1-config.h
src/libsecp256k1-config.h.in
src/ecmult_static_context.h
diff --git a/src/secp256k1/.travis.yml b/src/secp256k1/.travis.yml
index a6ad6fb27e..e1a88c4051 100644
--- a/src/secp256k1/.travis.yml
+++ b/src/secp256k1/.travis.yml
@@ -17,19 +17,19 @@ compiler:
- gcc
env:
global:
- - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2
+ - WIDEMUL=auto BIGNUM=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2
matrix:
- - SCALAR=32bit RECOVERY=yes
- - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
- - SCALAR=64bit
- - FIELD=64bit RECOVERY=yes
- - FIELD=64bit ENDOMORPHISM=yes
- - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
- - FIELD=64bit ASM=x86_64
- - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
- - FIELD=32bit ENDOMORPHISM=yes
+ - WIDEMUL=int64 RECOVERY=yes
+ - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes
+ - WIDEMUL=int64 ENDOMORPHISM=yes
+ - WIDEMUL=int128
+ - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes
+ - WIDEMUL=int128 ENDOMORPHISM=yes
+ - WIDEMUL=int128 ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes
+ - WIDEMUL=int128 ASM=x86_64
+ - WIDEMUL=int128 ENDOMORPHISM=yes ASM=x86_64
- BIGNUM=no
- - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes
+ - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes
- BIGNUM=no STATICPRECOMPUTATION=no
- BUILD=distcheck CTIMETEST= BENCH=
- CPPFLAGS=-DDETERMINISTIC
@@ -83,6 +83,10 @@ matrix:
- valgrind
- libtool-bin
- libc6-dbg:i386
+ # S390x build (big endian system)
+ - compiler: gcc
+ env: HOST=s390x-unknown-linux-gnu ECDH=yes RECOVERY=yes EXPERIMENTAL=yes CTIMETEST=
+ arch: s390x
# We use this to install macOS dependencies instead of the built in `homebrew` plugin,
# because in xcode earlier than 11 they have a bug requiring updating the system which overall takes ~8 minutes.
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index d8c1c79e8c..023fa6067f 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -34,9 +34,11 @@ noinst_HEADERS += src/field_5x52.h
noinst_HEADERS += src/field_5x52_impl.h
noinst_HEADERS += src/field_5x52_int128_impl.h
noinst_HEADERS += src/field_5x52_asm_impl.h
+noinst_HEADERS += src/assumptions.h
noinst_HEADERS += src/util.h
noinst_HEADERS += src/scratch.h
noinst_HEADERS += src/scratch_impl.h
+noinst_HEADERS += src/selftest.h
noinst_HEADERS += src/testrand.h
noinst_HEADERS += src/testrand_impl.h
noinst_HEADERS += src/hash.h
@@ -99,7 +101,7 @@ if VALGRIND_ENABLED
tests_CPPFLAGS += -DVALGRIND
noinst_PROGRAMS += valgrind_ctime_test
valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c
-valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
+valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_LIBS) $(COMMON_LIB)
endif
if !ENABLE_COVERAGE
tests_CPPFLAGS += -DVERIFY
@@ -152,3 +154,11 @@ endif
if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include
endif
+
+if ENABLE_MODULE_EXTRAKEYS
+include src/modules/extrakeys/Makefile.am.include
+endif
+
+if ENABLE_MODULE_SCHNORRSIG
+include src/modules/schnorrsig/Makefile.am.include
+endif
diff --git a/src/secp256k1/TODO b/src/secp256k1/TODO
deleted file mode 100644
index a300e1c5eb..0000000000
--- a/src/secp256k1/TODO
+++ /dev/null
@@ -1,3 +0,0 @@
-* Unit tests for fieldelem/groupelem, including ones intended to
- trigger fieldelem's boundary cases.
-* Complete constant-time operations for signing/keygen
diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
index 1b2b71e6ab..57595f4499 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -1,8 +1,3 @@
-dnl libsecp25k1 helper checks
-AC_DEFUN([SECP_INT128_CHECK],[
-has_int128=$ac_cv_type___int128
-])
-
dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell.
AC_DEFUN([SECP_64BIT_ASM_CHECK],[
AC_MSG_CHECKING(for x86_64 assembly availability)
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index 6021b760b5..6fe8984f4d 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -136,20 +136,28 @@ AC_ARG_ENABLE(module_recovery,
[enable_module_recovery=$enableval],
[enable_module_recovery=no])
+AC_ARG_ENABLE(module_extrakeys,
+ AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module (experimental)]),
+ [enable_module_extrakeys=$enableval],
+ [enable_module_extrakeys=no])
+
+AC_ARG_ENABLE(module_schnorrsig,
+ AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]),
+ [enable_module_schnorrsig=$enableval],
+ [enable_module_schnorrsig=no])
+
AC_ARG_ENABLE(external_default_callbacks,
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]),
[use_external_default_callbacks=$enableval],
[use_external_default_callbacks=no])
-AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
-[finite field implementation to use [default=auto]])],[req_field=$withval], [req_field=auto])
+dnl Test-only override of the (autodetected by the C code) "widemul" setting.
+dnl Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default).
+AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto])
AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
[bignum implementation to use [default=auto]])],[req_bignum=$withval], [req_bignum=auto])
-AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto],
-[scalar implementation to use [default=auto]])],[req_scalar=$withval], [req_scalar=auto])
-
AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto],
[assembly optimizations to use (experimental: arm) [default=auto]])],[req_asm=$withval], [req_asm=auto])
@@ -170,8 +178,6 @@ AC_ARG_WITH([ecmult-gen-precision], [AS_HELP_STRING([--with-ecmult-gen-precision
)],
[req_ecmult_gen_precision=$withval], [req_ecmult_gen_precision=auto])
-AC_CHECK_TYPES([__int128])
-
AC_CHECK_HEADER([valgrind/memcheck.h], [enable_valgrind=yes], [enable_valgrind=no], [])
AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
@@ -265,63 +271,6 @@ else
esac
fi
-if test x"$req_field" = x"auto"; then
- if test x"set_asm" = x"x86_64"; then
- set_field=64bit
- fi
- if test x"$set_field" = x; then
- SECP_INT128_CHECK
- if test x"$has_int128" = x"yes"; then
- set_field=64bit
- fi
- fi
- if test x"$set_field" = x; then
- set_field=32bit
- fi
-else
- set_field=$req_field
- case $set_field in
- 64bit)
- if test x"$set_asm" != x"x86_64"; then
- SECP_INT128_CHECK
- if test x"$has_int128" != x"yes"; then
- AC_MSG_ERROR([64bit field explicitly requested but neither __int128 support or x86_64 assembly available])
- fi
- fi
- ;;
- 32bit)
- ;;
- *)
- AC_MSG_ERROR([invalid field implementation selection])
- ;;
- esac
-fi
-
-if test x"$req_scalar" = x"auto"; then
- SECP_INT128_CHECK
- if test x"$has_int128" = x"yes"; then
- set_scalar=64bit
- fi
- if test x"$set_scalar" = x; then
- set_scalar=32bit
- fi
-else
- set_scalar=$req_scalar
- case $set_scalar in
- 64bit)
- SECP_INT128_CHECK
- if test x"$has_int128" != x"yes"; then
- AC_MSG_ERROR([64bit scalar explicitly requested but __int128 support not available])
- fi
- ;;
- 32bit)
- ;;
- *)
- AC_MSG_ERROR([invalid scalar implementation selected])
- ;;
- esac
-fi
-
if test x"$req_bignum" = x"auto"; then
SECP_GMP_CHECK
if test x"$has_gmp" = x"yes"; then
@@ -365,16 +314,18 @@ no)
;;
esac
-# select field implementation
-case $set_field in
-64bit)
- AC_DEFINE(USE_FIELD_5X52, 1, [Define this symbol to use the FIELD_5X52 implementation])
+# select wide multiplication implementation
+case $set_widemul in
+int128)
+ AC_DEFINE(USE_FORCE_WIDEMUL_INT128, 1, [Define this symbol to force the use of the (unsigned) __int128 based wide multiplication implementation])
+ ;;
+int64)
+ AC_DEFINE(USE_FORCE_WIDEMUL_INT64, 1, [Define this symbol to force the use of the (u)int64_t based wide multiplication implementation])
;;
-32bit)
- AC_DEFINE(USE_FIELD_10X26, 1, [Define this symbol to use the FIELD_10X26 implementation])
+auto)
;;
*)
- AC_MSG_ERROR([invalid field implementation])
+ AC_MSG_ERROR([invalid wide multiplication implementation])
;;
esac
@@ -396,19 +347,6 @@ no)
;;
esac
-#select scalar implementation
-case $set_scalar in
-64bit)
- AC_DEFINE(USE_SCALAR_4X64, 1, [Define this symbol to use the 4x64 scalar implementation])
- ;;
-32bit)
- AC_DEFINE(USE_SCALAR_8X32, 1, [Define this symbol to use the 8x32 scalar implementation])
- ;;
-*)
- AC_MSG_ERROR([invalid scalar implementation])
- ;;
-esac
-
#set ecmult window size
if test x"$req_ecmult_window" = x"auto"; then
set_ecmult_window=15
@@ -493,7 +431,16 @@ if test x"$enable_module_recovery" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
fi
-AC_C_BIGENDIAN()
+if test x"$enable_module_schnorrsig" = x"yes"; then
+ AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module])
+ enable_module_extrakeys=yes
+fi
+
+# Test if extrakeys is set after the schnorrsig module to allow the schnorrsig
+# module to set enable_module_extrakeys=yes
+if test x"$enable_module_extrakeys" = x"yes"; then
+ AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module])
+fi
if test x"$use_external_asm" = x"yes"; then
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
@@ -508,11 +455,19 @@ if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([WARNING: experimental build])
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
+ AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys])
+ AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig])
AC_MSG_NOTICE([******])
else
if test x"$enable_module_ecdh" = x"yes"; then
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
fi
+ if test x"$enable_module_extrakeys" = x"yes"; then
+ AC_MSG_ERROR([extrakeys module is experimental. Use --enable-experimental to allow.])
+ fi
+ if test x"$enable_module_schnorrsig" = x"yes"; then
+ AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.])
+ fi
if test x"$set_asm" = x"arm"; then
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
fi
@@ -531,6 +486,8 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
+AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
+AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
@@ -550,13 +507,17 @@ echo " with benchmarks = $use_benchmark"
echo " with coverage = $enable_coverage"
echo " module ecdh = $enable_module_ecdh"
echo " module recovery = $enable_module_recovery"
+echo " module extrakeys = $enable_module_extrakeys"
+echo " module schnorrsig = $enable_module_schnorrsig"
echo
echo " asm = $set_asm"
echo " bignum = $set_bignum"
-echo " field = $set_field"
-echo " scalar = $set_scalar"
echo " ecmult window size = $set_ecmult_window"
echo " ecmult gen prec. bits = $set_ecmult_gen_precision"
+dnl Hide test-only options unless they're used.
+if test x"$set_widemul" != xauto; then
+echo " wide multiplication = $set_widemul"
+fi
echo
echo " valgrind = $enable_valgrind"
echo " CC = $CC"
diff --git a/src/secp256k1/contrib/lax_der_parsing.c b/src/secp256k1/contrib/lax_der_parsing.c
index e177a0562d..f71db4b535 100644
--- a/src/secp256k1/contrib/lax_der_parsing.c
+++ b/src/secp256k1/contrib/lax_der_parsing.c
@@ -112,7 +112,6 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
return 0;
}
spos = pos;
- pos += slen;
/* Ignore leading zeroes in R */
while (rlen > 0 && input[rpos] == 0) {
diff --git a/src/secp256k1/contrib/travis.sh b/src/secp256k1/contrib/travis.sh
index 3909d16a27..b0b55b44b8 100755
--- a/src/secp256k1/contrib/travis.sh
+++ b/src/secp256k1/contrib/travis.sh
@@ -3,10 +3,6 @@
set -e
set -x
-if [ -n "$HOST" ]
-then
- export USE_HOST="--host=$HOST"
-fi
if [ "$HOST" = "i686-linux-gnu" ]
then
export CC="$CC -m32"
@@ -18,9 +14,11 @@ fi
./configure \
--enable-experimental="$EXPERIMENTAL" --enable-endomorphism="$ENDOMORPHISM" \
- --with-field="$FIELD" --with-bignum="$BIGNUM" --with-asm="$ASM" --with-scalar="$SCALAR" \
+ --with-test-override-wide-multiply="$WIDEMUL" --with-bignum="$BIGNUM" --with-asm="$ASM" \
--enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
- --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" "$EXTRAFLAGS" "$USE_HOST"
+ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
+ --enable-module-schnorrsig="$SCHNORRSIG" \
+ --host="$HOST" $EXTRAFLAGS
if [ -n "$BUILD" ]
then
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index 2ba2dca388..2178c8e2d6 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -134,7 +134,7 @@ typedef int (*secp256k1_nonce_function)(
# else
# define SECP256K1_API
# endif
-# elif defined(__GNUC__) && defined(SECP256K1_BUILD)
+# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD)
# define SECP256K1_API __attribute__ ((visibility ("default")))
# else
# define SECP256K1_API
diff --git a/src/secp256k1/include/secp256k1_extrakeys.h b/src/secp256k1/include/secp256k1_extrakeys.h
new file mode 100644
index 0000000000..0c5dff2c94
--- /dev/null
+++ b/src/secp256k1/include/secp256k1_extrakeys.h
@@ -0,0 +1,236 @@
+#ifndef SECP256K1_EXTRAKEYS_H
+#define SECP256K1_EXTRAKEYS_H
+
+#include "secp256k1.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Opaque data structure that holds a parsed and valid "x-only" public key.
+ * An x-only pubkey encodes a point whose Y coordinate is even. It is
+ * serialized using only its X coordinate (32 bytes). See BIP-340 for more
+ * information about x-only pubkeys.
+ *
+ * The exact representation of data inside is implementation defined and not
+ * guaranteed to be portable between different platforms or versions. It is
+ * however guaranteed to be 64 bytes in size, and can be safely copied/moved.
+ * If you need to convert to a format suitable for storage, transmission, or
+ * comparison, use secp256k1_xonly_pubkey_serialize and
+ * secp256k1_xonly_pubkey_parse.
+ */
+typedef struct {
+ unsigned char data[64];
+} secp256k1_xonly_pubkey;
+
+/** Opaque data structure that holds a keypair consisting of a secret and a
+ * public key.
+ *
+ * The exact representation of data inside is implementation defined and not
+ * guaranteed to be portable between different platforms or versions. It is
+ * however guaranteed to be 96 bytes in size, and can be safely copied/moved.
+ */
+typedef struct {
+ unsigned char data[96];
+} secp256k1_keypair;
+
+/** Parse a 32-byte sequence into a xonly_pubkey object.
+ *
+ * Returns: 1 if the public key was fully valid.
+ * 0 if the public key could not be parsed or is invalid.
+ *
+ * Args: ctx: a secp256k1 context object (cannot be NULL).
+ * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a
+ * parsed version of input. If not, it's set to an invalid value.
+ * (cannot be NULL).
+ * In: input32: pointer to a serialized xonly_pubkey (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_parse(
+ const secp256k1_context* ctx,
+ secp256k1_xonly_pubkey* pubkey,
+ const unsigned char *input32
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Serialize an xonly_pubkey object into a 32-byte sequence.
+ *
+ * Returns: 1 always.
+ *
+ * Args: ctx: a secp256k1 context object (cannot be NULL).
+ * Out: output32: a pointer to a 32-byte array to place the serialized key in
+ * (cannot be NULL).
+ * In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an
+ * initialized public key (cannot be NULL).
+ */
+SECP256K1_API int secp256k1_xonly_pubkey_serialize(
+ const secp256k1_context* ctx,
+ unsigned char *output32,
+ const secp256k1_xonly_pubkey* pubkey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey.
+ *
+ * Returns: 1 if the public key was successfully converted
+ * 0 otherwise
+ *
+ * Args: ctx: pointer to a context object (cannot be NULL)
+ * Out: xonly_pubkey: pointer to an x-only public key object for placing the
+ * converted public key (cannot be NULL)
+ * pk_parity: pointer to an integer that will be set to 1 if the point
+ * encoded by xonly_pubkey is the negation of the pubkey and
+ * set to 0 otherwise. (can be NULL)
+ * In: pubkey: pointer to a public key that is converted (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubkey(
+ const secp256k1_context* ctx,
+ secp256k1_xonly_pubkey *xonly_pubkey,
+ int *pk_parity,
+ const secp256k1_pubkey *pubkey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
+
+/** Tweak an x-only public key by adding the generator multiplied with tweak32
+ * to it.
+ *
+ * Note that the resulting point can not in general be represented by an x-only
+ * pubkey because it may have an odd Y coordinate. Instead, the output_pubkey
+ * is a normal secp256k1_pubkey.
+ *
+ * Returns: 0 if the arguments are invalid or the resulting public key would be
+ * invalid (only when the tweak is the negation of the corresponding
+ * secret key). 1 otherwise.
+ *
+ * Args: ctx: pointer to a context object initialized for verification
+ * (cannot be NULL)
+ * Out: output_pubkey: pointer to a public key to store the result. Will be set
+ * to an invalid value if this function returns 0 (cannot
+ * be NULL)
+ * In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to.
+ * (cannot be NULL).
+ * tweak32: pointer to a 32-byte tweak. If the tweak is invalid
+ * according to secp256k1_ec_seckey_verify, this function
+ * returns 0. For uniformly random 32-byte arrays the
+ * chance of being invalid is negligible (around 1 in
+ * 2^128) (cannot be NULL).
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add(
+ const secp256k1_context* ctx,
+ secp256k1_pubkey *output_pubkey,
+ const secp256k1_xonly_pubkey *internal_pubkey,
+ const unsigned char *tweak32
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+
+/** Checks that a tweaked pubkey is the result of calling
+ * secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32.
+ *
+ * The tweaked pubkey is represented by its 32-byte x-only serialization and
+ * its pk_parity, which can both be obtained by converting the result of
+ * tweak_add to a secp256k1_xonly_pubkey.
+ *
+ * Note that this alone does _not_ verify that the tweaked pubkey is a
+ * commitment. If the tweak is not chosen in a specific way, the tweaked pubkey
+ * can easily be the result of a different internal_pubkey and tweak.
+ *
+ * Returns: 0 if the arguments are invalid or the tweaked pubkey is not the
+ * result of tweaking the internal_pubkey with tweak32. 1 otherwise.
+ * Args: ctx: pointer to a context object initialized for verification
+ * (cannot be NULL)
+ * In: tweaked_pubkey32: pointer to a serialized xonly_pubkey (cannot be NULL)
+ * tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization
+ * is passed in as tweaked_pubkey32). This must match the
+ * pk_parity value that is returned when calling
+ * secp256k1_xonly_pubkey with the tweaked pubkey, or
+ * this function will fail.
+ * internal_pubkey: pointer to an x-only public key object to apply the
+ * tweak to (cannot be NULL)
+ * tweak32: pointer to a 32-byte tweak (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_check(
+ const secp256k1_context* ctx,
+ const unsigned char *tweaked_pubkey32,
+ int tweaked_pk_parity,
+ const secp256k1_xonly_pubkey *internal_pubkey,
+ const unsigned char *tweak32
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
+
+/** Compute the keypair for a secret key.
+ *
+ * Returns: 1: secret was valid, keypair is ready to use
+ * 0: secret was invalid, try again with a different secret
+ * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
+ * Out: keypair: pointer to the created keypair (cannot be NULL)
+ * In: seckey: pointer to a 32-byte secret key (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create(
+ const secp256k1_context* ctx,
+ secp256k1_keypair *keypair,
+ const unsigned char *seckey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Get the public key from a keypair.
+ *
+ * Returns: 0 if the arguments are invalid. 1 otherwise.
+ * Args: ctx: pointer to a context object (cannot be NULL)
+ * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to
+ * the keypair public key. If not, it's set to an invalid value.
+ * (cannot be NULL)
+ * In: keypair: pointer to a keypair (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub(
+ const secp256k1_context* ctx,
+ secp256k1_pubkey *pubkey,
+ const secp256k1_keypair *keypair
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Get the x-only public key from a keypair.
+ *
+ * This is the same as calling secp256k1_keypair_pub and then
+ * secp256k1_xonly_pubkey_from_pubkey.
+ *
+ * Returns: 0 if the arguments are invalid. 1 otherwise.
+ * Args: ctx: pointer to a context object (cannot be NULL)
+ * Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set
+ * to the keypair public key after converting it to an
+ * xonly_pubkey. If not, it's set to an invalid value (cannot be
+ * NULL).
+ * pk_parity: pointer to an integer that will be set to the pk_parity
+ * argument of secp256k1_xonly_pubkey_from_pubkey (can be NULL).
+ * In: keypair: pointer to a keypair (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub(
+ const secp256k1_context* ctx,
+ secp256k1_xonly_pubkey *pubkey,
+ int *pk_parity,
+ const secp256k1_keypair *keypair
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
+
+/** Tweak a keypair by adding tweak32 to the secret key and updating the public
+ * key accordingly.
+ *
+ * Calling this function and then secp256k1_keypair_pub results in the same
+ * public key as calling secp256k1_keypair_xonly_pub and then
+ * secp256k1_xonly_pubkey_tweak_add.
+ *
+ * Returns: 0 if the arguments are invalid or the resulting keypair would be
+ * invalid (only when the tweak is the negation of the keypair's
+ * secret key). 1 otherwise.
+ *
+ * Args: ctx: pointer to a context object initialized for verification
+ * (cannot be NULL)
+ * In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to
+ * an invalid value if this function returns 0 (cannot be
+ * NULL).
+ * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according
+ * to secp256k1_ec_seckey_verify, this function returns 0. For
+ * uniformly random 32-byte arrays the chance of being invalid
+ * is negligible (around 1 in 2^128) (cannot be NULL).
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add(
+ const secp256k1_context* ctx,
+ secp256k1_keypair *keypair,
+ const unsigned char *tweak32
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SECP256K1_EXTRAKEYS_H */
diff --git a/src/secp256k1/include/secp256k1_schnorrsig.h b/src/secp256k1/include/secp256k1_schnorrsig.h
new file mode 100644
index 0000000000..0150cd3395
--- /dev/null
+++ b/src/secp256k1/include/secp256k1_schnorrsig.h
@@ -0,0 +1,111 @@
+#ifndef SECP256K1_SCHNORRSIG_H
+#define SECP256K1_SCHNORRSIG_H
+
+#include "secp256k1.h"
+#include "secp256k1_extrakeys.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This module implements a variant of Schnorr signatures compliant with
+ * Bitcoin Improvement Proposal 340 "Schnorr Signatures for secp256k1"
+ * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
+ */
+
+/** A pointer to a function to deterministically generate a nonce.
+ *
+ * Same as secp256k1_nonce function with the exception of accepting an
+ * additional pubkey argument and not requiring an attempt argument. The pubkey
+ * argument can protect signature schemes with key-prefixed challenge hash
+ * inputs against reusing the nonce when signing with the wrong precomputed
+ * pubkey.
+ *
+ * Returns: 1 if a nonce was successfully generated. 0 will cause signing to
+ * return an error.
+ * Out: nonce32: pointer to a 32-byte array to be filled by the function.
+ * In: msg32: the 32-byte message hash being verified (will not be NULL)
+ * key32: pointer to a 32-byte secret key (will not be NULL)
+ * xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32
+ * (will not be NULL)
+ * algo16: pointer to a 16-byte array describing the signature
+ * algorithm (will not be NULL).
+ * data: Arbitrary data pointer that is passed through.
+ *
+ * Except for test cases, this function should compute some cryptographic hash of
+ * the message, the key, the pubkey, the algorithm description, and data.
+ */
+typedef int (*secp256k1_nonce_function_hardened)(
+ unsigned char *nonce32,
+ const unsigned char *msg32,
+ const unsigned char *key32,
+ const unsigned char *xonly_pk32,
+ const unsigned char *algo16,
+ void *data
+);
+
+/** An implementation of the nonce generation function as defined in Bitcoin
+ * Improvement Proposal 340 "Schnorr Signatures for secp256k1"
+ * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
+ *
+ * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
+ * auxiliary random data as defined in BIP-340. If the data pointer is NULL,
+ * schnorrsig_sign does not produce BIP-340 compliant signatures. The algo16
+ * argument must be non-NULL, otherwise the function will fail and return 0.
+ * The hash will be tagged with algo16 after removing all terminating null
+ * bytes. Therefore, to create BIP-340 compliant signatures, algo16 must be set
+ * to "BIP0340/nonce\0\0\0"
+ */
+SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340;
+
+/** Create a Schnorr signature.
+ *
+ * Does _not_ strictly follow BIP-340 because it does not verify the resulting
+ * signature. Instead, you can manually use secp256k1_schnorrsig_verify and
+ * abort if it fails.
+ *
+ * Otherwise BIP-340 compliant if the noncefp argument is NULL or
+ * secp256k1_nonce_function_bip340 and the ndata argument is 32-byte auxiliary
+ * randomness.
+ *
+ * Returns 1 on success, 0 on failure.
+ * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
+ * Out: sig64: pointer to a 64-byte array to store the serialized signature (cannot be NULL)
+ * In: msg32: the 32-byte message being signed (cannot be NULL)
+ * keypair: pointer to an initialized keypair (cannot be NULL)
+ * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bip340 is used
+ * ndata: pointer to arbitrary data used by the nonce generation
+ * function (can be NULL). If it is non-NULL and
+ * secp256k1_nonce_function_bip340 is used, then ndata must be a
+ * pointer to 32-byte auxiliary randomness as per BIP-340.
+ */
+SECP256K1_API int secp256k1_schnorrsig_sign(
+ const secp256k1_context* ctx,
+ unsigned char *sig64,
+ const unsigned char *msg32,
+ const secp256k1_keypair *keypair,
+ secp256k1_nonce_function_hardened noncefp,
+ void *ndata
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+
+/** Verify a Schnorr signature.
+ *
+ * Returns: 1: correct signature
+ * 0: incorrect signature
+ * Args: ctx: a secp256k1 context object, initialized for verification.
+ * In: sig64: pointer to the 64-byte signature to verify (cannot be NULL)
+ * msg32: the 32-byte message being verified (cannot be NULL)
+ * pubkey: pointer to an x-only public key to verify with (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
+ const secp256k1_context* ctx,
+ const unsigned char *sig64,
+ const unsigned char *msg32,
+ const secp256k1_xonly_pubkey *pubkey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SECP256K1_SCHNORRSIG_H */
diff --git a/src/secp256k1/src/assumptions.h b/src/secp256k1/src/assumptions.h
new file mode 100644
index 0000000000..f9d4e8e793
--- /dev/null
+++ b/src/secp256k1/src/assumptions.h
@@ -0,0 +1,74 @@
+/**********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_ASSUMPTIONS_H
+#define SECP256K1_ASSUMPTIONS_H
+
+#include "util.h"
+
+/* This library, like most software, relies on a number of compiler implementation defined (but not undefined)
+ behaviours. Although the behaviours we require are essentially universal we test them specifically here to
+ reduce the odds of experiencing an unwelcome surprise.
+*/
+
+struct secp256k1_assumption_checker {
+ /* This uses a trick to implement a static assertion in C89: a type with an array of negative size is not
+ allowed. */
+ int dummy_array[(
+ /* Bytes are 8 bits. */
+ CHAR_BIT == 8 &&
+
+ /* Conversions from unsigned to signed outside of the bounds of the signed type are
+ implementation-defined. Verify that they function as reinterpreting the lower
+ bits of the input in two's complement notation. Do this for conversions:
+ - from uint(N)_t to int(N)_t with negative result
+ - from uint(2N)_t to int(N)_t with negative result
+ - from int(2N)_t to int(N)_t with negative result
+ - from int(2N)_t to int(N)_t with positive result */
+
+ /* To int8_t. */
+ ((int8_t)(uint8_t)0xAB == (int8_t)-(int8_t)0x55) &&
+ ((int8_t)(uint16_t)0xABCD == (int8_t)-(int8_t)0x33) &&
+ ((int8_t)(int16_t)(uint16_t)0xCDEF == (int8_t)(uint8_t)0xEF) &&
+ ((int8_t)(int16_t)(uint16_t)0x9234 == (int8_t)(uint8_t)0x34) &&
+
+ /* To int16_t. */
+ ((int16_t)(uint16_t)0xBCDE == (int16_t)-(int16_t)0x4322) &&
+ ((int16_t)(uint32_t)0xA1B2C3D4 == (int16_t)-(int16_t)0x3C2C) &&
+ ((int16_t)(int32_t)(uint32_t)0xC1D2E3F4 == (int16_t)(uint16_t)0xE3F4) &&
+ ((int16_t)(int32_t)(uint32_t)0x92345678 == (int16_t)(uint16_t)0x5678) &&
+
+ /* To int32_t. */
+ ((int32_t)(uint32_t)0xB2C3D4E5 == (int32_t)-(int32_t)0x4D3C2B1B) &&
+ ((int32_t)(uint64_t)0xA123B456C789D012ULL == (int32_t)-(int32_t)0x38762FEE) &&
+ ((int32_t)(int64_t)(uint64_t)0xC1D2E3F4A5B6C7D8ULL == (int32_t)(uint32_t)0xA5B6C7D8) &&
+ ((int32_t)(int64_t)(uint64_t)0xABCDEF0123456789ULL == (int32_t)(uint32_t)0x23456789) &&
+
+ /* To int64_t. */
+ ((int64_t)(uint64_t)0xB123C456D789E012ULL == (int64_t)-(int64_t)0x4EDC3BA928761FEEULL) &&
+#if defined(SECP256K1_WIDEMUL_INT128)
+ ((int64_t)(((uint128_t)0xA1234567B8901234ULL << 64) + 0xC5678901D2345678ULL) == (int64_t)-(int64_t)0x3A9876FE2DCBA988ULL) &&
+ (((int64_t)(int128_t)(((uint128_t)0xB1C2D3E4F5A6B7C8ULL << 64) + 0xD9E0F1A2B3C4D5E6ULL)) == (int64_t)(uint64_t)0xD9E0F1A2B3C4D5E6ULL) &&
+ (((int64_t)(int128_t)(((uint128_t)0xABCDEF0123456789ULL << 64) + 0x0123456789ABCDEFULL)) == (int64_t)(uint64_t)0x0123456789ABCDEFULL) &&
+
+ /* To int128_t. */
+ ((int128_t)(((uint128_t)0xB1234567C8901234ULL << 64) + 0xD5678901E2345678ULL) == (int128_t)(-(int128_t)0x8E1648B3F50E80DCULL * 0x8E1648B3F50E80DDULL + 0x5EA688D5482F9464ULL)) &&
+#endif
+
+ /* Right shift on negative signed values is implementation defined. Verify that it
+ acts as a right shift in two's complement with sign extension (i.e duplicating
+ the top bit into newly added bits). */
+ ((((int8_t)0xE8) >> 2) == (int8_t)(uint8_t)0xFA) &&
+ ((((int16_t)0xE9AC) >> 4) == (int16_t)(uint16_t)0xFE9A) &&
+ ((((int32_t)0x937C918A) >> 9) == (int32_t)(uint32_t)0xFFC9BE48) &&
+ ((((int64_t)0xA8B72231DF9CF4B9ULL) >> 19) == (int64_t)(uint64_t)0xFFFFF516E4463BF3ULL) &&
+#if defined(SECP256K1_WIDEMUL_INT128)
+ ((((int128_t)(((uint128_t)0xCD833A65684A0DBCULL << 64) + 0xB349312F71EA7637ULL)) >> 39) == (int128_t)(((uint128_t)0xFFFFFFFFFF9B0674ULL << 64) + 0xCAD0941B79669262ULL)) &&
+#endif
+ 1) * 2 - 1];
+};
+
+#endif /* SECP256K1_ASSUMPTIONS_H */
diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h
index e9be39d4ca..83dbe6f25b 100644
--- a/src/secp256k1/src/basic-config.h
+++ b/src/secp256k1/src/basic-config.h
@@ -14,23 +14,20 @@
#undef USE_ENDOMORPHISM
#undef USE_EXTERNAL_ASM
#undef USE_EXTERNAL_DEFAULT_CALLBACKS
-#undef USE_FIELD_10X26
-#undef USE_FIELD_5X52
#undef USE_FIELD_INV_BUILTIN
#undef USE_FIELD_INV_NUM
#undef USE_NUM_GMP
#undef USE_NUM_NONE
-#undef USE_SCALAR_4X64
-#undef USE_SCALAR_8X32
#undef USE_SCALAR_INV_BUILTIN
#undef USE_SCALAR_INV_NUM
+#undef USE_FORCE_WIDEMUL_INT64
+#undef USE_FORCE_WIDEMUL_INT128
#undef ECMULT_WINDOW_SIZE
#define USE_NUM_NONE 1
#define USE_FIELD_INV_BUILTIN 1
#define USE_SCALAR_INV_BUILTIN 1
-#define USE_FIELD_10X26 1
-#define USE_SCALAR_8X32 1
+#define USE_WIDEMUL_64 1
#define ECMULT_WINDOW_SIZE 15
#endif /* USE_BASIC_CONFIG */
diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c
index 20759127d3..9687fe4482 100644
--- a/src/secp256k1/src/bench_internal.c
+++ b/src/secp256k1/src/bench_internal.c
@@ -7,6 +7,7 @@
#include "include/secp256k1.h"
+#include "assumptions.h"
#include "util.h"
#include "hash_impl.h"
#include "num_impl.h"
@@ -19,10 +20,10 @@
#include "secp256k1.c"
typedef struct {
- secp256k1_scalar scalar_x, scalar_y;
- secp256k1_fe fe_x, fe_y;
- secp256k1_ge ge_x, ge_y;
- secp256k1_gej gej_x, gej_y;
+ secp256k1_scalar scalar[2];
+ secp256k1_fe fe[4];
+ secp256k1_ge ge[2];
+ secp256k1_gej gej[2];
unsigned char data[64];
int wnaf[256];
} bench_inv;
@@ -30,30 +31,53 @@ typedef struct {
void bench_setup(void* arg) {
bench_inv *data = (bench_inv*)arg;
- static const unsigned char init_x[32] = {
- 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13,
- 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35,
- 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59,
- 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83
+ static const unsigned char init[4][32] = {
+ /* Initializer for scalar[0], fe[0], first half of data, the X coordinate of ge[0],
+ and the (implied affine) X coordinate of gej[0]. */
+ {
+ 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13,
+ 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35,
+ 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59,
+ 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83
+ },
+ /* Initializer for scalar[1], fe[1], first half of data, the X coordinate of ge[1],
+ and the (implied affine) X coordinate of gej[1]. */
+ {
+ 0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83,
+ 0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5,
+ 0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9,
+ 0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3
+ },
+ /* Initializer for fe[2] and the Z coordinate of gej[0]. */
+ {
+ 0x3d, 0x2d, 0xef, 0xf4, 0x25, 0x98, 0x4f, 0x5d,
+ 0xe2, 0xca, 0x5f, 0x41, 0x3f, 0x3f, 0xce, 0x44,
+ 0xaa, 0x2c, 0x53, 0x8a, 0xc6, 0x59, 0x1f, 0x38,
+ 0x38, 0x23, 0xe4, 0x11, 0x27, 0xc6, 0xa0, 0xe7
+ },
+ /* Initializer for fe[3] and the Z coordinate of gej[1]. */
+ {
+ 0xbd, 0x21, 0xa5, 0xe1, 0x13, 0x50, 0x73, 0x2e,
+ 0x52, 0x98, 0xc8, 0x9e, 0xab, 0x00, 0xa2, 0x68,
+ 0x43, 0xf5, 0xd7, 0x49, 0x80, 0x72, 0xa7, 0xf3,
+ 0xd7, 0x60, 0xe6, 0xab, 0x90, 0x92, 0xdf, 0xc5
+ }
};
- static const unsigned char init_y[32] = {
- 0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83,
- 0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5,
- 0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9,
- 0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3
- };
-
- secp256k1_scalar_set_b32(&data->scalar_x, init_x, NULL);
- secp256k1_scalar_set_b32(&data->scalar_y, init_y, NULL);
- secp256k1_fe_set_b32(&data->fe_x, init_x);
- secp256k1_fe_set_b32(&data->fe_y, init_y);
- CHECK(secp256k1_ge_set_xo_var(&data->ge_x, &data->fe_x, 0));
- CHECK(secp256k1_ge_set_xo_var(&data->ge_y, &data->fe_y, 1));
- secp256k1_gej_set_ge(&data->gej_x, &data->ge_x);
- secp256k1_gej_set_ge(&data->gej_y, &data->ge_y);
- memcpy(data->data, init_x, 32);
- memcpy(data->data + 32, init_y, 32);
+ secp256k1_scalar_set_b32(&data->scalar[0], init[0], NULL);
+ secp256k1_scalar_set_b32(&data->scalar[1], init[1], NULL);
+ secp256k1_fe_set_b32(&data->fe[0], init[0]);
+ secp256k1_fe_set_b32(&data->fe[1], init[1]);
+ secp256k1_fe_set_b32(&data->fe[2], init[2]);
+ secp256k1_fe_set_b32(&data->fe[3], init[3]);
+ CHECK(secp256k1_ge_set_xo_var(&data->ge[0], &data->fe[0], 0));
+ CHECK(secp256k1_ge_set_xo_var(&data->ge[1], &data->fe[1], 1));
+ secp256k1_gej_set_ge(&data->gej[0], &data->ge[0]);
+ secp256k1_gej_rescale(&data->gej[0], &data->fe[2]);
+ secp256k1_gej_set_ge(&data->gej[1], &data->ge[1]);
+ secp256k1_gej_rescale(&data->gej[1], &data->fe[3]);
+ memcpy(data->data, init[0], 32);
+ memcpy(data->data + 32, init[1], 32);
}
void bench_scalar_add(void* arg, int iters) {
@@ -61,7 +85,7 @@ void bench_scalar_add(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(j <= iters);
}
@@ -71,7 +95,7 @@ void bench_scalar_negate(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x);
+ secp256k1_scalar_negate(&data->scalar[0], &data->scalar[0]);
}
}
@@ -80,7 +104,7 @@ void bench_scalar_sqr(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x);
+ secp256k1_scalar_sqr(&data->scalar[0], &data->scalar[0]);
}
}
@@ -89,7 +113,7 @@ void bench_scalar_mul(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ secp256k1_scalar_mul(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
}
@@ -99,8 +123,8 @@ void bench_scalar_split(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_split_lambda(&data->scalar_x, &data->scalar_y, &data->scalar_x);
- j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ secp256k1_scalar_split_lambda(&data->scalar[0], &data->scalar[1], &data->scalar[0]);
+ j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(j <= iters);
}
@@ -111,8 +135,8 @@ void bench_scalar_inverse(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x);
- j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ secp256k1_scalar_inverse(&data->scalar[0], &data->scalar[0]);
+ j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(j <= iters);
}
@@ -122,8 +146,8 @@ void bench_scalar_inverse_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x);
- j += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ secp256k1_scalar_inverse_var(&data->scalar[0], &data->scalar[0]);
+ j += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(j <= iters);
}
@@ -133,7 +157,7 @@ void bench_field_normalize(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_normalize(&data->fe_x);
+ secp256k1_fe_normalize(&data->fe[0]);
}
}
@@ -142,7 +166,7 @@ void bench_field_normalize_weak(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_normalize_weak(&data->fe_x);
+ secp256k1_fe_normalize_weak(&data->fe[0]);
}
}
@@ -151,7 +175,7 @@ void bench_field_mul(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y);
+ secp256k1_fe_mul(&data->fe[0], &data->fe[0], &data->fe[1]);
}
}
@@ -160,7 +184,7 @@ void bench_field_sqr(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_sqr(&data->fe_x, &data->fe_x);
+ secp256k1_fe_sqr(&data->fe[0], &data->fe[0]);
}
}
@@ -169,8 +193,8 @@ void bench_field_inverse(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_inv(&data->fe_x, &data->fe_x);
- secp256k1_fe_add(&data->fe_x, &data->fe_y);
+ secp256k1_fe_inv(&data->fe[0], &data->fe[0]);
+ secp256k1_fe_add(&data->fe[0], &data->fe[1]);
}
}
@@ -179,8 +203,8 @@ void bench_field_inverse_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_fe_inv_var(&data->fe_x, &data->fe_x);
- secp256k1_fe_add(&data->fe_x, &data->fe_y);
+ secp256k1_fe_inv_var(&data->fe[0], &data->fe[0]);
+ secp256k1_fe_add(&data->fe[0], &data->fe[1]);
}
}
@@ -190,9 +214,9 @@ void bench_field_sqrt(void* arg, int iters) {
secp256k1_fe t;
for (i = 0; i < iters; i++) {
- t = data->fe_x;
- j += secp256k1_fe_sqrt(&data->fe_x, &t);
- secp256k1_fe_add(&data->fe_x, &data->fe_y);
+ t = data->fe[0];
+ j += secp256k1_fe_sqrt(&data->fe[0], &t);
+ secp256k1_fe_add(&data->fe[0], &data->fe[1]);
}
CHECK(j <= iters);
}
@@ -202,7 +226,7 @@ void bench_group_double_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL);
+ secp256k1_gej_double_var(&data->gej[0], &data->gej[0], NULL);
}
}
@@ -211,7 +235,7 @@ void bench_group_add_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL);
+ secp256k1_gej_add_var(&data->gej[0], &data->gej[0], &data->gej[1], NULL);
}
}
@@ -220,7 +244,7 @@ void bench_group_add_affine(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y);
+ secp256k1_gej_add_ge(&data->gej[0], &data->gej[0], &data->ge[1]);
}
}
@@ -229,7 +253,7 @@ void bench_group_add_affine_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL);
+ secp256k1_gej_add_ge_var(&data->gej[0], &data->gej[0], &data->ge[1], NULL);
}
}
@@ -238,9 +262,37 @@ void bench_group_jacobi_var(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- j += secp256k1_gej_has_quad_y_var(&data->gej_x);
+ j += secp256k1_gej_has_quad_y_var(&data->gej[0]);
+ /* Vary the Y and Z coordinates of the input (the X coordinate doesn't matter to
+ secp256k1_gej_has_quad_y_var). Note that the resulting coordinates will
+ generally not correspond to a point on the curve, but this is not a problem
+ for the code being benchmarked here. Adding and normalizing have less
+ overhead than EC operations (which could guarantee the point remains on the
+ curve). */
+ secp256k1_fe_add(&data->gej[0].y, &data->fe[1]);
+ secp256k1_fe_add(&data->gej[0].z, &data->fe[2]);
+ secp256k1_fe_normalize_var(&data->gej[0].y);
+ secp256k1_fe_normalize_var(&data->gej[0].z);
+ }
+ CHECK(j <= iters);
+}
+
+void bench_group_to_affine_var(void* arg, int iters) {
+ int i;
+ bench_inv *data = (bench_inv*)arg;
+
+ for (i = 0; i < iters; ++i) {
+ secp256k1_ge_set_gej_var(&data->ge[1], &data->gej[0]);
+ /* Use the output affine X/Y coordinates to vary the input X/Y/Z coordinates.
+ Similar to bench_group_jacobi_var, this approach does not result in
+ coordinates of points on the curve. */
+ secp256k1_fe_add(&data->gej[0].x, &data->ge[1].y);
+ secp256k1_fe_add(&data->gej[0].y, &data->fe[2]);
+ secp256k1_fe_add(&data->gej[0].z, &data->ge[1].x);
+ secp256k1_fe_normalize_var(&data->gej[0].x);
+ secp256k1_fe_normalize_var(&data->gej[0].y);
+ secp256k1_fe_normalize_var(&data->gej[0].z);
}
- CHECK(j == iters);
}
void bench_ecmult_wnaf(void* arg, int iters) {
@@ -248,8 +300,8 @@ void bench_ecmult_wnaf(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- bits += secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A);
- overflow += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ bits += secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar[0], WINDOW_A);
+ overflow += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(overflow >= 0);
CHECK(bits <= 256*iters);
@@ -260,8 +312,8 @@ void bench_wnaf_const(void* arg, int iters) {
bench_inv *data = (bench_inv*)arg;
for (i = 0; i < iters; i++) {
- bits += secp256k1_wnaf_const(data->wnaf, &data->scalar_x, WINDOW_A, 256);
- overflow += secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
+ bits += secp256k1_wnaf_const(data->wnaf, &data->scalar[0], WINDOW_A, 256);
+ overflow += secp256k1_scalar_add(&data->scalar[0], &data->scalar[0], &data->scalar[1]);
}
CHECK(overflow >= 0);
CHECK(bits <= 256*iters);
@@ -323,14 +375,15 @@ void bench_context_sign(void* arg, int iters) {
void bench_num_jacobi(void* arg, int iters) {
int i, j = 0;
bench_inv *data = (bench_inv*)arg;
- secp256k1_num nx, norder;
+ secp256k1_num nx, na, norder;
- secp256k1_scalar_get_num(&nx, &data->scalar_x);
+ secp256k1_scalar_get_num(&nx, &data->scalar[0]);
secp256k1_scalar_order_get_num(&norder);
- secp256k1_scalar_get_num(&norder, &data->scalar_y);
+ secp256k1_scalar_get_num(&na, &data->scalar[1]);
for (i = 0; i < iters; i++) {
j += secp256k1_num_jacobi(&nx, &norder);
+ secp256k1_num_add(&nx, &nx, &na);
}
CHECK(j <= iters);
}
@@ -363,6 +416,7 @@ int main(int argc, char **argv) {
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, iters*10);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, iters*10);
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, iters);
+ if (have_flag(argc, argv, "group") || have_flag(argc, argv, "to_affine")) run_benchmark("group_to_affine_var", bench_group_to_affine_var, bench_setup, NULL, &data, 10, iters);
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, iters);
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, iters);
diff --git a/src/secp256k1/src/bench_schnorrsig.c b/src/secp256k1/src/bench_schnorrsig.c
new file mode 100644
index 0000000000..315f5af28e
--- /dev/null
+++ b/src/secp256k1/src/bench_schnorrsig.c
@@ -0,0 +1,102 @@
+/**********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#include <string.h>
+#include <stdlib.h>
+
+
+#include "include/secp256k1.h"
+#include "include/secp256k1_schnorrsig.h"
+#include "util.h"
+#include "bench.h"
+
+typedef struct {
+ secp256k1_context *ctx;
+ int n;
+
+ const secp256k1_keypair **keypairs;
+ const unsigned char **pk;
+ const unsigned char **sigs;
+ const unsigned char **msgs;
+} bench_schnorrsig_data;
+
+void bench_schnorrsig_sign(void* arg, int iters) {
+ bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
+ int i;
+ unsigned char msg[32] = "benchmarkexamplemessagetemplate";
+ unsigned char sig[64];
+
+ for (i = 0; i < iters; i++) {
+ msg[0] = i;
+ msg[1] = i >> 8;
+ CHECK(secp256k1_schnorrsig_sign(data->ctx, sig, msg, data->keypairs[i], NULL, NULL));
+ }
+}
+
+void bench_schnorrsig_verify(void* arg, int iters) {
+ bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
+ int i;
+
+ for (i = 0; i < iters; i++) {
+ secp256k1_xonly_pubkey pk;
+ CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pk[i]) == 1);
+ CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk));
+ }
+}
+
+int main(void) {
+ int i;
+ bench_schnorrsig_data data;
+ int iters = get_iters(10000);
+
+ data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
+ data.keypairs = (const secp256k1_keypair **)malloc(iters * sizeof(secp256k1_keypair *));
+ data.pk = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
+ data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
+ data.sigs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
+
+ for (i = 0; i < iters; i++) {
+ unsigned char sk[32];
+ unsigned char *msg = (unsigned char *)malloc(32);
+ unsigned char *sig = (unsigned char *)malloc(64);
+ secp256k1_keypair *keypair = (secp256k1_keypair *)malloc(sizeof(*keypair));
+ unsigned char *pk_char = (unsigned char *)malloc(32);
+ secp256k1_xonly_pubkey pk;
+ msg[0] = sk[0] = i;
+ msg[1] = sk[1] = i >> 8;
+ msg[2] = sk[2] = i >> 16;
+ msg[3] = sk[3] = i >> 24;
+ memset(&msg[4], 'm', 28);
+ memset(&sk[4], 's', 28);
+
+ data.keypairs[i] = keypair;
+ data.pk[i] = pk_char;
+ data.msgs[i] = msg;
+ data.sigs[i] = sig;
+
+ CHECK(secp256k1_keypair_create(data.ctx, keypair, sk));
+ CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, msg, keypair, NULL, NULL));
+ CHECK(secp256k1_keypair_xonly_pub(data.ctx, &pk, NULL, keypair));
+ CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, pk_char, &pk) == 1);
+ }
+
+ run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, iters);
+ run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, iters);
+
+ for (i = 0; i < iters; i++) {
+ free((void *)data.keypairs[i]);
+ free((void *)data.pk[i]);
+ free((void *)data.msgs[i]);
+ free((void *)data.sigs[i]);
+ }
+ free(data.keypairs);
+ free(data.pk);
+ free(data.msgs);
+ free(data.sigs);
+
+ secp256k1_context_destroy(data.ctx);
+ return 0;
+}
diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h
index 6d6d354aa4..55b61e4937 100644
--- a/src/secp256k1/src/ecmult_const_impl.h
+++ b/src/secp256k1/src/ecmult_const_impl.h
@@ -105,16 +105,22 @@ static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w
/* 4 */
u_last = secp256k1_scalar_shr_int(&s, w);
do {
- int sign;
int even;
/* 4.1 4.4 */
u = secp256k1_scalar_shr_int(&s, w);
/* 4.2 */
even = ((u & 1) == 0);
- sign = 2 * (u_last > 0) - 1;
- u += sign * even;
- u_last -= sign * even * (1 << w);
+ /* In contrast to the original algorithm, u_last is always > 0 and
+ * therefore we do not need to check its sign. In particular, it's easy
+ * to see that u_last is never < 0 because u is never < 0. Moreover,
+ * u_last is never = 0 because u is never even after a loop
+ * iteration. The same holds analogously for the initial value of
+ * u_last (in the first loop iteration). */
+ VERIFY_CHECK(u_last > 0);
+ VERIFY_CHECK((u_last & 1) == 1);
+ u += even;
+ u_last -= even * (1 << w);
/* 4.3, adapted for global sign change */
wnaf[word++] = u_last * global_sign;
@@ -202,7 +208,7 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
int n;
int j;
for (j = 0; j < WINDOW_A - 1; ++j) {
- secp256k1_gej_double_nonzero(r, r);
+ secp256k1_gej_double(r, r);
}
n = wnaf_1[i];
diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h
index 7993a1f11e..aca1fb72c5 100644
--- a/src/secp256k1/src/field.h
+++ b/src/secp256k1/src/field.h
@@ -22,16 +22,16 @@
#include "libsecp256k1-config.h"
#endif
-#if defined(USE_FIELD_10X26)
-#include "field_10x26.h"
-#elif defined(USE_FIELD_5X52)
+#include "util.h"
+
+#if defined(SECP256K1_WIDEMUL_INT128)
#include "field_5x52.h"
+#elif defined(SECP256K1_WIDEMUL_INT64)
+#include "field_10x26.h"
#else
-#error "Please select field implementation"
+#error "Please select wide multiplication implementation"
#endif
-#include "util.h"
-
/** Normalize a field element. This brings the field element to a canonical representation, reduces
* its magnitude to 1, and reduces it modulo field size `p`.
*/
diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h
index fc5bfe357e..6a068484c2 100644
--- a/src/secp256k1/src/field_5x52.h
+++ b/src/secp256k1/src/field_5x52.h
@@ -46,4 +46,10 @@ typedef struct {
(d6) | (((uint64_t)(d7)) << 32) \
}}
+#define SECP256K1_FE_STORAGE_CONST_GET(d) \
+ (uint32_t)(d.n[3] >> 32), (uint32_t)d.n[3], \
+ (uint32_t)(d.n[2] >> 32), (uint32_t)d.n[2], \
+ (uint32_t)(d.n[1] >> 32), (uint32_t)d.n[1], \
+ (uint32_t)(d.n[0] >> 32), (uint32_t)d.n[0]
+
#endif /* SECP256K1_FIELD_REPR_H */
diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h
index 485921a60e..18e4d2f30e 100644
--- a/src/secp256k1/src/field_impl.h
+++ b/src/secp256k1/src/field_impl.h
@@ -14,12 +14,12 @@
#include "util.h"
#include "num.h"
-#if defined(USE_FIELD_10X26)
-#include "field_10x26_impl.h"
-#elif defined(USE_FIELD_5X52)
+#if defined(SECP256K1_WIDEMUL_INT128)
#include "field_5x52_impl.h"
+#elif defined(SECP256K1_WIDEMUL_INT64)
+#include "field_10x26_impl.h"
#else
-#error "Please select field implementation"
+#error "Please select wide multiplication implementation"
#endif
SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
diff --git a/src/secp256k1/src/gen_context.c b/src/secp256k1/src/gen_context.c
index 539f574bfd..8b7729aee4 100644
--- a/src/secp256k1/src/gen_context.c
+++ b/src/secp256k1/src/gen_context.c
@@ -13,6 +13,7 @@
#include "basic-config.h"
#include "include/secp256k1.h"
+#include "assumptions.h"
#include "util.h"
#include "field_impl.h"
#include "scalar_impl.h"
diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h
index 863644f0f0..6185be052d 100644
--- a/src/secp256k1/src/group.h
+++ b/src/secp256k1/src/group.h
@@ -95,8 +95,8 @@ static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
/** Check whether a group element's y coordinate is a quadratic residue. */
static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a);
-/** Set r equal to the double of a, a cannot be infinity. Constant time. */
-static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a);
+/** Set r equal to the double of a. Constant time. */
+static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a);
/** Set r equal to the double of a. If rzr is not-NULL this sets *rzr such that r->z == a->z * *rzr (where infinity means an implicit z = 0). */
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index 43b039becf..ccd93d3483 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -303,7 +303,7 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
return secp256k1_fe_equal_var(&y2, &x3);
}
-static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a) {
+static SECP256K1_INLINE void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a) {
/* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate.
*
* Note that there is an implementation described at
@@ -313,8 +313,7 @@ static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, cons
*/
secp256k1_fe t1,t2,t3,t4;
- VERIFY_CHECK(!secp256k1_gej_is_infinity(a));
- r->infinity = 0;
+ r->infinity = a->infinity;
secp256k1_fe_mul(&r->z, &a->z, &a->y);
secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */
@@ -363,7 +362,7 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
secp256k1_fe_mul_int(rzr, 2);
}
- secp256k1_gej_double_nonzero(r, a);
+ secp256k1_gej_double(r, a);
}
static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) {
@@ -400,7 +399,7 @@ static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, cons
if (rzr != NULL) {
secp256k1_fe_set_int(rzr, 0);
}
- r->infinity = 1;
+ secp256k1_gej_set_infinity(r);
}
return;
}
@@ -450,7 +449,7 @@ static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, c
if (rzr != NULL) {
secp256k1_fe_set_int(rzr, 0);
}
- r->infinity = 1;
+ secp256k1_gej_set_infinity(r);
}
return;
}
@@ -509,7 +508,7 @@ static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a,
if (secp256k1_fe_normalizes_to_zero_var(&i)) {
secp256k1_gej_double_var(r, a, NULL);
} else {
- r->infinity = 1;
+ secp256k1_gej_set_infinity(r);
}
return;
}
diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h
index 782f97216c..409772587b 100644
--- a/src/secp256k1/src/hash_impl.h
+++ b/src/secp256k1/src/hash_impl.h
@@ -8,6 +8,7 @@
#define SECP256K1_HASH_IMPL_H
#include "hash.h"
+#include "util.h"
#include <stdlib.h>
#include <stdint.h>
@@ -27,9 +28,9 @@
(h) = t1 + t2; \
} while(0)
-#ifdef WORDS_BIGENDIAN
+#if defined(SECP256K1_BIG_ENDIAN)
#define BE32(x) (x)
-#else
+#elif defined(SECP256K1_LITTLE_ENDIAN)
#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24))
#endif
@@ -163,6 +164,19 @@ static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out
memcpy(out32, (const unsigned char*)out, 32);
}
+/* Initializes a sha256 struct and writes the 64 byte string
+ * SHA256(tag)||SHA256(tag) into it. */
+static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen) {
+ unsigned char buf[32];
+ secp256k1_sha256_initialize(hash);
+ secp256k1_sha256_write(hash, tag, taglen);
+ secp256k1_sha256_finalize(hash, buf);
+
+ secp256k1_sha256_initialize(hash);
+ secp256k1_sha256_write(hash, buf, 32);
+ secp256k1_sha256_write(hash, buf, 32);
+}
+
static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) {
size_t n;
unsigned char rkey[64];
diff --git a/src/secp256k1/src/modules/extrakeys/Makefile.am.include b/src/secp256k1/src/modules/extrakeys/Makefile.am.include
new file mode 100644
index 0000000000..8515f92e7a
--- /dev/null
+++ b/src/secp256k1/src/modules/extrakeys/Makefile.am.include
@@ -0,0 +1,3 @@
+include_HEADERS += include/secp256k1_extrakeys.h
+noinst_HEADERS += src/modules/extrakeys/tests_impl.h
+noinst_HEADERS += src/modules/extrakeys/main_impl.h
diff --git a/src/secp256k1/src/modules/extrakeys/main_impl.h b/src/secp256k1/src/modules/extrakeys/main_impl.h
new file mode 100644
index 0000000000..d319215355
--- /dev/null
+++ b/src/secp256k1/src/modules/extrakeys/main_impl.h
@@ -0,0 +1,248 @@
+/**********************************************************************
+ * Copyright (c) 2020 Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_MODULE_EXTRAKEYS_MAIN_
+#define _SECP256K1_MODULE_EXTRAKEYS_MAIN_
+
+#include "include/secp256k1.h"
+#include "include/secp256k1_extrakeys.h"
+
+static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) {
+ return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey);
+}
+
+static SECP256K1_INLINE void secp256k1_xonly_pubkey_save(secp256k1_xonly_pubkey *pubkey, secp256k1_ge *ge) {
+ secp256k1_pubkey_save((secp256k1_pubkey *) pubkey, ge);
+}
+
+int secp256k1_xonly_pubkey_parse(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, const unsigned char *input32) {
+ secp256k1_ge pk;
+ secp256k1_fe x;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(pubkey != NULL);
+ memset(pubkey, 0, sizeof(*pubkey));
+ ARG_CHECK(input32 != NULL);
+
+ if (!secp256k1_fe_set_b32(&x, input32)) {
+ return 0;
+ }
+ if (!secp256k1_ge_set_xo_var(&pk, &x, 0)) {
+ return 0;
+ }
+ secp256k1_xonly_pubkey_save(pubkey, &pk);
+ return 1;
+}
+
+int secp256k1_xonly_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output32, const secp256k1_xonly_pubkey *pubkey) {
+ secp256k1_ge pk;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(output32 != NULL);
+ memset(output32, 0, 32);
+ ARG_CHECK(pubkey != NULL);
+
+ if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) {
+ return 0;
+ }
+ secp256k1_fe_get_b32(output32, &pk.x);
+ return 1;
+}
+
+/** Keeps a group element as is if it has an even Y and otherwise negates it.
+ * y_parity is set to 0 in the former case and to 1 in the latter case.
+ * Requires that the coordinates of r are normalized. */
+static int secp256k1_extrakeys_ge_even_y(secp256k1_ge *r) {
+ int y_parity = 0;
+ VERIFY_CHECK(!secp256k1_ge_is_infinity(r));
+
+ if (secp256k1_fe_is_odd(&r->y)) {
+ secp256k1_fe_negate(&r->y, &r->y, 1);
+ y_parity = 1;
+ }
+ return y_parity;
+}
+
+int secp256k1_xonly_pubkey_from_pubkey(const secp256k1_context* ctx, secp256k1_xonly_pubkey *xonly_pubkey, int *pk_parity, const secp256k1_pubkey *pubkey) {
+ secp256k1_ge pk;
+ int tmp;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(xonly_pubkey != NULL);
+ ARG_CHECK(pubkey != NULL);
+
+ if (!secp256k1_pubkey_load(ctx, &pk, pubkey)) {
+ return 0;
+ }
+ tmp = secp256k1_extrakeys_ge_even_y(&pk);
+ if (pk_parity != NULL) {
+ *pk_parity = tmp;
+ }
+ secp256k1_xonly_pubkey_save(xonly_pubkey, &pk);
+ return 1;
+}
+
+int secp256k1_xonly_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
+ secp256k1_ge pk;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(output_pubkey != NULL);
+ memset(output_pubkey, 0, sizeof(*output_pubkey));
+ ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
+ ARG_CHECK(internal_pubkey != NULL);
+ ARG_CHECK(tweak32 != NULL);
+
+ if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey)
+ || !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) {
+ return 0;
+ }
+ secp256k1_pubkey_save(output_pubkey, &pk);
+ return 1;
+}
+
+int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const unsigned char *tweaked_pubkey32, int tweaked_pk_parity, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
+ secp256k1_ge pk;
+ unsigned char pk_expected32[32];
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
+ ARG_CHECK(internal_pubkey != NULL);
+ ARG_CHECK(tweaked_pubkey32 != NULL);
+ ARG_CHECK(tweak32 != NULL);
+
+ if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey)
+ || !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) {
+ return 0;
+ }
+ secp256k1_fe_normalize_var(&pk.x);
+ secp256k1_fe_normalize_var(&pk.y);
+ secp256k1_fe_get_b32(pk_expected32, &pk.x);
+
+ return memcmp(&pk_expected32, tweaked_pubkey32, 32) == 0
+ && secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity;
+}
+
+static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) {
+ secp256k1_scalar_get_b32(&keypair->data[0], sk);
+ secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk);
+}
+
+
+static int secp256k1_keypair_seckey_load(const secp256k1_context* ctx, secp256k1_scalar *sk, const secp256k1_keypair *keypair) {
+ int ret;
+
+ ret = secp256k1_scalar_set_b32_seckey(sk, &keypair->data[0]);
+ /* We can declassify ret here because sk is only zero if a keypair function
+ * failed (which zeroes the keypair) and its return value is ignored. */
+ secp256k1_declassify(ctx, &ret, sizeof(ret));
+ ARG_CHECK(ret);
+ return ret;
+}
+
+/* Load a keypair into pk and sk (if non-NULL). This function declassifies pk
+ * and ARG_CHECKs that the keypair is not invalid. It always initializes sk and
+ * pk with dummy values. */
+static int secp256k1_keypair_load(const secp256k1_context* ctx, secp256k1_scalar *sk, secp256k1_ge *pk, const secp256k1_keypair *keypair) {
+ int ret;
+ const secp256k1_pubkey *pubkey = (const secp256k1_pubkey *)&keypair->data[32];
+
+ /* Need to declassify the pubkey because pubkey_load ARG_CHECKs if it's
+ * invalid. */
+ secp256k1_declassify(ctx, pubkey, sizeof(*pubkey));
+ ret = secp256k1_pubkey_load(ctx, pk, pubkey);
+ if (sk != NULL) {
+ ret = ret && secp256k1_keypair_seckey_load(ctx, sk, keypair);
+ }
+ if (!ret) {
+ *pk = secp256k1_ge_const_g;
+ if (sk != NULL) {
+ *sk = secp256k1_scalar_one;
+ }
+ }
+ return ret;
+}
+
+int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *seckey32) {
+ secp256k1_scalar sk;
+ secp256k1_ge pk;
+ int ret = 0;
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(keypair != NULL);
+ memset(keypair, 0, sizeof(*keypair));
+ ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
+ ARG_CHECK(seckey32 != NULL);
+
+ ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32);
+ secp256k1_keypair_save(keypair, &sk, &pk);
+ memczero(keypair, sizeof(*keypair), !ret);
+
+ secp256k1_scalar_clear(&sk);
+ return ret;
+}
+
+int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) {
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(pubkey != NULL);
+ memset(pubkey, 0, sizeof(*pubkey));
+ ARG_CHECK(keypair != NULL);
+
+ memcpy(pubkey->data, &keypair->data[32], sizeof(*pubkey));
+ return 1;
+}
+
+int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, const secp256k1_keypair *keypair) {
+ secp256k1_ge pk;
+ int tmp;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(pubkey != NULL);
+ memset(pubkey, 0, sizeof(*pubkey));
+ ARG_CHECK(keypair != NULL);
+
+ if (!secp256k1_keypair_load(ctx, NULL, &pk, keypair)) {
+ return 0;
+ }
+ tmp = secp256k1_extrakeys_ge_even_y(&pk);
+ if (pk_parity != NULL) {
+ *pk_parity = tmp;
+ }
+ secp256k1_xonly_pubkey_save(pubkey, &pk);
+
+ return 1;
+}
+
+int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *tweak32) {
+ secp256k1_ge pk;
+ secp256k1_scalar sk;
+ int y_parity;
+ int ret;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
+ ARG_CHECK(keypair != NULL);
+ ARG_CHECK(tweak32 != NULL);
+
+ ret = secp256k1_keypair_load(ctx, &sk, &pk, keypair);
+ memset(keypair, 0, sizeof(*keypair));
+
+ y_parity = secp256k1_extrakeys_ge_even_y(&pk);
+ if (y_parity == 1) {
+ secp256k1_scalar_negate(&sk, &sk);
+ }
+
+ ret &= secp256k1_ec_seckey_tweak_add_helper(&sk, tweak32);
+ ret &= secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32);
+
+ secp256k1_declassify(ctx, &ret, sizeof(ret));
+ if (ret) {
+ secp256k1_keypair_save(keypair, &sk, &pk);
+ }
+
+ secp256k1_scalar_clear(&sk);
+ return ret;
+}
+
+#endif
diff --git a/src/secp256k1/src/modules/extrakeys/tests_impl.h b/src/secp256k1/src/modules/extrakeys/tests_impl.h
new file mode 100644
index 0000000000..fc9d40eda1
--- /dev/null
+++ b/src/secp256k1/src/modules/extrakeys/tests_impl.h
@@ -0,0 +1,524 @@
+/**********************************************************************
+ * Copyright (c) 2020 Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_
+#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_
+
+#include "secp256k1_extrakeys.h"
+
+static secp256k1_context* api_test_context(int flags, int *ecount) {
+ secp256k1_context *ctx0 = secp256k1_context_create(flags);
+ secp256k1_context_set_error_callback(ctx0, counting_illegal_callback_fn, ecount);
+ secp256k1_context_set_illegal_callback(ctx0, counting_illegal_callback_fn, ecount);
+ return ctx0;
+}
+
+void test_xonly_pubkey(void) {
+ secp256k1_pubkey pk;
+ secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp;
+ secp256k1_ge pk1;
+ secp256k1_ge pk2;
+ secp256k1_fe y;
+ unsigned char sk[32];
+ unsigned char xy_sk[32];
+ unsigned char buf32[32];
+ unsigned char ones32[32];
+ unsigned char zeros64[64] = { 0 };
+ int pk_parity;
+ int i;
+
+ int ecount;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+ secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+ secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+ secp256k1_rand256(sk);
+ memset(ones32, 0xFF, 32);
+ secp256k1_rand256(xy_sk);
+ CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
+
+ /* Test xonly_pubkey_from_pubkey */
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(sign, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(verify, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, NULL, &pk_parity, &pk) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, NULL, &pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, NULL) == 0);
+ CHECK(ecount == 2);
+ memset(&pk, 0, sizeof(pk));
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 0);
+ CHECK(ecount == 3);
+
+ /* Choose a secret key such that the resulting pubkey and xonly_pubkey match. */
+ memset(sk, 0, sizeof(sk));
+ sk[0] = 1;
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(memcmp(&pk, &xonly_pk, sizeof(pk)) == 0);
+ CHECK(pk_parity == 0);
+
+ /* Choose a secret key such that pubkey and xonly_pubkey are each others
+ * negation. */
+ sk[0] = 2;
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(memcmp(&xonly_pk, &pk, sizeof(xonly_pk)) != 0);
+ CHECK(pk_parity == 1);
+ secp256k1_pubkey_load(ctx, &pk1, &pk);
+ secp256k1_pubkey_load(ctx, &pk2, (secp256k1_pubkey *) &xonly_pk);
+ CHECK(secp256k1_fe_equal(&pk1.x, &pk2.x) == 1);
+ secp256k1_fe_negate(&y, &pk2.y, 1);
+ CHECK(secp256k1_fe_equal(&pk1.y, &y) == 1);
+
+ /* Test xonly_pubkey_serialize and xonly_pubkey_parse */
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_serialize(none, NULL, &xonly_pk) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, NULL) == 0);
+ CHECK(memcmp(buf32, zeros64, 32) == 0);
+ CHECK(ecount == 2);
+ {
+ /* A pubkey filled with 0s will fail to serialize due to pubkey_load
+ * special casing. */
+ secp256k1_xonly_pubkey pk_tmp;
+ memset(&pk_tmp, 0, sizeof(pk_tmp));
+ CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, &pk_tmp) == 0);
+ }
+ /* pubkey_load called illegal callback */
+ CHECK(ecount == 3);
+
+ CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, &xonly_pk) == 1);
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_parse(none, NULL, buf32) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, NULL) == 0);
+ CHECK(ecount == 2);
+
+ /* Serialization and parse roundtrip */
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, NULL, &pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &xonly_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk_tmp, buf32) == 1);
+ CHECK(memcmp(&xonly_pk, &xonly_pk_tmp, sizeof(xonly_pk)) == 0);
+
+ /* Test parsing invalid field elements */
+ memset(&xonly_pk, 1, sizeof(xonly_pk));
+ /* Overflowing field element */
+ CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, ones32) == 0);
+ CHECK(memcmp(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0);
+ memset(&xonly_pk, 1, sizeof(xonly_pk));
+ /* There's no point with x-coordinate 0 on secp256k1 */
+ CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, zeros64) == 0);
+ CHECK(memcmp(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0);
+ /* If a random 32-byte string can not be parsed with ec_pubkey_parse
+ * (because interpreted as X coordinate it does not correspond to a point on
+ * the curve) then xonly_pubkey_parse should fail as well. */
+ for (i = 0; i < count; i++) {
+ unsigned char rand33[33];
+ secp256k1_rand256(&rand33[1]);
+ rand33[0] = SECP256K1_TAG_PUBKEY_EVEN;
+ if (!secp256k1_ec_pubkey_parse(ctx, &pk, rand33, 33)) {
+ memset(&xonly_pk, 1, sizeof(xonly_pk));
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, &rand33[1]) == 0);
+ CHECK(memcmp(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0);
+ } else {
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, &rand33[1]) == 1);
+ }
+ }
+ CHECK(ecount == 2);
+
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(verify);
+}
+
+void test_xonly_pubkey_tweak(void) {
+ unsigned char zeros64[64] = { 0 };
+ unsigned char overflows[32];
+ unsigned char sk[32];
+ secp256k1_pubkey internal_pk;
+ secp256k1_xonly_pubkey internal_xonly_pk;
+ secp256k1_pubkey output_pk;
+ int pk_parity;
+ unsigned char tweak[32];
+ int i;
+
+ int ecount;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+ secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+ secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+ memset(overflows, 0xff, sizeof(overflows));
+ secp256k1_rand256(tweak);
+ secp256k1_rand256(sk);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &internal_xonly_pk, &pk_parity, &internal_pk) == 1);
+
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_tweak_add(none, &output_pk, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(sign, &output_pk, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, NULL, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, NULL, tweak) == 0);
+ CHECK(ecount == 4);
+ /* NULL internal_xonly_pk zeroes the output_pk */
+ CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, NULL) == 0);
+ CHECK(ecount == 5);
+ /* NULL tweak zeroes the output_pk */
+ CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0);
+
+ /* Invalid tweak zeroes the output_pk */
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, overflows) == 0);
+ CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0);
+
+ /* A zero tweak is fine */
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, zeros64) == 1);
+
+ /* Fails if the resulting key was infinity */
+ for (i = 0; i < count; i++) {
+ secp256k1_scalar scalar_tweak;
+ /* Because sk may be negated before adding, we need to try with tweak =
+ * sk as well as tweak = -sk. */
+ secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL);
+ secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak);
+ secp256k1_scalar_get_b32(tweak, &scalar_tweak);
+ CHECK((secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, sk) == 0)
+ || (secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 0));
+ CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0);
+ }
+
+ /* Invalid pk with a valid tweak */
+ memset(&internal_xonly_pk, 0, sizeof(internal_xonly_pk));
+ secp256k1_rand256(tweak);
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 1);
+ CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0);
+
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(verify);
+}
+
+void test_xonly_pubkey_tweak_check(void) {
+ unsigned char zeros64[64] = { 0 };
+ unsigned char overflows[32];
+ unsigned char sk[32];
+ secp256k1_pubkey internal_pk;
+ secp256k1_xonly_pubkey internal_xonly_pk;
+ secp256k1_pubkey output_pk;
+ secp256k1_xonly_pubkey output_xonly_pk;
+ unsigned char output_pk32[32];
+ unsigned char buf32[32];
+ int pk_parity;
+ unsigned char tweak[32];
+
+ int ecount;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+ secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+ secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+ memset(overflows, 0xff, sizeof(overflows));
+ secp256k1_rand256(tweak);
+ secp256k1_rand256(sk);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &internal_xonly_pk, &pk_parity, &internal_pk) == 1);
+
+ ecount = 0;
+ CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(verify, &output_xonly_pk, &pk_parity, &output_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &output_xonly_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(none, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(sign, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, &internal_xonly_pk, tweak) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, NULL, pk_parity, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 3);
+ /* invalid pk_parity value */
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, 2, &internal_xonly_pk, tweak) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, NULL, tweak) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, &internal_xonly_pk, NULL) == 0);
+ CHECK(ecount == 5);
+
+ memset(tweak, 1, sizeof(tweak));
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &internal_xonly_pk, NULL, &internal_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_xonly_pk, &pk_parity, &output_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk32, &output_xonly_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, tweak) == 1);
+
+ /* Wrong pk_parity */
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, !pk_parity, &internal_xonly_pk, tweak) == 0);
+ /* Wrong public key */
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &internal_xonly_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
+
+ /* Overflowing tweak not allowed */
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, overflows) == 0);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, overflows) == 0);
+ CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0);
+ CHECK(ecount == 5);
+
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(verify);
+}
+
+/* Starts with an initial pubkey and recursively creates N_PUBKEYS - 1
+ * additional pubkeys by calling tweak_add. Then verifies every tweak starting
+ * from the last pubkey. */
+#define N_PUBKEYS 32
+void test_xonly_pubkey_tweak_recursive(void) {
+ unsigned char sk[32];
+ secp256k1_pubkey pk[N_PUBKEYS];
+ unsigned char pk_serialized[32];
+ unsigned char tweak[N_PUBKEYS - 1][32];
+ int i;
+
+ secp256k1_rand256(sk);
+ CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk) == 1);
+ /* Add tweaks */
+ for (i = 0; i < N_PUBKEYS - 1; i++) {
+ secp256k1_xonly_pubkey xonly_pk;
+ memset(tweak[i], i + 1, sizeof(tweak[i]));
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i]) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &pk[i + 1], &xonly_pk, tweak[i]) == 1);
+ }
+
+ /* Verify tweaks */
+ for (i = N_PUBKEYS - 1; i > 0; i--) {
+ secp256k1_xonly_pubkey xonly_pk;
+ int pk_parity;
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk[i]) == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk_serialized, &xonly_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i - 1]) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk_serialized, pk_parity, &xonly_pk, tweak[i - 1]) == 1);
+ }
+}
+#undef N_PUBKEYS
+
+void test_keypair(void) {
+ unsigned char sk[32];
+ unsigned char zeros96[96] = { 0 };
+ unsigned char overflows[32];
+ secp256k1_keypair keypair;
+ secp256k1_pubkey pk, pk_tmp;
+ secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp;
+ int pk_parity, pk_parity_tmp;
+ int ecount;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+ secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+ secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+ CHECK(sizeof(zeros96) == sizeof(keypair));
+ memset(overflows, 0xFF, sizeof(overflows));
+
+ /* Test keypair_create */
+ ecount = 0;
+ secp256k1_rand256(sk);
+ CHECK(secp256k1_keypair_create(none, &keypair, sk) == 0);
+ CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_keypair_create(verify, &keypair, sk) == 0);
+ CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_create(sign, NULL, sk) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_keypair_create(sign, &keypair, NULL) == 0);
+ CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
+ CHECK(ecount == 4);
+
+ /* Invalid secret key */
+ CHECK(secp256k1_keypair_create(sign, &keypair, zeros96) == 0);
+ CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
+ CHECK(secp256k1_keypair_create(sign, &keypair, overflows) == 0);
+ CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
+
+ /* Test keypair_pub */
+ ecount = 0;
+ secp256k1_rand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
+ CHECK(secp256k1_keypair_pub(none, NULL, &keypair) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_keypair_pub(none, &pk, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(memcmp(zeros96, &pk, sizeof(pk)) == 0);
+
+ /* Using an invalid keypair is fine for keypair_pub */
+ memset(&keypair, 0, sizeof(keypair));
+ CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
+ CHECK(memcmp(zeros96, &pk, sizeof(pk)) == 0);
+
+ /* keypair holds the same pubkey as pubkey_create */
+ CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
+ CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_pub(none, &pk_tmp, &keypair) == 1);
+ CHECK(memcmp(&pk, &pk_tmp, sizeof(pk)) == 0);
+
+ /** Test keypair_xonly_pub **/
+ ecount = 0;
+ secp256k1_rand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(none, NULL, &pk_parity, &keypair) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, NULL, &keypair) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(memcmp(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
+ /* Using an invalid keypair will set the xonly_pk to 0 (first reset
+ * xonly_pk). */
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
+ memset(&keypair, 0, sizeof(keypair));
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 0);
+ CHECK(memcmp(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
+ CHECK(ecount == 3);
+
+ /** keypair holds the same xonly pubkey as pubkey_create **/
+ CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
+ CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
+ CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk_tmp, &pk_parity_tmp, &keypair) == 1);
+ CHECK(memcmp(&xonly_pk, &xonly_pk_tmp, sizeof(pk)) == 0);
+ CHECK(pk_parity == pk_parity_tmp);
+
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(verify);
+}
+
+void test_keypair_add(void) {
+ unsigned char sk[32];
+ secp256k1_keypair keypair;
+ unsigned char overflows[32];
+ unsigned char zeros96[96] = { 0 };
+ unsigned char tweak[32];
+ int i;
+ int ecount = 0;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+ secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+ secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+ CHECK(sizeof(zeros96) == sizeof(keypair));
+ secp256k1_rand256(sk);
+ secp256k1_rand256(tweak);
+ memset(overflows, 0xFF, 32);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+
+ CHECK(secp256k1_keypair_xonly_tweak_add(none, &keypair, tweak) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(sign, &keypair, tweak) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, NULL, tweak) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, NULL) == 0);
+ CHECK(ecount == 4);
+ /* This does not set the keypair to zeroes */
+ CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) != 0);
+
+ /* Invalid tweak zeroes the keypair */
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, overflows) == 0);
+ CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0);
+
+ /* A zero tweak is fine */
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, zeros96) == 1);
+
+ /* Fails if the resulting keypair was (sk=0, pk=infinity) */
+ for (i = 0; i < count; i++) {
+ secp256k1_scalar scalar_tweak;
+ secp256k1_keypair keypair_tmp;
+ secp256k1_rand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ memcpy(&keypair_tmp, &keypair, sizeof(keypair));
+ /* Because sk may be negated before adding, we need to try with tweak =
+ * sk as well as tweak = -sk. */
+ secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL);
+ secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak);
+ secp256k1_scalar_get_b32(tweak, &scalar_tweak);
+ CHECK((secp256k1_keypair_xonly_tweak_add(ctx, &keypair, sk) == 0)
+ || (secp256k1_keypair_xonly_tweak_add(ctx, &keypair_tmp, tweak) == 0));
+ CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0
+ || memcmp(&keypair_tmp, zeros96, sizeof(keypair_tmp)) == 0);
+ }
+
+ /* Invalid keypair with a valid tweak */
+ memset(&keypair, 0, sizeof(keypair));
+ secp256k1_rand256(tweak);
+ ecount = 0;
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
+ CHECK(ecount == 1);
+ CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0);
+ /* Only seckey part of keypair invalid */
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ memset(&keypair, 0, 32);
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
+ CHECK(ecount == 2);
+ /* Only pubkey part of keypair invalid */
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ memset(&keypair.data[32], 0, 64);
+ CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
+ CHECK(ecount == 3);
+
+ /* Check that the keypair_tweak_add implementation is correct */
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ for (i = 0; i < count; i++) {
+ secp256k1_xonly_pubkey internal_pk;
+ secp256k1_xonly_pubkey output_pk;
+ secp256k1_pubkey output_pk_xy;
+ secp256k1_pubkey output_pk_expected;
+ unsigned char pk32[32];
+ int pk_parity;
+
+ secp256k1_rand256(tweak);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1);
+
+ /* Check that it passes xonly_pubkey_tweak_add_check */
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk32, &output_pk) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk32, pk_parity, &internal_pk, tweak) == 1);
+
+ /* Check that the resulting pubkey matches xonly_pubkey_tweak_add */
+ CHECK(secp256k1_keypair_pub(ctx, &output_pk_xy, &keypair) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk_expected, &internal_pk, tweak) == 1);
+ CHECK(memcmp(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
+
+ /* Check that the secret key in the keypair is tweaked correctly */
+ CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, &keypair.data[0]) == 1);
+ CHECK(memcmp(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
+ }
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(verify);
+}
+
+void run_extrakeys_tests(void) {
+ /* xonly key test cases */
+ test_xonly_pubkey();
+ test_xonly_pubkey_tweak();
+ test_xonly_pubkey_tweak_check();
+ test_xonly_pubkey_tweak_recursive();
+
+ /* keypair tests */
+ test_keypair();
+ test_keypair_add();
+}
+
+#endif
diff --git a/src/secp256k1/src/modules/schnorrsig/Makefile.am.include b/src/secp256k1/src/modules/schnorrsig/Makefile.am.include
new file mode 100644
index 0000000000..a82bafe43f
--- /dev/null
+++ b/src/secp256k1/src/modules/schnorrsig/Makefile.am.include
@@ -0,0 +1,8 @@
+include_HEADERS += include/secp256k1_schnorrsig.h
+noinst_HEADERS += src/modules/schnorrsig/main_impl.h
+noinst_HEADERS += src/modules/schnorrsig/tests_impl.h
+if USE_BENCHMARK
+noinst_PROGRAMS += bench_schnorrsig
+bench_schnorrsig_SOURCES = src/bench_schnorrsig.c
+bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
+endif
diff --git a/src/secp256k1/src/modules/schnorrsig/main_impl.h b/src/secp256k1/src/modules/schnorrsig/main_impl.h
new file mode 100644
index 0000000000..a0218f881a
--- /dev/null
+++ b/src/secp256k1/src/modules/schnorrsig/main_impl.h
@@ -0,0 +1,238 @@
+/**********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_
+#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_
+
+#include "include/secp256k1.h"
+#include "include/secp256k1_schnorrsig.h"
+#include "hash.h"
+
+/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
+ * SHA256 to SHA256("BIP0340/nonce")||SHA256("BIP0340/nonce"). */
+static void secp256k1_nonce_function_bip340_sha256_tagged(secp256k1_sha256 *sha) {
+ secp256k1_sha256_initialize(sha);
+ sha->s[0] = 0x46615b35ul;
+ sha->s[1] = 0xf4bfbff7ul;
+ sha->s[2] = 0x9f8dc671ul;
+ sha->s[3] = 0x83627ab3ul;
+ sha->s[4] = 0x60217180ul;
+ sha->s[5] = 0x57358661ul;
+ sha->s[6] = 0x21a29e54ul;
+ sha->s[7] = 0x68b07b4cul;
+
+ sha->bytes = 64;
+}
+
+/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
+ * SHA256 to SHA256("BIP0340/aux")||SHA256("BIP0340/aux"). */
+static void secp256k1_nonce_function_bip340_sha256_tagged_aux(secp256k1_sha256 *sha) {
+ secp256k1_sha256_initialize(sha);
+ sha->s[0] = 0x24dd3219ul;
+ sha->s[1] = 0x4eba7e70ul;
+ sha->s[2] = 0xca0fabb9ul;
+ sha->s[3] = 0x0fa3166dul;
+ sha->s[4] = 0x3afbe4b1ul;
+ sha->s[5] = 0x4c44df97ul;
+ sha->s[6] = 0x4aac2739ul;
+ sha->s[7] = 0x249e850aul;
+
+ sha->bytes = 64;
+}
+
+/* algo16 argument for nonce_function_bip340 to derive the nonce exactly as stated in BIP-340
+ * by using the correct tagged hash function. */
+static const unsigned char bip340_algo16[16] = "BIP0340/nonce\0\0\0";
+
+static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
+ secp256k1_sha256 sha;
+ unsigned char masked_key[32];
+ int i;
+
+ if (algo16 == NULL) {
+ return 0;
+ }
+
+ if (data != NULL) {
+ secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha);
+ secp256k1_sha256_write(&sha, data, 32);
+ secp256k1_sha256_finalize(&sha, masked_key);
+ for (i = 0; i < 32; i++) {
+ masked_key[i] ^= key32[i];
+ }
+ }
+
+ /* Tag the hash with algo16 which is important to avoid nonce reuse across
+ * algorithms. If this nonce function is used in BIP-340 signing as defined
+ * in the spec, an optimized tagging implementation is used. */
+ if (memcmp(algo16, bip340_algo16, 16) == 0) {
+ secp256k1_nonce_function_bip340_sha256_tagged(&sha);
+ } else {
+ int algo16_len = 16;
+ /* Remove terminating null bytes */
+ while (algo16_len > 0 && !algo16[algo16_len - 1]) {
+ algo16_len--;
+ }
+ secp256k1_sha256_initialize_tagged(&sha, algo16, algo16_len);
+ }
+
+ /* Hash (masked-)key||pk||msg using the tagged hash as per the spec */
+ if (data != NULL) {
+ secp256k1_sha256_write(&sha, masked_key, 32);
+ } else {
+ secp256k1_sha256_write(&sha, key32, 32);
+ }
+ secp256k1_sha256_write(&sha, xonly_pk32, 32);
+ secp256k1_sha256_write(&sha, msg32, 32);
+ secp256k1_sha256_finalize(&sha, nonce32);
+ return 1;
+}
+
+const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340 = nonce_function_bip340;
+
+/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
+ * SHA256 to SHA256("BIP0340/challenge")||SHA256("BIP0340/challenge"). */
+static void secp256k1_schnorrsig_sha256_tagged(secp256k1_sha256 *sha) {
+ secp256k1_sha256_initialize(sha);
+ sha->s[0] = 0x9cecba11ul;
+ sha->s[1] = 0x23925381ul;
+ sha->s[2] = 0x11679112ul;
+ sha->s[3] = 0xd1627e0ful;
+ sha->s[4] = 0x97c87550ul;
+ sha->s[5] = 0x003cc765ul;
+ sha->s[6] = 0x90f61164ul;
+ sha->s[7] = 0x33e9b66aul;
+ sha->bytes = 64;
+}
+
+int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) {
+ secp256k1_scalar sk;
+ secp256k1_scalar e;
+ secp256k1_scalar k;
+ secp256k1_gej rj;
+ secp256k1_ge pk;
+ secp256k1_ge r;
+ secp256k1_sha256 sha;
+ unsigned char buf[32] = { 0 };
+ unsigned char pk_buf[32];
+ unsigned char seckey[32];
+ int ret = 1;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
+ ARG_CHECK(sig64 != NULL);
+ ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(keypair != NULL);
+
+ if (noncefp == NULL) {
+ noncefp = secp256k1_nonce_function_bip340;
+ }
+
+ ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair);
+ /* Because we are signing for a x-only pubkey, the secret key is negated
+ * before signing if the point corresponding to the secret key does not
+ * have an even Y. */
+ if (secp256k1_fe_is_odd(&pk.y)) {
+ secp256k1_scalar_negate(&sk, &sk);
+ }
+
+ secp256k1_scalar_get_b32(seckey, &sk);
+ secp256k1_fe_get_b32(pk_buf, &pk.x);
+ ret &= !!noncefp(buf, msg32, seckey, pk_buf, bip340_algo16, ndata);
+ secp256k1_scalar_set_b32(&k, buf, NULL);
+ ret &= !secp256k1_scalar_is_zero(&k);
+ secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret);
+
+ secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
+ secp256k1_ge_set_gej(&r, &rj);
+
+ /* We declassify r to allow using it as a branch point. This is fine
+ * because r is not a secret. */
+ secp256k1_declassify(ctx, &r, sizeof(r));
+ secp256k1_fe_normalize_var(&r.y);
+ if (secp256k1_fe_is_odd(&r.y)) {
+ secp256k1_scalar_negate(&k, &k);
+ }
+ secp256k1_fe_normalize_var(&r.x);
+ secp256k1_fe_get_b32(&sig64[0], &r.x);
+
+ /* tagged hash(r.x, pk.x, msg32) */
+ secp256k1_schnorrsig_sha256_tagged(&sha);
+ secp256k1_sha256_write(&sha, &sig64[0], 32);
+ secp256k1_sha256_write(&sha, pk_buf, sizeof(pk_buf));
+ secp256k1_sha256_write(&sha, msg32, 32);
+ secp256k1_sha256_finalize(&sha, buf);
+
+ /* Set scalar e to the challenge hash modulo the curve order as per
+ * BIP340. */
+ secp256k1_scalar_set_b32(&e, buf, NULL);
+ secp256k1_scalar_mul(&e, &e, &sk);
+ secp256k1_scalar_add(&e, &e, &k);
+ secp256k1_scalar_get_b32(&sig64[32], &e);
+
+ memczero(sig64, 64, !ret);
+ secp256k1_scalar_clear(&k);
+ secp256k1_scalar_clear(&sk);
+ memset(seckey, 0, sizeof(seckey));
+
+ return ret;
+}
+
+int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey) {
+ secp256k1_scalar s;
+ secp256k1_scalar e;
+ secp256k1_gej rj;
+ secp256k1_ge pk;
+ secp256k1_gej pkj;
+ secp256k1_fe rx;
+ secp256k1_ge r;
+ secp256k1_sha256 sha;
+ unsigned char buf[32];
+ int overflow;
+
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
+ ARG_CHECK(sig64 != NULL);
+ ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(pubkey != NULL);
+
+ if (!secp256k1_fe_set_b32(&rx, &sig64[0])) {
+ return 0;
+ }
+
+ secp256k1_scalar_set_b32(&s, &sig64[32], &overflow);
+ if (overflow) {
+ return 0;
+ }
+
+ if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) {
+ return 0;
+ }
+
+ secp256k1_schnorrsig_sha256_tagged(&sha);
+ secp256k1_sha256_write(&sha, &sig64[0], 32);
+ secp256k1_fe_get_b32(buf, &pk.x);
+ secp256k1_sha256_write(&sha, buf, sizeof(buf));
+ secp256k1_sha256_write(&sha, msg32, 32);
+ secp256k1_sha256_finalize(&sha, buf);
+ secp256k1_scalar_set_b32(&e, buf, NULL);
+
+ /* Compute rj = s*G + (-e)*pkj */
+ secp256k1_scalar_negate(&e, &e);
+ secp256k1_gej_set_ge(&pkj, &pk);
+ secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s);
+
+ secp256k1_ge_set_gej_var(&r, &rj);
+ if (secp256k1_ge_is_infinity(&r)) {
+ return 0;
+ }
+
+ secp256k1_fe_normalize_var(&r.y);
+ return !secp256k1_fe_is_odd(&r.y) &&
+ secp256k1_fe_equal_var(&rx, &r.x);
+}
+
+#endif
diff --git a/src/secp256k1/src/modules/schnorrsig/tests_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_impl.h
new file mode 100644
index 0000000000..88d8f56404
--- /dev/null
+++ b/src/secp256k1/src/modules/schnorrsig/tests_impl.h
@@ -0,0 +1,806 @@
+/**********************************************************************
+ * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_
+#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_
+
+#include "secp256k1_schnorrsig.h"
+
+/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many
+ * bytes) changes the hash function
+ */
+void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) {
+ unsigned char nonces[2][32];
+ CHECK(nonce_function_bip340(nonces[0], args[0], args[1], args[2], args[3], args[4]) == 1);
+ secp256k1_rand_flip(args[n_flip], n_bytes);
+ CHECK(nonce_function_bip340(nonces[1], args[0], args[1], args[2], args[3], args[4]) == 1);
+ CHECK(memcmp(nonces[0], nonces[1], 32) != 0);
+}
+
+/* Tests for the equality of two sha256 structs. This function only produces a
+ * correct result if an integer multiple of 64 many bytes have been written
+ * into the hash functions. */
+void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) {
+ /* Is buffer fully consumed? */
+ CHECK((sha1->bytes & 0x3F) == 0);
+
+ CHECK(sha1->bytes == sha2->bytes);
+ CHECK(memcmp(sha1->s, sha2->s, sizeof(sha1->s)) == 0);
+}
+
+void run_nonce_function_bip340_tests(void) {
+ unsigned char tag[13] = "BIP0340/nonce";
+ unsigned char aux_tag[11] = "BIP0340/aux";
+ unsigned char algo16[16] = "BIP0340/nonce\0\0\0";
+ secp256k1_sha256 sha;
+ secp256k1_sha256 sha_optimized;
+ unsigned char nonce[32];
+ unsigned char msg[32];
+ unsigned char key[32];
+ unsigned char pk[32];
+ unsigned char aux_rand[32];
+ unsigned char *args[5];
+ int i;
+
+ /* Check that hash initialized by
+ * secp256k1_nonce_function_bip340_sha256_tagged has the expected
+ * state. */
+ secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag));
+ secp256k1_nonce_function_bip340_sha256_tagged(&sha_optimized);
+ test_sha256_eq(&sha, &sha_optimized);
+
+ /* Check that hash initialized by
+ * secp256k1_nonce_function_bip340_sha256_tagged_aux has the expected
+ * state. */
+ secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag));
+ secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha_optimized);
+ test_sha256_eq(&sha, &sha_optimized);
+
+ secp256k1_rand256(msg);
+ secp256k1_rand256(key);
+ secp256k1_rand256(pk);
+ secp256k1_rand256(aux_rand);
+
+ /* Check that a bitflip in an argument results in different nonces. */
+ args[0] = msg;
+ args[1] = key;
+ args[2] = pk;
+ args[3] = algo16;
+ args[4] = aux_rand;
+ for (i = 0; i < count; i++) {
+ nonce_function_bip340_bitflip(args, 0, 32);
+ nonce_function_bip340_bitflip(args, 1, 32);
+ nonce_function_bip340_bitflip(args, 2, 32);
+ /* Flip algo16 special case "BIP0340/nonce" */
+ nonce_function_bip340_bitflip(args, 3, 16);
+ /* Flip algo16 again */
+ nonce_function_bip340_bitflip(args, 3, 16);
+ nonce_function_bip340_bitflip(args, 4, 32);
+ }
+
+ /* NULL algo16 is disallowed */
+ CHECK(nonce_function_bip340(nonce, msg, key, pk, NULL, NULL) == 0);
+ /* Empty algo16 is fine */
+ memset(algo16, 0x00, 16);
+ CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
+ /* algo16 with terminating null bytes is fine */
+ algo16[1] = 65;
+ CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
+ /* Other algo16 is fine */
+ memset(algo16, 0xFF, 16);
+ CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
+
+ /* NULL aux_rand argument is allowed. */
+ CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
+}
+
+void test_schnorrsig_api(void) {
+ unsigned char sk1[32];
+ unsigned char sk2[32];
+ unsigned char sk3[32];
+ unsigned char msg[32];
+ secp256k1_keypair keypairs[3];
+ secp256k1_keypair invalid_keypair = { 0 };
+ secp256k1_xonly_pubkey pk[3];
+ secp256k1_xonly_pubkey zero_pk;
+ unsigned char sig[64];
+
+ /** setup **/
+ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
+ secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+ secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
+ secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ int ecount;
+
+ secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
+ secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount);
+
+ secp256k1_rand256(sk1);
+ secp256k1_rand256(sk2);
+ secp256k1_rand256(sk3);
+ secp256k1_rand256(msg);
+ CHECK(secp256k1_keypair_create(ctx, &keypairs[0], sk1) == 1);
+ CHECK(secp256k1_keypair_create(ctx, &keypairs[1], sk2) == 1);
+ CHECK(secp256k1_keypair_create(ctx, &keypairs[2], sk3) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[0], NULL, &keypairs[0]) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[1], NULL, &keypairs[1]) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[2], NULL, &keypairs[2]) == 1);
+ memset(&zero_pk, 0, sizeof(zero_pk));
+
+ /** main test body **/
+ ecount = 0;
+ CHECK(secp256k1_schnorrsig_sign(none, sig, msg, &keypairs[0], NULL, NULL) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_schnorrsig_sign(vrfy, sig, msg, &keypairs[0], NULL, NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL, NULL) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, &keypairs[0], NULL, NULL) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, NULL, &keypairs[0], NULL, NULL) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, NULL, NULL, NULL) == 0);
+ CHECK(ecount == 5);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &invalid_keypair, NULL, NULL) == 0);
+ CHECK(ecount == 6);
+
+ ecount = 0;
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL, NULL) == 1);
+ CHECK(secp256k1_schnorrsig_verify(none, sig, msg, &pk[0]) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_schnorrsig_verify(sign, sig, msg, &pk[0]) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &pk[0]) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, NULL, &pk[0]) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, NULL) == 0);
+ CHECK(ecount == 5);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &zero_pk) == 0);
+ CHECK(ecount == 6);
+
+ secp256k1_context_destroy(none);
+ secp256k1_context_destroy(sign);
+ secp256k1_context_destroy(vrfy);
+ secp256k1_context_destroy(both);
+}
+
+/* Checks that hash initialized by secp256k1_schnorrsig_sha256_tagged has the
+ * expected state. */
+void test_schnorrsig_sha256_tagged(void) {
+ char tag[17] = "BIP0340/challenge";
+ secp256k1_sha256 sha;
+ secp256k1_sha256 sha_optimized;
+
+ secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag));
+ secp256k1_schnorrsig_sha256_tagged(&sha_optimized);
+ test_sha256_eq(&sha, &sha_optimized);
+}
+
+/* Helper function for schnorrsig_bip_vectors
+ * Signs the message and checks that it's the same as expected_sig. */
+void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, unsigned char *aux_rand, const unsigned char *msg, const unsigned char *expected_sig) {
+ unsigned char sig[64];
+ secp256k1_keypair keypair;
+ secp256k1_xonly_pubkey pk, pk_expected;
+
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, aux_rand));
+ CHECK(memcmp(sig, expected_sig, 64) == 0);
+
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk_expected, pk_serialized));
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair));
+ CHECK(memcmp(&pk, &pk_expected, sizeof(pk)) == 0);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, &pk));
+}
+
+/* Helper function for schnorrsig_bip_vectors
+ * Checks that both verify and verify_batch (TODO) return the same value as expected. */
+void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig, int expected) {
+ secp256k1_xonly_pubkey pk;
+
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk, pk_serialized));
+ CHECK(expected == secp256k1_schnorrsig_verify(ctx, sig, msg32, &pk));
+}
+
+/* Test vectors according to BIP-340 ("Schnorr Signatures for secp256k1"). See
+ * https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv. */
+void test_schnorrsig_bip_vectors(void) {
+ {
+ /* Test vector 0 */
+ const unsigned char sk[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03
+ };
+ const unsigned char pk[32] = {
+ 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10,
+ 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29,
+ 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0,
+ 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9
+ };
+ unsigned char aux_rand[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const unsigned char msg[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ const unsigned char sig[64] = {
+ 0xE9, 0x07, 0x83, 0x1F, 0x80, 0x84, 0x8D, 0x10,
+ 0x69, 0xA5, 0x37, 0x1B, 0x40, 0x24, 0x10, 0x36,
+ 0x4B, 0xDF, 0x1C, 0x5F, 0x83, 0x07, 0xB0, 0x08,
+ 0x4C, 0x55, 0xF1, 0xCE, 0x2D, 0xCA, 0x82, 0x15,
+ 0x25, 0xF6, 0x6A, 0x4A, 0x85, 0xEA, 0x8B, 0x71,
+ 0xE4, 0x82, 0xA7, 0x4F, 0x38, 0x2D, 0x2C, 0xE5,
+ 0xEB, 0xEE, 0xE8, 0xFD, 0xB2, 0x17, 0x2F, 0x47,
+ 0x7D, 0xF4, 0x90, 0x0D, 0x31, 0x05, 0x36, 0xC0
+ };
+ test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
+ }
+ {
+ /* Test vector 1 */
+ const unsigned char sk[32] = {
+ 0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A,
+ 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7,
+ 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56,
+ 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF
+ };
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ unsigned char aux_rand[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x68, 0x96, 0xBD, 0x60, 0xEE, 0xAE, 0x29, 0x6D,
+ 0xB4, 0x8A, 0x22, 0x9F, 0xF7, 0x1D, 0xFE, 0x07,
+ 0x1B, 0xDE, 0x41, 0x3E, 0x6D, 0x43, 0xF9, 0x17,
+ 0xDC, 0x8D, 0xCF, 0x8C, 0x78, 0xDE, 0x33, 0x41,
+ 0x89, 0x06, 0xD1, 0x1A, 0xC9, 0x76, 0xAB, 0xCC,
+ 0xB2, 0x0B, 0x09, 0x12, 0x92, 0xBF, 0xF4, 0xEA,
+ 0x89, 0x7E, 0xFC, 0xB6, 0x39, 0xEA, 0x87, 0x1C,
+ 0xFA, 0x95, 0xF6, 0xDE, 0x33, 0x9E, 0x4B, 0x0A
+ };
+ test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
+ }
+ {
+ /* Test vector 2 */
+ const unsigned char sk[32] = {
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC9
+ };
+ const unsigned char pk[32] = {
+ 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13,
+ 0x12, 0x1F, 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC,
+ 0x01, 0x39, 0x71, 0x53, 0x09, 0xB0, 0x86, 0xC9,
+ 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8
+ };
+ unsigned char aux_rand[32] = {
+ 0xC8, 0x7A, 0xA5, 0x38, 0x24, 0xB4, 0xD7, 0xAE,
+ 0x2E, 0xB0, 0x35, 0xA2, 0xB5, 0xBB, 0xBC, 0xCC,
+ 0x08, 0x0E, 0x76, 0xCD, 0xC6, 0xD1, 0x69, 0x2C,
+ 0x4B, 0x0B, 0x62, 0xD7, 0x98, 0xE6, 0xD9, 0x06
+ };
+ const unsigned char msg[32] = {
+ 0x7E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A,
+ 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D,
+ 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33,
+ 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C
+ };
+ const unsigned char sig[64] = {
+ 0x58, 0x31, 0xAA, 0xEE, 0xD7, 0xB4, 0x4B, 0xB7,
+ 0x4E, 0x5E, 0xAB, 0x94, 0xBA, 0x9D, 0x42, 0x94,
+ 0xC4, 0x9B, 0xCF, 0x2A, 0x60, 0x72, 0x8D, 0x8B,
+ 0x4C, 0x20, 0x0F, 0x50, 0xDD, 0x31, 0x3C, 0x1B,
+ 0xAB, 0x74, 0x58, 0x79, 0xA5, 0xAD, 0x95, 0x4A,
+ 0x72, 0xC4, 0x5A, 0x91, 0xC3, 0xA5, 0x1D, 0x3C,
+ 0x7A, 0xDE, 0xA9, 0x8D, 0x82, 0xF8, 0x48, 0x1E,
+ 0x0E, 0x1E, 0x03, 0x67, 0x4A, 0x6F, 0x3F, 0xB7
+ };
+ test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
+ }
+ {
+ /* Test vector 3 */
+ const unsigned char sk[32] = {
+ 0x0B, 0x43, 0x2B, 0x26, 0x77, 0x93, 0x73, 0x81,
+ 0xAE, 0xF0, 0x5B, 0xB0, 0x2A, 0x66, 0xEC, 0xD0,
+ 0x12, 0x77, 0x30, 0x62, 0xCF, 0x3F, 0xA2, 0x54,
+ 0x9E, 0x44, 0xF5, 0x8E, 0xD2, 0x40, 0x17, 0x10
+ };
+ const unsigned char pk[32] = {
+ 0x25, 0xD1, 0xDF, 0xF9, 0x51, 0x05, 0xF5, 0x25,
+ 0x3C, 0x40, 0x22, 0xF6, 0x28, 0xA9, 0x96, 0xAD,
+ 0x3A, 0x0D, 0x95, 0xFB, 0xF2, 0x1D, 0x46, 0x8A,
+ 0x1B, 0x33, 0xF8, 0xC1, 0x60, 0xD8, 0xF5, 0x17
+ };
+ unsigned char aux_rand[32] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+ const unsigned char msg[32] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+ const unsigned char sig[64] = {
+ 0x7E, 0xB0, 0x50, 0x97, 0x57, 0xE2, 0x46, 0xF1,
+ 0x94, 0x49, 0x88, 0x56, 0x51, 0x61, 0x1C, 0xB9,
+ 0x65, 0xEC, 0xC1, 0xA1, 0x87, 0xDD, 0x51, 0xB6,
+ 0x4F, 0xDA, 0x1E, 0xDC, 0x96, 0x37, 0xD5, 0xEC,
+ 0x97, 0x58, 0x2B, 0x9C, 0xB1, 0x3D, 0xB3, 0x93,
+ 0x37, 0x05, 0xB3, 0x2B, 0xA9, 0x82, 0xAF, 0x5A,
+ 0xF2, 0x5F, 0xD7, 0x88, 0x81, 0xEB, 0xB3, 0x27,
+ 0x71, 0xFC, 0x59, 0x22, 0xEF, 0xC6, 0x6E, 0xA3
+ };
+ test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig);
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
+ }
+ {
+ /* Test vector 4 */
+ const unsigned char pk[32] = {
+ 0xD6, 0x9C, 0x35, 0x09, 0xBB, 0x99, 0xE4, 0x12,
+ 0xE6, 0x8B, 0x0F, 0xE8, 0x54, 0x4E, 0x72, 0x83,
+ 0x7D, 0xFA, 0x30, 0x74, 0x6D, 0x8B, 0xE2, 0xAA,
+ 0x65, 0x97, 0x5F, 0x29, 0xD2, 0x2D, 0xC7, 0xB9
+ };
+ const unsigned char msg[32] = {
+ 0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2,
+ 0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24,
+ 0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B,
+ 0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03
+ };
+ const unsigned char sig[64] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F,
+ 0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28,
+ 0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63,
+ 0x76, 0xAF, 0xB1, 0x54, 0x8A, 0xF6, 0x03, 0xB3,
+ 0xEB, 0x45, 0xC9, 0xF8, 0x20, 0x7D, 0xEE, 0x10,
+ 0x60, 0xCB, 0x71, 0xC0, 0x4E, 0x80, 0xF5, 0x93,
+ 0x06, 0x0B, 0x07, 0xD2, 0x83, 0x08, 0xD7, 0xF4
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1);
+ }
+ {
+ /* Test vector 5 */
+ const unsigned char pk[32] = {
+ 0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, 0x50,
+ 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, 0x21,
+ 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, 0x87,
+ 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, 0x34
+ };
+ secp256k1_xonly_pubkey pk_parsed;
+ /* No need to check the signature of the test vector as parsing the pubkey already fails */
+ CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk));
+ }
+ {
+ /* Test vector 6 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0xFF, 0xF9, 0x7B, 0xD5, 0x75, 0x5E, 0xEE, 0xA4,
+ 0x20, 0x45, 0x3A, 0x14, 0x35, 0x52, 0x35, 0xD3,
+ 0x82, 0xF6, 0x47, 0x2F, 0x85, 0x68, 0xA1, 0x8B,
+ 0x2F, 0x05, 0x7A, 0x14, 0x60, 0x29, 0x75, 0x56,
+ 0x3C, 0xC2, 0x79, 0x44, 0x64, 0x0A, 0xC6, 0x07,
+ 0xCD, 0x10, 0x7A, 0xE1, 0x09, 0x23, 0xD9, 0xEF,
+ 0x7A, 0x73, 0xC6, 0x43, 0xE1, 0x66, 0xBE, 0x5E,
+ 0xBE, 0xAF, 0xA3, 0x4B, 0x1A, 0xC5, 0x53, 0xE2
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 7 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x1F, 0xA6, 0x2E, 0x33, 0x1E, 0xDB, 0xC2, 0x1C,
+ 0x39, 0x47, 0x92, 0xD2, 0xAB, 0x11, 0x00, 0xA7,
+ 0xB4, 0x32, 0xB0, 0x13, 0xDF, 0x3F, 0x6F, 0xF4,
+ 0xF9, 0x9F, 0xCB, 0x33, 0xE0, 0xE1, 0x51, 0x5F,
+ 0x28, 0x89, 0x0B, 0x3E, 0xDB, 0x6E, 0x71, 0x89,
+ 0xB6, 0x30, 0x44, 0x8B, 0x51, 0x5C, 0xE4, 0xF8,
+ 0x62, 0x2A, 0x95, 0x4C, 0xFE, 0x54, 0x57, 0x35,
+ 0xAA, 0xEA, 0x51, 0x34, 0xFC, 0xCD, 0xB2, 0xBD
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 8 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x6C, 0xFF, 0x5C, 0x3B, 0xA8, 0x6C, 0x69, 0xEA,
+ 0x4B, 0x73, 0x76, 0xF3, 0x1A, 0x9B, 0xCB, 0x4F,
+ 0x74, 0xC1, 0x97, 0x60, 0x89, 0xB2, 0xD9, 0x96,
+ 0x3D, 0xA2, 0xE5, 0x54, 0x3E, 0x17, 0x77, 0x69,
+ 0x96, 0x17, 0x64, 0xB3, 0xAA, 0x9B, 0x2F, 0xFC,
+ 0xB6, 0xEF, 0x94, 0x7B, 0x68, 0x87, 0xA2, 0x26,
+ 0xE8, 0xD7, 0xC9, 0x3E, 0x00, 0xC5, 0xED, 0x0C,
+ 0x18, 0x34, 0xFF, 0x0D, 0x0C, 0x2E, 0x6D, 0xA6
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 9 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x12, 0x3D, 0xDA, 0x83, 0x28, 0xAF, 0x9C, 0x23,
+ 0xA9, 0x4C, 0x1F, 0xEE, 0xCF, 0xD1, 0x23, 0xBA,
+ 0x4F, 0xB7, 0x34, 0x76, 0xF0, 0xD5, 0x94, 0xDC,
+ 0xB6, 0x5C, 0x64, 0x25, 0xBD, 0x18, 0x60, 0x51
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 10 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x76, 0x15, 0xFB, 0xAF, 0x5A, 0xE2, 0x88, 0x64,
+ 0x01, 0x3C, 0x09, 0x97, 0x42, 0xDE, 0xAD, 0xB4,
+ 0xDB, 0xA8, 0x7F, 0x11, 0xAC, 0x67, 0x54, 0xF9,
+ 0x37, 0x80, 0xD5, 0xA1, 0x83, 0x7C, 0xF1, 0x97
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 11 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A,
+ 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB,
+ 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7,
+ 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D,
+ 0x69, 0xE8, 0x9B, 0x4C, 0x55, 0x64, 0xD0, 0x03,
+ 0x49, 0x10, 0x6B, 0x84, 0x97, 0x78, 0x5D, 0xD7,
+ 0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F,
+ 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 12 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F,
+ 0x69, 0xE8, 0x9B, 0x4C, 0x55, 0x64, 0xD0, 0x03,
+ 0x49, 0x10, 0x6B, 0x84, 0x97, 0x78, 0x5D, 0xD7,
+ 0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F,
+ 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 13 */
+ const unsigned char pk[32] = {
+ 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
+ 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
+ 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
+ 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
+ };
+ const unsigned char msg[32] = {
+ 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3,
+ 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44,
+ 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0,
+ 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89
+ };
+ const unsigned char sig[64] = {
+ 0x6C, 0xFF, 0x5C, 0x3B, 0xA8, 0x6C, 0x69, 0xEA,
+ 0x4B, 0x73, 0x76, 0xF3, 0x1A, 0x9B, 0xCB, 0x4F,
+ 0x74, 0xC1, 0x97, 0x60, 0x89, 0xB2, 0xD9, 0x96,
+ 0x3D, 0xA2, 0xE5, 0x54, 0x3E, 0x17, 0x77, 0x69,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
+ 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B,
+ 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41
+ };
+ test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0);
+ }
+ {
+ /* Test vector 14 */
+ const unsigned char pk[32] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30
+ };
+ secp256k1_xonly_pubkey pk_parsed;
+ /* No need to check the signature of the test vector as parsing the pubkey already fails */
+ CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk));
+ }
+}
+
+/* Nonce function that returns constant 0 */
+static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
+ (void) msg32;
+ (void) key32;
+ (void) xonly_pk32;
+ (void) algo16;
+ (void) data;
+ (void) nonce32;
+ return 0;
+}
+
+/* Nonce function that sets nonce to 0 */
+static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
+ (void) msg32;
+ (void) key32;
+ (void) xonly_pk32;
+ (void) algo16;
+ (void) data;
+
+ memset(nonce32, 0, 32);
+ return 1;
+}
+
+/* Nonce function that sets nonce to 0xFF...0xFF */
+static int nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
+ (void) msg32;
+ (void) key32;
+ (void) xonly_pk32;
+ (void) algo16;
+ (void) data;
+
+ memset(nonce32, 0xFF, 32);
+ return 1;
+}
+
+void test_schnorrsig_sign(void) {
+ unsigned char sk[32];
+ secp256k1_keypair keypair;
+ const unsigned char msg[32] = "this is a msg for a schnorrsig..";
+ unsigned char sig[64];
+ unsigned char zeros64[64] = { 0 };
+
+ secp256k1_rand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL) == 1);
+
+ /* Test different nonce functions */
+ memset(sig, 1, sizeof(sig));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_failing, NULL) == 0);
+ CHECK(memcmp(sig, zeros64, sizeof(sig)) == 0);
+ memset(&sig, 1, sizeof(sig));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_0, NULL) == 0);
+ CHECK(memcmp(sig, zeros64, sizeof(sig)) == 0);
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_overflowing, NULL) == 1);
+ CHECK(memcmp(sig, zeros64, sizeof(sig)) != 0);
+}
+
+#define N_SIGS 3
+/* Creates N_SIGS valid signatures and verifies them with verify and
+ * verify_batch (TODO). Then flips some bits and checks that verification now
+ * fails. */
+void test_schnorrsig_sign_verify(void) {
+ unsigned char sk[32];
+ unsigned char msg[N_SIGS][32];
+ unsigned char sig[N_SIGS][64];
+ size_t i;
+ secp256k1_keypair keypair;
+ secp256k1_xonly_pubkey pk;
+ secp256k1_scalar s;
+
+ secp256k1_rand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair));
+
+ for (i = 0; i < N_SIGS; i++) {
+ secp256k1_rand256(msg[i]);
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig[i], msg[i], &keypair, NULL, NULL));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], &pk));
+ }
+
+ {
+ /* Flip a few bits in the signature and in the message and check that
+ * verify and verify_batch (TODO) fail */
+ size_t sig_idx = secp256k1_rand_int(N_SIGS);
+ size_t byte_idx = secp256k1_rand_int(32);
+ unsigned char xorbyte = secp256k1_rand_int(254)+1;
+ sig[sig_idx][byte_idx] ^= xorbyte;
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ sig[sig_idx][byte_idx] ^= xorbyte;
+
+ byte_idx = secp256k1_rand_int(32);
+ sig[sig_idx][32+byte_idx] ^= xorbyte;
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ sig[sig_idx][32+byte_idx] ^= xorbyte;
+
+ byte_idx = secp256k1_rand_int(32);
+ msg[sig_idx][byte_idx] ^= xorbyte;
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ msg[sig_idx][byte_idx] ^= xorbyte;
+
+ /* Check that above bitflips have been reversed correctly */
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ }
+
+ /* Test overflowing s */
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL, NULL));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+ memset(&sig[0][32], 0xFF, 32);
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+
+ /* Test negative s */
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL, NULL));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+ secp256k1_scalar_set_b32(&s, &sig[0][32], NULL);
+ secp256k1_scalar_negate(&s, &s);
+ secp256k1_scalar_get_b32(&sig[0][32], &s);
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+}
+#undef N_SIGS
+
+void test_schnorrsig_taproot(void) {
+ unsigned char sk[32];
+ secp256k1_keypair keypair;
+ secp256k1_xonly_pubkey internal_pk;
+ unsigned char internal_pk_bytes[32];
+ secp256k1_xonly_pubkey output_pk;
+ unsigned char output_pk_bytes[32];
+ unsigned char tweak[32];
+ int pk_parity;
+ unsigned char msg[32];
+ unsigned char sig[64];
+
+ /* Create output key */
+ secp256k1_rand256(sk);
+ CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1);
+ /* In actual taproot the tweak would be hash of internal_pk */
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, tweak, &internal_pk) == 1);
+ CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1);
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk_bytes, &output_pk) == 1);
+
+ /* Key spend */
+ secp256k1_rand256(msg);
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL) == 1);
+ /* Verify key spend */
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk, output_pk_bytes) == 1);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, &output_pk) == 1);
+
+ /* Script spend */
+ CHECK(secp256k1_xonly_pubkey_serialize(ctx, internal_pk_bytes, &internal_pk) == 1);
+ /* Verify script spend */
+ CHECK(secp256k1_xonly_pubkey_parse(ctx, &internal_pk, internal_pk_bytes) == 1);
+ CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1);
+}
+
+void run_schnorrsig_tests(void) {
+ int i;
+ run_nonce_function_bip340_tests();
+
+ test_schnorrsig_api();
+ test_schnorrsig_sha256_tagged();
+ test_schnorrsig_bip_vectors();
+ for (i = 0; i < count; i++) {
+ test_schnorrsig_sign();
+ test_schnorrsig_sign_verify();
+ }
+ test_schnorrsig_taproot();
+}
+
+#endif
diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h
index 2a74703523..95d3e326c9 100644
--- a/src/secp256k1/src/scalar.h
+++ b/src/secp256k1/src/scalar.h
@@ -8,6 +8,7 @@
#define SECP256K1_SCALAR_H
#include "num.h"
+#include "util.h"
#if defined HAVE_CONFIG_H
#include "libsecp256k1-config.h"
@@ -15,12 +16,12 @@
#if defined(EXHAUSTIVE_TEST_ORDER)
#include "scalar_low.h"
-#elif defined(USE_SCALAR_4X64)
+#elif defined(SECP256K1_WIDEMUL_INT128)
#include "scalar_4x64.h"
-#elif defined(USE_SCALAR_8X32)
+#elif defined(SECP256K1_WIDEMUL_INT64)
#include "scalar_8x32.h"
#else
-#error "Please select scalar implementation"
+#error "Please select wide multiplication implementation"
#endif
/** Clear a scalar to prevent the leak of sensitive data. */
diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h
index 8f539c4bc6..7f39927861 100644
--- a/src/secp256k1/src/scalar_4x64_impl.h
+++ b/src/secp256k1/src/scalar_4x64_impl.h
@@ -192,9 +192,9 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
c0 += tl; /* overflow is handled on the next line */ \
- th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \
+ th += (c0 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
c1 += th; /* overflow is handled on the next line */ \
- c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \
+ c2 += (c1 < th); /* never overflows by contract (verified in the next line) */ \
VERIFY_CHECK((c1 >= th) || (c2 != 0)); \
}
@@ -207,7 +207,7 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
c0 += tl; /* overflow is handled on the next line */ \
- th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \
+ th += (c0 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
c1 += th; /* never overflows by contract (verified in the next line) */ \
VERIFY_CHECK(c1 >= th); \
}
@@ -221,16 +221,16 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \
- c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((th2 >= th) || (c2 != 0)); \
tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \
- th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \
+ th2 += (tl2 < tl); /* at most 0xFFFFFFFFFFFFFFFF */ \
c0 += tl2; /* overflow is handled on the next line */ \
- th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \
+ th2 += (c0 < tl2); /* second overflow is handled on the next line */ \
c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \
c1 += th2; /* overflow is handled on the next line */ \
- c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \
}
@@ -238,15 +238,15 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
#define sumadd(a) { \
unsigned int over; \
c0 += (a); /* overflow is handled on the next line */ \
- over = (c0 < (a)) ? 1 : 0; \
+ over = (c0 < (a)); \
c1 += over; /* overflow is handled on the next line */ \
- c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \
+ c2 += (c1 < over); /* never overflows by contract */ \
}
/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */
#define sumadd_fast(a) { \
c0 += (a); /* overflow is handled on the next line */ \
- c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c1 += (c0 < (a)); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \
VERIFY_CHECK(c2 == 0); \
}
diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h
index 3c372f34fe..f8c7fa7efa 100644
--- a/src/secp256k1/src/scalar_8x32_impl.h
+++ b/src/secp256k1/src/scalar_8x32_impl.h
@@ -271,9 +271,9 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
c0 += tl; /* overflow is handled on the next line */ \
- th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \
+ th += (c0 < tl); /* at most 0xFFFFFFFF */ \
c1 += th; /* overflow is handled on the next line */ \
- c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \
+ c2 += (c1 < th); /* never overflows by contract (verified in the next line) */ \
VERIFY_CHECK((c1 >= th) || (c2 != 0)); \
}
@@ -286,7 +286,7 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
c0 += tl; /* overflow is handled on the next line */ \
- th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \
+ th += (c0 < tl); /* at most 0xFFFFFFFF */ \
c1 += th; /* never overflows by contract (verified in the next line) */ \
VERIFY_CHECK(c1 >= th); \
}
@@ -300,16 +300,16 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
tl = t; \
} \
th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \
- c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c2 += (th2 < th); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((th2 >= th) || (c2 != 0)); \
tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \
- th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \
+ th2 += (tl2 < tl); /* at most 0xFFFFFFFF */ \
c0 += tl2; /* overflow is handled on the next line */ \
- th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \
+ th2 += (c0 < tl2); /* second overflow is handled on the next line */ \
c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \
c1 += th2; /* overflow is handled on the next line */ \
- c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c2 += (c1 < th2); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \
}
@@ -317,15 +317,15 @@ static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
#define sumadd(a) { \
unsigned int over; \
c0 += (a); /* overflow is handled on the next line */ \
- over = (c0 < (a)) ? 1 : 0; \
+ over = (c0 < (a)); \
c1 += over; /* overflow is handled on the next line */ \
- c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \
+ c2 += (c1 < over); /* never overflows by contract */ \
}
/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */
#define sumadd_fast(a) { \
c0 += (a); /* overflow is handled on the next line */ \
- c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \
+ c1 += (c0 < (a)); /* never overflows by contract (verified the next line) */ \
VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \
VERIFY_CHECK(c2 == 0); \
}
diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h
index 70cd73db06..2ec04b1ae9 100644
--- a/src/secp256k1/src/scalar_impl.h
+++ b/src/secp256k1/src/scalar_impl.h
@@ -16,12 +16,12 @@
#if defined(EXHAUSTIVE_TEST_ORDER)
#include "scalar_low_impl.h"
-#elif defined(USE_SCALAR_4X64)
+#elif defined(SECP256K1_WIDEMUL_INT128)
#include "scalar_4x64_impl.h"
-#elif defined(USE_SCALAR_8X32)
+#elif defined(SECP256K1_WIDEMUL_INT64)
#include "scalar_8x32_impl.h"
#else
-#error "Please select scalar implementation"
+#error "Please select wide multiplication implementation"
#endif
static const secp256k1_scalar secp256k1_scalar_one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1);
diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h
index 4cee700001..b205620224 100644
--- a/src/secp256k1/src/scratch_impl.h
+++ b/src/secp256k1/src/scratch_impl.h
@@ -11,7 +11,7 @@
#include "scratch.h"
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t size) {
- const size_t base_alloc = ((sizeof(secp256k1_scratch) + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
+ const size_t base_alloc = ROUND_TO_ALIGN(sizeof(secp256k1_scratch));
void *alloc = checked_malloc(error_callback, base_alloc + size);
secp256k1_scratch* ret = (secp256k1_scratch *)alloc;
if (ret != NULL) {
@@ -60,6 +60,10 @@ static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_c
secp256k1_callback_call(error_callback, "invalid scratch space");
return 0;
}
+ /* Ensure that multiplication will not wrap around */
+ if (ALIGNMENT > 1 && objects > SIZE_MAX/(ALIGNMENT - 1)) {
+ return 0;
+ }
if (scratch->max_size - scratch->alloc_size <= objects * (ALIGNMENT - 1)) {
return 0;
}
@@ -68,7 +72,14 @@ static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_c
static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t size) {
void *ret;
- size = ROUND_TO_ALIGN(size);
+ size_t rounded_size;
+
+ rounded_size = ROUND_TO_ALIGN(size);
+ /* Check that rounding did not wrap around */
+ if (rounded_size < size) {
+ return NULL;
+ }
+ size = rounded_size;
if (memcmp(scratch->magic, "scratch", 8) != 0) {
secp256k1_callback_call(error_callback, "invalid scratch space");
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index b03a6e6345..eaafb3a21d 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -7,6 +7,7 @@
#include "include/secp256k1.h"
#include "include/secp256k1_preallocated.h"
+#include "assumptions.h"
#include "util.h"
#include "num_impl.h"
#include "field_impl.h"
@@ -19,6 +20,7 @@
#include "eckey_impl.h"
#include "hash_impl.h"
#include "scratch_impl.h"
+#include "selftest.h"
#if defined(VALGRIND)
# include <valgrind/memcheck.h>
@@ -117,6 +119,9 @@ secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigne
size_t prealloc_size;
secp256k1_context* ret;
+ if (!secp256k1_selftest()) {
+ secp256k1_callback_call(&default_error_callback, "self test failed");
+ }
VERIFY_CHECK(prealloc != NULL);
prealloc_size = secp256k1_context_preallocated_size(flags);
ret = (secp256k1_context*)manual_alloc(&prealloc, sizeof(secp256k1_context), base, prealloc_size);
@@ -226,7 +231,7 @@ void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scr
* of the software. This is setup for use with valgrind but could be substituted with
* the appropriate instrumentation for other analysis tools.
*/
-static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, void *p, size_t len) {
+static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, const void *p, size_t len) {
#if defined(VALGRIND)
if (EXPECT(ctx->declassify,0)) VALGRIND_MAKE_MEM_DEFINED(p, len);
#else
@@ -291,7 +296,7 @@ int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *o
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(outputlen != NULL);
- ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65));
+ ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33u : 65u));
len = *outputlen;
*outputlen = 0;
ARG_CHECK(output != NULL);
@@ -548,10 +553,21 @@ int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char
return ret;
}
-int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) {
+static int secp256k1_ec_pubkey_create_helper(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_scalar *seckey_scalar, secp256k1_ge *p, const unsigned char *seckey) {
secp256k1_gej pj;
+ int ret;
+
+ ret = secp256k1_scalar_set_b32_seckey(seckey_scalar, seckey);
+ secp256k1_scalar_cmov(seckey_scalar, &secp256k1_scalar_one, !ret);
+
+ secp256k1_ecmult_gen(ecmult_gen_ctx, &pj, seckey_scalar);
+ secp256k1_ge_set_gej(p, &pj);
+ return ret;
+}
+
+int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) {
secp256k1_ge p;
- secp256k1_scalar sec;
+ secp256k1_scalar seckey_scalar;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkey != NULL);
@@ -559,15 +575,11 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(seckey != NULL);
- ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
- secp256k1_scalar_cmov(&sec, &secp256k1_scalar_one, !ret);
-
- secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
- secp256k1_ge_set_gej(&p, &pj);
+ ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey);
secp256k1_pubkey_save(pubkey, &p);
memczero(pubkey, sizeof(*pubkey), !ret);
- secp256k1_scalar_clear(&sec);
+ secp256k1_scalar_clear(&seckey_scalar);
return ret;
}
@@ -605,24 +617,31 @@ int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *p
return ret;
}
-int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
+
+static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak) {
secp256k1_scalar term;
+ int overflow = 0;
+ int ret = 0;
+
+ secp256k1_scalar_set_b32(&term, tweak, &overflow);
+ ret = (!overflow) & secp256k1_eckey_privkey_tweak_add(sec, &term);
+ secp256k1_scalar_clear(&term);
+ return ret;
+}
+
+int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) {
secp256k1_scalar sec;
int ret = 0;
- int overflow = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
ARG_CHECK(tweak != NULL);
- secp256k1_scalar_set_b32(&term, tweak, &overflow);
ret = secp256k1_scalar_set_b32_seckey(&sec, seckey);
-
- ret &= (!overflow) & secp256k1_eckey_privkey_tweak_add(&sec, &term);
+ ret &= secp256k1_ec_seckey_tweak_add_helper(&sec, tweak);
secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret);
secp256k1_scalar_get_b32(seckey, &sec);
secp256k1_scalar_clear(&sec);
- secp256k1_scalar_clear(&term);
return ret;
}
@@ -630,25 +649,26 @@ int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *
return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak);
}
+static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak) {
+ secp256k1_scalar term;
+ int overflow = 0;
+ secp256k1_scalar_set_b32(&term, tweak, &overflow);
+ return !overflow && secp256k1_eckey_pubkey_tweak_add(ecmult_ctx, p, &term);
+}
+
int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) {
secp256k1_ge p;
- secp256k1_scalar term;
int ret = 0;
- int overflow = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(pubkey != NULL);
ARG_CHECK(tweak != NULL);
- secp256k1_scalar_set_b32(&term, tweak, &overflow);
- ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey);
+ ret = secp256k1_pubkey_load(ctx, &p, pubkey);
memset(pubkey, 0, sizeof(*pubkey));
+ ret = ret && secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &p, tweak);
if (ret) {
- if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) {
- secp256k1_pubkey_save(pubkey, &p);
- } else {
- ret = 0;
- }
+ secp256k1_pubkey_save(pubkey, &p);
}
return ret;
@@ -741,3 +761,11 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *
#ifdef ENABLE_MODULE_RECOVERY
# include "modules/recovery/main_impl.h"
#endif
+
+#ifdef ENABLE_MODULE_EXTRAKEYS
+# include "modules/extrakeys/main_impl.h"
+#endif
+
+#ifdef ENABLE_MODULE_SCHNORRSIG
+# include "modules/schnorrsig/main_impl.h"
+#endif
diff --git a/src/secp256k1/src/selftest.h b/src/secp256k1/src/selftest.h
new file mode 100644
index 0000000000..885983aa20
--- /dev/null
+++ b/src/secp256k1/src/selftest.h
@@ -0,0 +1,32 @@
+/**********************************************************************
+ * Copyright (c) 2020 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef SECP256K1_SELFTEST_H
+#define SECP256K1_SELFTEST_H
+
+#include "hash.h"
+
+#include <string.h>
+
+static int secp256k1_selftest_sha256(void) {
+ static const char *input63 = "For this sample, this 63-byte string will be used as input data";
+ static const unsigned char output32[32] = {
+ 0xf0, 0x8a, 0x78, 0xcb, 0xba, 0xee, 0x08, 0x2b, 0x05, 0x2a, 0xe0, 0x70, 0x8f, 0x32, 0xfa, 0x1e,
+ 0x50, 0xc5, 0xc4, 0x21, 0xaa, 0x77, 0x2b, 0xa5, 0xdb, 0xb4, 0x06, 0xa2, 0xea, 0x6b, 0xe3, 0x42,
+ };
+ unsigned char out[32];
+ secp256k1_sha256 hasher;
+ secp256k1_sha256_initialize(&hasher);
+ secp256k1_sha256_write(&hasher, (const unsigned char*)input63, 63);
+ secp256k1_sha256_finalize(&hasher, out);
+ return memcmp(out, output32, 32) == 0;
+}
+
+static int secp256k1_selftest(void) {
+ return secp256k1_selftest_sha256();
+}
+
+#endif /* SECP256K1_SELFTEST_H */
diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h
index f1f9be077e..bcbe15a6f1 100644
--- a/src/secp256k1/src/testrand.h
+++ b/src/secp256k1/src/testrand.h
@@ -35,4 +35,7 @@ static void secp256k1_rand256_test(unsigned char *b32);
/** Generate pseudorandom bytes with long sequences of zero and one bits. */
static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len);
+/** Flip a single random bit in a byte array */
+static void secp256k1_rand_flip(unsigned char *b, size_t len);
+
#endif /* SECP256K1_TESTRAND_H */
diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h
index 30a91e5296..dfb658d9c6 100644
--- a/src/secp256k1/src/testrand_impl.h
+++ b/src/secp256k1/src/testrand_impl.h
@@ -107,4 +107,8 @@ static void secp256k1_rand256_test(unsigned char *b32) {
secp256k1_rand_bytes_test(b32, 32);
}
+static void secp256k1_rand_flip(unsigned char *b, size_t len) {
+ b[secp256k1_rand_int(len)] ^= (1 << secp256k1_rand_int(8));
+}
+
#endif /* SECP256K1_TESTRAND_IMPL_H */
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index 374ed7dc12..4780e9319b 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -182,8 +182,10 @@ void run_context_tests(int use_prealloc) {
ecount2 = 10;
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount2);
- secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL);
- CHECK(vrfy->error_callback.fn != sign->error_callback.fn);
+ /* set error callback (to a function that still aborts in case malloc() fails in secp256k1_context_clone() below) */
+ secp256k1_context_set_error_callback(sign, secp256k1_default_illegal_callback_fn, NULL);
+ CHECK(sign->error_callback.fn != vrfy->error_callback.fn);
+ CHECK(sign->error_callback.fn == secp256k1_default_illegal_callback_fn);
/* check if sizes for cloning are consistent */
CHECK(secp256k1_context_preallocated_clone_size(none) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
@@ -239,7 +241,8 @@ void run_context_tests(int use_prealloc) {
}
/* Verify that the error callback makes it across the clone. */
- CHECK(vrfy->error_callback.fn != sign->error_callback.fn);
+ CHECK(sign->error_callback.fn != vrfy->error_callback.fn);
+ CHECK(sign->error_callback.fn == secp256k1_default_illegal_callback_fn);
/* And that it resets back to default. */
secp256k1_context_set_error_callback(sign, NULL, NULL);
CHECK(vrfy->error_callback.fn == sign->error_callback.fn);
@@ -361,8 +364,8 @@ void run_scratch_tests(void) {
CHECK(scratch->alloc_size != 0);
CHECK(scratch->alloc_size % ALIGNMENT == 0);
- /* Allocating another 500 bytes fails */
- CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
+ /* Allocating another 501 bytes fails */
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 501) == NULL);
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
CHECK(scratch->alloc_size != 0);
@@ -395,6 +398,18 @@ void run_scratch_tests(void) {
secp256k1_scratch_space_destroy(none, scratch);
CHECK(ecount == 5);
+ /* Test that large integers do not wrap around in a bad way */
+ scratch = secp256k1_scratch_space_create(none, 1000);
+ /* Try max allocation with a large number of objects. Only makes sense if
+ * ALIGNMENT is greater than 1 because otherwise the objects take no extra
+ * space. */
+ CHECK(ALIGNMENT <= 1 || !secp256k1_scratch_max_allocation(&none->error_callback, scratch, (SIZE_MAX / (ALIGNMENT - 1)) + 1));
+ /* Try allocating SIZE_MAX to test wrap around which only happens if
+ * ALIGNMENT > 1, otherwise it returns NULL anyway because the scratch
+ * space is too small. */
+ CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, SIZE_MAX) == NULL);
+ secp256k1_scratch_space_destroy(none, scratch);
+
/* cleanup */
secp256k1_scratch_space_destroy(none, NULL); /* no-op */
secp256k1_context_destroy(none);
@@ -2215,6 +2230,9 @@ void test_ge(void) {
/* Normal doubling. */
secp256k1_gej_double_var(&resj, &gej[i2], NULL);
ge_equals_gej(&ref, &resj);
+ /* Constant-time doubling. */
+ secp256k1_gej_double(&resj, &gej[i2]);
+ ge_equals_gej(&ref, &resj);
}
/* Test adding opposites. */
@@ -2300,6 +2318,39 @@ void test_ge(void) {
free(zinv);
}
+
+void test_intialized_inf(void) {
+ secp256k1_ge p;
+ secp256k1_gej pj, npj, infj1, infj2, infj3;
+ secp256k1_fe zinv;
+
+ /* Test that adding P+(-P) results in a fully initalized infinity*/
+ random_group_element_test(&p);
+ secp256k1_gej_set_ge(&pj, &p);
+ secp256k1_gej_neg(&npj, &pj);
+
+ secp256k1_gej_add_var(&infj1, &pj, &npj, NULL);
+ CHECK(secp256k1_gej_is_infinity(&infj1));
+ CHECK(secp256k1_fe_is_zero(&infj1.x));
+ CHECK(secp256k1_fe_is_zero(&infj1.y));
+ CHECK(secp256k1_fe_is_zero(&infj1.z));
+
+ secp256k1_gej_add_ge_var(&infj2, &npj, &p, NULL);
+ CHECK(secp256k1_gej_is_infinity(&infj2));
+ CHECK(secp256k1_fe_is_zero(&infj2.x));
+ CHECK(secp256k1_fe_is_zero(&infj2.y));
+ CHECK(secp256k1_fe_is_zero(&infj2.z));
+
+ secp256k1_fe_set_int(&zinv, 1);
+ secp256k1_gej_add_zinv_var(&infj3, &npj, &p, &zinv);
+ CHECK(secp256k1_gej_is_infinity(&infj3));
+ CHECK(secp256k1_fe_is_zero(&infj3.x));
+ CHECK(secp256k1_fe_is_zero(&infj3.y));
+ CHECK(secp256k1_fe_is_zero(&infj3.z));
+
+
+}
+
void test_add_neg_y_diff_x(void) {
/* The point of this test is to check that we can add two points
* whose y-coordinates are negatives of each other but whose x
@@ -2373,6 +2424,7 @@ void run_ge(void) {
test_ge();
}
test_add_neg_y_diff_x();
+ test_intialized_inf();
}
void test_ec_combine(void) {
@@ -2967,14 +3019,16 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) {
secp256k1_scalar szero;
- secp256k1_scalar sc[32];
- secp256k1_ge pt[32];
+ secp256k1_scalar sc;
+ secp256k1_ge pt;
secp256k1_gej r;
ecmult_multi_data data;
secp256k1_scratch *scratch_empty;
- data.sc = sc;
- data.pt = pt;
+ random_group_element_test(&pt);
+ random_scalar_order(&sc);
+ data.sc = &sc;
+ data.pt = &pt;
secp256k1_scalar_set_int(&szero, 0);
/* Try to multiply 1 point, but scratch space is empty.*/
@@ -3232,6 +3286,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
int skew;
int bits = 256;
secp256k1_scalar num = *number;
+ secp256k1_scalar scalar_skew;
secp256k1_scalar_set_int(&x, 0);
secp256k1_scalar_set_int(&shift, 1 << w);
@@ -3262,7 +3317,8 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
secp256k1_scalar_add(&x, &x, &t);
}
/* Skew num because when encoding numbers as odd we use an offset */
- secp256k1_scalar_cadd_bit(&num, skew == 2, 1);
+ secp256k1_scalar_set_int(&scalar_skew, 1 << (skew == 2));
+ secp256k1_scalar_add(&num, &num, &scalar_skew);
CHECK(secp256k1_scalar_eq(&x, &num));
}
@@ -3374,13 +3430,32 @@ void run_wnaf(void) {
int i;
secp256k1_scalar n = {{0}};
+ test_constant_wnaf(&n, 4);
/* Sanity check: 1 and 2 are the smallest odd and even numbers and should
* have easier-to-diagnose failure modes */
n.d[0] = 1;
test_constant_wnaf(&n, 4);
n.d[0] = 2;
test_constant_wnaf(&n, 4);
- /* Test 0 */
+ /* Test -1, because it's a special case in wnaf_const */
+ n = secp256k1_scalar_one;
+ secp256k1_scalar_negate(&n, &n);
+ test_constant_wnaf(&n, 4);
+
+ /* Test -2, which may not lead to overflows in wnaf_const */
+ secp256k1_scalar_add(&n, &secp256k1_scalar_one, &secp256k1_scalar_one);
+ secp256k1_scalar_negate(&n, &n);
+ test_constant_wnaf(&n, 4);
+
+ /* Test (1/2) - 1 = 1/-2 and 1/2 = (1/-2) + 1
+ as corner cases of negation handling in wnaf_const */
+ secp256k1_scalar_inverse(&n, &n);
+ test_constant_wnaf(&n, 4);
+
+ secp256k1_scalar_add(&n, &n, &secp256k1_scalar_one);
+ test_constant_wnaf(&n, 4);
+
+ /* Test 0 for fixed wnaf */
test_fixed_wnaf_small();
/* Random tests */
for (i = 0; i < count; i++) {
@@ -5277,6 +5352,14 @@ void run_ecdsa_openssl(void) {
# include "modules/recovery/tests_impl.h"
#endif
+#ifdef ENABLE_MODULE_EXTRAKEYS
+# include "modules/extrakeys/tests_impl.h"
+#endif
+
+#ifdef ENABLE_MODULE_SCHNORRSIG
+# include "modules/schnorrsig/tests_impl.h"
+#endif
+
void run_memczero_test(void) {
unsigned char buf1[6] = {1, 2, 3, 4, 5, 6};
unsigned char buf2[sizeof(buf1)];
@@ -5583,6 +5666,14 @@ int main(int argc, char **argv) {
run_recovery_tests();
#endif
+#ifdef ENABLE_MODULE_EXTRAKEYS
+ run_extrakeys_tests();
+#endif
+
+#ifdef ENABLE_MODULE_SCHNORRSIG
+ run_schnorrsig_tests();
+#endif
+
/* util tests */
run_memczero_test();
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
index 8cca1cef21..681ed80bd0 100644
--- a/src/secp256k1/src/tests_exhaustive.c
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -22,6 +22,7 @@
#endif
#include "include/secp256k1.h"
+#include "assumptions.h"
#include "group.h"
#include "secp256k1.c"
#include "testrand_impl.h"
@@ -141,10 +142,8 @@ void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *gr
/* Check doubling */
for (i = 0; i < order; i++) {
secp256k1_gej tmp;
- if (i > 0) {
- secp256k1_gej_double_nonzero(&tmp, &groupj[i]);
- ge_equals_gej(&group[(2 * i) % order], &tmp);
- }
+ secp256k1_gej_double(&tmp, &groupj[i]);
+ ge_equals_gej(&group[(2 * i) % order], &tmp);
secp256k1_gej_double_var(&tmp, &groupj[i], NULL);
ge_equals_gej(&group[(2 * i) % order], &tmp);
}
diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h
index 8289e23e0c..a5cbe03ef5 100644
--- a/src/secp256k1/src/util.h
+++ b/src/secp256k1/src/util.h
@@ -170,13 +170,35 @@ static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_siz
# define I64uFORMAT "llu"
#endif
-#if defined(HAVE___INT128)
-# if defined(__GNUC__)
-# define SECP256K1_GNUC_EXT __extension__
-# else
-# define SECP256K1_GNUC_EXT
+#if defined(__GNUC__)
+# define SECP256K1_GNUC_EXT __extension__
+#else
+# define SECP256K1_GNUC_EXT
+#endif
+
+/* If SECP256K1_{LITTLE,BIG}_ENDIAN is not explicitly provided, infer from various other system macros. */
+#if !defined(SECP256K1_LITTLE_ENDIAN) && !defined(SECP256K1_BIG_ENDIAN)
+/* Inspired by https://github.com/rofl0r/endianness.h/blob/9853923246b065a3b52d2c43835f3819a62c7199/endianness.h#L52L73 */
+# if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
+ defined(_X86_) || defined(__x86_64__) || defined(__i386__) || \
+ defined(__i486__) || defined(__i586__) || defined(__i686__) || \
+ defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) || \
+ defined(__ARMEL__) || defined(__AARCH64EL__) || \
+ (defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__ == 1) || \
+ (defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN == 1) || \
+ defined(_M_IX86) || defined(_M_AMD64) || defined(_M_ARM) /* MSVC */
+# define SECP256K1_LITTLE_ENDIAN
# endif
-SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t;
+# if (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
+ defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) || \
+ defined(__MICROBLAZEEB__) || defined(__ARMEB__) || defined(__AARCH64EB__) || \
+ (defined(__BIG_ENDIAN__) && __BIG_ENDIAN__ == 1) || \
+ (defined(_BIG_ENDIAN) && _BIG_ENDIAN == 1)
+# define SECP256K1_BIG_ENDIAN
+# endif
+#endif
+#if defined(SECP256K1_LITTLE_ENDIAN) == defined(SECP256K1_BIG_ENDIAN)
+# error Please make sure that either SECP256K1_LITTLE_ENDIAN or SECP256K1_BIG_ENDIAN is set, see src/util.h.
#endif
/* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */
@@ -197,10 +219,15 @@ static SECP256K1_INLINE void memczero(void *s, size_t len, int flag) {
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized and non-negative.*/
static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag) {
unsigned int mask0, mask1, r_masked, a_masked;
+ /* Access flag with a volatile-qualified lvalue.
+ This prevents clang from figuring out (after inlining) that flag can
+ take only be 0 or 1, which leads to variable time code. */
+ volatile int vflag = flag;
+
/* Casting a negative int to unsigned and back to int is implementation defined behavior */
VERIFY_CHECK(*r >= 0 && *a >= 0);
- mask0 = (unsigned int)flag + ~0u;
+ mask0 = (unsigned int)vflag + ~0u;
mask1 = ~mask0;
r_masked = ((unsigned int)*r & mask0);
a_masked = ((unsigned int)*a & mask1);
@@ -208,4 +235,21 @@ static SECP256K1_INLINE void secp256k1_int_cmov(int *r, const int *a, int flag)
*r = (int)(r_masked | a_masked);
}
+/* If USE_FORCE_WIDEMUL_{INT128,INT64} is set, use that wide multiplication implementation.
+ * Otherwise use the presence of __SIZEOF_INT128__ to decide.
+ */
+#if defined(USE_FORCE_WIDEMUL_INT128)
+# define SECP256K1_WIDEMUL_INT128 1
+#elif defined(USE_FORCE_WIDEMUL_INT64)
+# define SECP256K1_WIDEMUL_INT64 1
+#elif defined(__SIZEOF_INT128__)
+# define SECP256K1_WIDEMUL_INT128 1
+#else
+# define SECP256K1_WIDEMUL_INT64 1
+#endif
+#if defined(SECP256K1_WIDEMUL_INT128)
+SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t;
+SECP256K1_GNUC_EXT typedef __int128 int128_t;
+#endif
+
#endif /* SECP256K1_UTIL_H */
diff --git a/src/secp256k1/src/valgrind_ctime_test.c b/src/secp256k1/src/valgrind_ctime_test.c
index 60a82d599e..e676a8326c 100644
--- a/src/secp256k1/src/valgrind_ctime_test.c
+++ b/src/secp256k1/src/valgrind_ctime_test.c
@@ -6,6 +6,7 @@
#include <valgrind/memcheck.h>
#include "include/secp256k1.h"
+#include "assumptions.h"
#include "util.h"
#if ENABLE_MODULE_ECDH
@@ -16,6 +17,14 @@
# include "include/secp256k1_recovery.h"
#endif
+#if ENABLE_MODULE_EXTRAKEYS
+# include "include/secp256k1_extrakeys.h"
+#endif
+
+#if ENABLE_MODULE_SCHNORRSIG
+#include "include/secp256k1_schnorrsig.h"
+#endif
+
int main(void) {
secp256k1_context* ctx;
secp256k1_ecdsa_signature signature;
@@ -32,6 +41,9 @@ int main(void) {
secp256k1_ecdsa_recoverable_signature recoverable_signature;
int recid;
#endif
+#if ENABLE_MODULE_EXTRAKEYS
+ secp256k1_keypair keypair;
+#endif
if (!RUNNING_ON_VALGRIND) {
fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
@@ -49,7 +61,9 @@ int main(void) {
msg[i] = i + 1;
}
- ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_DECLASSIFY);
+ ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN
+ | SECP256K1_CONTEXT_VERIFY
+ | SECP256K1_CONTEXT_DECLASSIFY);
/* Test keygen. */
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
@@ -114,6 +128,30 @@ int main(void) {
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret);
+ /* Test keypair_create and keypair_xonly_tweak_add. */
+#if ENABLE_MODULE_EXTRAKEYS
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_keypair_create(ctx, &keypair, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+
+ /* The tweak is not treated as a secret in keypair_tweak_add */
+ VALGRIND_MAKE_MEM_DEFINED(msg, 32);
+ ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+#endif
+
+#if ENABLE_MODULE_SCHNORRSIG
+ VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+ ret = secp256k1_keypair_create(ctx, &keypair, key);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+ ret = secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL);
+ VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+ CHECK(ret == 1);
+#endif
+
secp256k1_context_destroy(ctx);
return 0;
}
diff --git a/src/sync.cpp b/src/sync.cpp
index 4be13a3c48..322198a852 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -238,12 +238,15 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
template void AssertLockHeldInternal(const char*, const char*, int, Mutex*);
template void AssertLockHeldInternal(const char*, const char*, int, RecursiveMutex*);
-void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
+template <typename MutexType>
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs)
{
if (!LockHeld(cs)) return;
tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
abort();
}
+template void AssertLockNotHeldInternal(const char*, const char*, int, Mutex*);
+template void AssertLockNotHeldInternal(const char*, const char*, int, RecursiveMutex*);
void DeleteLock(void* cs)
{
diff --git a/src/sync.h b/src/sync.h
index 05ff2ee8a9..7b397a8003 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -53,8 +53,9 @@ void LeaveCritical();
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
std::string LocksHeld();
template <typename MutexType>
-void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs);
-void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
+void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
+template <typename MutexType>
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs);
void DeleteLock(void* cs);
bool LockStackEmpty();
@@ -69,8 +70,9 @@ inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, v
inline void LeaveCritical() {}
inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
template <typename MutexType>
-inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
-inline void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
+inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
+template <typename MutexType>
+void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) {}
inline void DeleteLock(void* cs) {}
inline bool LockStackEmpty() { return true; }
#endif
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index b3cc8cefd9..0ad5066603 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -12,6 +12,7 @@
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
+#include <crypto/sha3.h>
#include <crypto/sha512.h>
#include <random.h>
#include <test/util/setup_common.h>
@@ -750,4 +751,110 @@ BOOST_AUTO_TEST_CASE(sha256d64)
}
}
+static void TestSHA3_256(const std::string& input, const std::string& output)
+{
+ const auto in_bytes = ParseHex(input);
+ const auto out_bytes = ParseHex(output);
+
+ SHA3_256 sha;
+ // Hash the whole thing.
+ unsigned char out[SHA3_256::OUTPUT_SIZE];
+ sha.Write(in_bytes).Finalize(out);
+ assert(out_bytes.size() == sizeof(out));
+ BOOST_CHECK(std::equal(std::begin(out_bytes), std::end(out_bytes), out));
+
+ // Reset and split randomly in 3
+ sha.Reset();
+ int s1 = InsecureRandRange(in_bytes.size() + 1);
+ int s2 = InsecureRandRange(in_bytes.size() + 1 - s1);
+ int s3 = in_bytes.size() - s1 - s2;
+ sha.Write(MakeSpan(in_bytes).first(s1)).Write(MakeSpan(in_bytes).subspan(s1, s2));
+ sha.Write(MakeSpan(in_bytes).last(s3)).Finalize(out);
+ BOOST_CHECK(std::equal(std::begin(out_bytes), std::end(out_bytes), out));
+}
+
+BOOST_AUTO_TEST_CASE(keccak_tests)
+{
+ // Start with the zero state.
+ uint64_t state[25] = {0};
+ CSHA256 tester;
+ for (int i = 0; i < 262144; ++i) {
+ KeccakF(state);
+ for (int j = 0; j < 25; ++j) {
+ unsigned char buf[8];
+ WriteLE64(buf, state[j]);
+ tester.Write(buf, 8);
+ }
+ }
+ uint256 out;
+ tester.Finalize(out.begin());
+ // Expected hash of the concatenated serialized states after 1...262144 iterations of KeccakF.
+ // Verified against an independent implementation.
+ BOOST_CHECK_EQUAL(out.ToString(), "5f4a7f2eca7d57740ef9f1a077b4fc67328092ec62620447fe27ad8ed5f7e34f");
+}
+
+BOOST_AUTO_TEST_CASE(sha3_256_tests)
+{
+ // Test vectors from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/sha3/sha-3bytetestvectors.zip
+
+ // SHA3-256 Short test vectors (SHA3_256ShortMsg.rsp)
+ TestSHA3_256("", "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a");
+ TestSHA3_256("e9", "f0d04dd1e6cfc29a4460d521796852f25d9ef8d28b44ee91ff5b759d72c1e6d6");
+ TestSHA3_256("d477", "94279e8f5ccdf6e17f292b59698ab4e614dfe696a46c46da78305fc6a3146ab7");
+ TestSHA3_256("b053fa", "9d0ff086cd0ec06a682c51c094dc73abdc492004292344bd41b82a60498ccfdb");
+ TestSHA3_256("e7372105", "3a42b68ab079f28c4ca3c752296f279006c4fe78b1eb79d989777f051e4046ae");
+ TestSHA3_256("0296f2c40a", "53a018937221081d09ed0497377e32a1fa724025dfdc1871fa503d545df4b40d");
+ TestSHA3_256("e6fd42037f80", "2294f8d3834f24aa9037c431f8c233a66a57b23fa3de10530bbb6911f6e1850f");
+ TestSHA3_256("37b442385e0538", "cfa55031e716bbd7a83f2157513099e229a88891bb899d9ccd317191819998f8");
+ TestSHA3_256("8bca931c8a132d2f", "dbb8be5dec1d715bd117b24566dc3f24f2cc0c799795d0638d9537481ef1e03e");
+ TestSHA3_256("fb8dfa3a132f9813ac", "fd09b3501888445ffc8c3bb95d106440ceee469415fce1474743273094306e2e");
+ TestSHA3_256("71fbacdbf8541779c24a", "cc4e5a216b01f987f24ab9cad5eb196e89d32ed4aac85acb727e18e40ceef00e");
+ TestSHA3_256("7e8f1fd1882e4a7c49e674", "79bef78c78aa71e11a3375394c2562037cd0f82a033b48a6cc932cc43358fd9e");
+ TestSHA3_256("5c56a6b18c39e66e1b7a993a", "b697556cb30d6df448ee38b973cb6942559de4c2567b1556240188c55ec0841c");
+ TestSHA3_256("9c76ca5b6f8d1212d8e6896ad8", "69dfc3a25865f3535f18b4a7bd9c0c69d78455f1fc1f4bf4e29fc82bf32818ec");
+ TestSHA3_256("687ff7485b7eb51fe208f6ff9a1b", "fe7e68ae3e1a91944e4d1d2146d9360e5333c099a256f3711edc372bc6eeb226");
+ TestSHA3_256("4149f41be1d265e668c536b85dde41", "229a7702448c640f55dafed08a52aa0b1139657ba9fc4c5eb8587e174ecd9b92");
+ TestSHA3_256("d83c721ee51b060c5a41438a8221e040", "b87d9e4722edd3918729ded9a6d03af8256998ee088a1ae662ef4bcaff142a96");
+ TestSHA3_256("266e8cbd3e73d80df2a49cfdaf0dc39cd1", "6c2de3c95900a1bcec6bd4ca780056af4acf3aa36ee640474b6e870187f59361");
+ TestSHA3_256("a1d7ce5104eb25d6131bb8f66e1fb13f3523", "ee9062f39720b821b88be5e64621d7e0ca026a9fe7248d78150b14bdbaa40bed");
+ TestSHA3_256("d751ccd2cd65f27db539176920a70057a08a6b", "7aaca80dbeb8dc3677d18b84795985463650d72f2543e0ec709c9e70b8cd7b79");
+ TestSHA3_256("b32dec58865ab74614ea982efb93c08d9acb1bb0", "6a12e535dbfddab6d374058d92338e760b1a211451a6c09be9b61ee22f3bb467");
+ TestSHA3_256("4e0cc4f5c6dcf0e2efca1f9f129372e2dcbca57ea6", "d2b7717864e9438dd02a4f8bb0203b77e2d3cd8f8ffcf9dc684e63de5ef39f0d");
+ TestSHA3_256("d16d978dfbaecf2c8a04090f6eebdb421a5a711137a6", "7f497913318defdc60c924b3704b65ada7ca3ba203f23fb918c6fb03d4b0c0da");
+ TestSHA3_256("47249c7cb85d8f0242ab240efd164b9c8b0bd3104bba3b", "435e276f06ae73aa5d5d6018f58e0f009be351eada47b677c2f7c06455f384e7");
+ TestSHA3_256("cf549a383c0ac31eae870c40867eeb94fa1b6f3cac4473f2", "cdfd1afa793e48fd0ee5b34dfc53fbcee43e9d2ac21515e4746475453ab3831f");
+ TestSHA3_256("9b3fdf8d448680840d6284f2997d3af55ffd85f6f4b33d7f8d", "25005d10e84ff97c74a589013be42fb37f68db64bdfc7626efc0dd628077493a");
+ TestSHA3_256("6b22fe94be2d0b2528d9847e127eb6c7d6967e7ec8b9660e77cc", "157a52b0477639b3bc179667b35c1cdfbb3eef845e4486f0f84a526e940b518c");
+ TestSHA3_256("d8decafdad377904a2789551135e782e302aed8450a42cfb89600c", "3ddecf5bba51643cd77ebde2141c8545f862067b209990d4cb65bfa65f4fa0c0");
+ TestSHA3_256("938fe6afdbf14d1229e03576e532f078898769e20620ae2164f5abfa", "9511abd13c756772b852114578ef9b96f9dc7d0f2b8dcde6ea7d1bd14c518890");
+ TestSHA3_256("66eb5e7396f5b451a02f39699da4dbc50538fb10678ec39a5e28baa3c0", "540acf81810a199996a612e885781308802fe460e9c638cc022e17076be8597a");
+ TestSHA3_256("de98968c8bd9408bd562ac6efbca2b10f5769aacaa01365763e1b2ce8048", "6b2f2547781449d4fa158180a178ef68d7056121bf8a2f2f49891afc24978521");
+ TestSHA3_256("94464e8fafd82f630e6aab9aa339d981db0a372dc5c1efb177305995ae2dc0", "ea7952ad759653cd47a18004ac2dbb9cf4a1e7bba8a530cf070570c711a634ea");
+ TestSHA3_256("c178ce0f720a6d73c6cf1caa905ee724d5ba941c2e2628136e3aad7d853733ba", "64537b87892835ff0963ef9ad5145ab4cfce5d303a0cb0415b3b03f9d16e7d6b");
+ TestSHA3_256("14365d3301150d7c5ba6bb8c1fc26e9dab218fc5d01c9ed528b72482aadee9c27bef667907797d55514468f68791f053daa2df598d7db7d54beea493bdcbb0c75c7b36ad84b9996dca96354190bd96d9d7fbe8ff54ffaf77c55eb92985da50825ee3b4179f5ec88b6fa60bb361d0caf9493494fe4d28ef843f0f498a2a9331b82a", "9b690531dee948a9c559a2e0efab2ec824151a9175f2730a030b748d07cbaa7f");
+ TestSHA3_256("4a757db93f6d4c6529211d70d5f8491799c0f73ae7f24bbd2138db2eaf2c63a85063b9f7adaa03fc348f275323248334e3ffdf9798859f9cf6693d29566ff7d50976c505ecb58e543c459b39acdf4ce4b5e80a682eaa7c1f1ce5fe4acb864ff91eb6892b23165735ea49626898b40ceeb78161f5d0ea4a103cb404d937f9d1dc362b", "1ac7cc7e2e8ea14fb1b90096f41265100712c5dd41519d78b2786cfb6355af72");
+ TestSHA3_256("da11c39c77250f6264dda4b096341ff9c4cc2c900633b20ea1664bf32193f790a923112488f882450cf334819bbaca46ffb88eff0265aa803bc79ca42739e4347c6bff0bb9aa99780261ffe42be0d3b5135d03723338fb2776841a0b4bc26360f9ef769b34c2bec5ed2feb216e2fa30fa5c37430c0360ecbfba3af6fb6b8dedacbb95c", "c163cd43de224ac5c262ae39db746cfcad66074ebaec4a6da23d86b310520f21");
+ TestSHA3_256("3341ca020d4835838b0d6c8f93aaaebb7af60730d208c85283f6369f1ee27fd96d38f2674f316ef9c29c1b6b42dd59ec5236f65f5845a401adceaa4cf5bbd91cac61c21102052634e99faedd6cdddcd4426b42b6a372f29a5a5f35f51ce580bb1845a3c7cfcd447d269e8caeb9b320bb731f53fe5c969a65b12f40603a685afed86bfe53", "6c3e93f2b49f493344cc3eb1e9454f79363032beee2f7ea65b3d994b5cae438f");
+ TestSHA3_256("989fc49594afc73405bacee4dbbe7135804f800368de39e2ea3bbec04e59c6c52752927ee3aa233ba0d8aab5410240f4c109d770c8c570777c928fce9a0bec9bc5156c821e204f0f14a9ab547e0319d3e758ae9e28eb2dbc3d9f7acf51bd52f41bf23aeb6d97b5780a35ba08b94965989744edd3b1d6d67ad26c68099af85f98d0f0e4fff9", "b10adeb6a9395a48788931d45a7b4e4f69300a76d8b716c40c614c3113a0f051");
+ TestSHA3_256("e5022f4c7dfe2dbd207105e2f27aaedd5a765c27c0bc60de958b49609440501848ccf398cf66dfe8dd7d131e04f1432f32827a057b8904d218e68ba3b0398038d755bd13d5f168cfa8a11ab34c0540873940c2a62eace3552dcd6953c683fdb29983d4e417078f1988c560c9521e6f8c78997c32618fc510db282a985f868f2d973f82351d11", "3293a4b9aeb8a65e1014d3847500ffc8241594e9c4564cbd7ce978bfa50767fe");
+ TestSHA3_256("b1f6076509938432145bb15dbe1a7b2e007934be5f753908b50fd24333455970a7429f2ffbd28bd6fe1804c4688311f318fe3fcd9f6744410243e115bcb00d7e039a4fee4c326c2d119c42abd2e8f4155a44472643704cc0bc72403b8a8ab0fd4d68e04a059d6e5ed45033b906326abb4eb4147052779bad6a03b55ca5bd8b140e131bed2dfada", "f82d9602b231d332d902cb6436b15aef89acc591cb8626233ced20c0a6e80d7a");
+ TestSHA3_256("56ea14d7fcb0db748ff649aaa5d0afdc2357528a9aad6076d73b2805b53d89e73681abfad26bee6c0f3d20215295f354f538ae80990d2281be6de0f6919aa9eb048c26b524f4d91ca87b54c0c54aa9b54ad02171e8bf31e8d158a9f586e92ffce994ecce9a5185cc80364d50a6f7b94849a914242fcb73f33a86ecc83c3403630d20650ddb8cd9c4", "4beae3515ba35ec8cbd1d94567e22b0d7809c466abfbafe9610349597ba15b45");
+
+ // SHA3-256 Long test vectors (SHA3_256LongMsg.rsp)
+ TestSHA3_256("b1caa396771a09a1db9bc20543e988e359d47c2a616417bbca1b62cb02796a888fc6eeff5c0b5c3d5062fcb4256f6ae1782f492c1cf03610b4a1fb7b814c057878e1190b9835425c7a4a0e182ad1f91535ed2a35033a5d8c670e21c575ff43c194a58a82d4a1a44881dd61f9f8161fc6b998860cbe4975780be93b6f87980bad0a99aa2cb7556b478ca35d1f3746c33e2bb7c47af426641cc7bbb3425e2144820345e1d0ea5b7da2c3236a52906acdc3b4d34e474dd714c0c40bf006a3a1d889a632983814bbc4a14fe5f159aa89249e7c738b3b73666bac2a615a83fd21ae0a1ce7352ade7b278b587158fd2fabb217aa1fe31d0bda53272045598015a8ae4d8cec226fefa58daa05500906c4d85e7567", "cb5648a1d61c6c5bdacd96f81c9591debc3950dcf658145b8d996570ba881a05");
+ TestSHA3_256("712b03d9ebe78d3a032a612939c518a6166ca9a161183a7596aa35b294d19d1f962da3ff64b57494cb5656e24adcf3b50e16f4e52135d2d9de76e94aa801cf49db10e384035329c54c9455bb3a9725fd9a44f44cb9078d18d3783d46ce372c31281aecef2f8b53d5702b863d71bc5786a33dd15d9256103b5ff7572f703d5cde6695e6c84f239acd1d6512ef581330590f4ab2a114ea064a693d5f8df5d908587bc7f998cde4a8b43d8821595566597dc8b3bf9ea78b154bd8907ee6c5d4d8a851f94be510962292b7ddda04d17b79fab4c022deb400e5489639dbc448f573d5cf72073a8001b36f73ac6677351b39d9bdb900e9a1121f488a7fa0aee60682e7dc7c531c85ec0154593ded3ae70e4121cae58445d8896b549cacf22d07cdace7625d57158721b44851d796d6511c38dac28dd37cbf2d7073b407fbc813149adc485e3dacee66755443c389d2d90dc70d8ff91816c0c5d7adbad7e30772a1f3ce76c72a6a2284ec7f174aefb6e9a895c118717999421b470a9665d2728c3c60c6d3e048d58b43c0d1b5b2f00be8b64bfe453d1e8fadf5699331f9", "095dcd0bc55206d2e1e715fb7173fc16a81979f278495dfc69a6d8f3174eba5a");
+ TestSHA3_256("2a459282195123ebc6cf5782ab611a11b9487706f7795e236df3a476404f4b8c1e9904e2dc5ef29c5e06b179b8649707928c3913d1e53164747f1fa9bba6eeaf8fb759d71e32adc8c611d061345882f1cdeee3ab4cab3554adb2e43f4b01c37b4546994b25f4dcd6c497bc206865643930157cb5b2f4f25be235fa223688535907efcc253bcd083021407ea09cb1c34684aa0c1849e7efe2d9af6938c46525af9e5afb4da6e5b83da4b61dc718672a8090549cbe5aadb44f5bc93a6b3fbdc2e6d32e2eaaae637465179ea17f23ad1e4f1ebc328e2c6dc90c302b74a1edbbb0676c136b269d70c41040a313af06ab291bf489d9700950b77f207c1fc41884799931b3bca8b93331a6e96b7a3f0a8bd24cdb64964c377e0512f36444bb0643a4e3ecb328194cd5428fd89ede167472a14a9bf5730aff1e3b2c708de96eff1ebaaf63beb75f9c7d8034d6e5471e8f8a1f7efce37793a958e134619c19c54d3d42645f7a7263f25471fbaae8be3ea2fbd34ec6d7aacd7d5680948c3cd9a837c9c469a88f600d95829f4d1e4e4a5ef4ed4623c07815a1c33d9fb3b91333ff04eac92806a68a46cf2e9293f8bff466ce87fe66b46fbff7c238c7f9b2c92eb2fdc7d8084167f6f4e680d03301e5c33f78f1857d6863b1b8c36c7fce3e07d2a96a8979712079ae0023a1e3970165bfcf3a5463d2a4fdf1ca0e044f9a247528cd935734cb6d85ba53ceb95325c0eaf0ff5cd81ecb32e58917eb26bfc52dba3704bf5a927fee3220", "cb1c691c87244c0caf733aacd427f83412cd48820b358c1b15dd9fadee54e5af");
+ TestSHA3_256("32659902674c94473a283be00835eb86339d394a189a87da41dad500db27da6b6a4753b2bb219c961a227d88c6df466ba2fc1e9a2d4c982db4398778c76714d5e9940da48bc3808f3c9989131a07683b8c29d6af336e9aee1dfa57d83c48a86f17146edec07869bb06550689ebf4788159ed0a921048b4a6e3e3ec272413bec15d8e1f6a40897fa0e11d9df223ef9fc270106249ae220fdc6ebdef6d6611805421ccc850f53ee9c836baf657a94005883b5a85def344d218264f07b2ea8714afcc941096c6ded0bb6bf5b8bf652fd15a21931c58c9f526e27363ddff98c0a25bc7af9f469ab35bffea948b333f042cc18a82cec0177f33c3bdbf185b580353de79e51e675b03b31e195f19ba1f063d44def0441dc52820426c2c61cf12974ec249fd3502f017ffa06220075ced7e2d6b86a52677ba3916e8e8726062aec5bc8ea1c18b1e4137680b2c9d002191b423bee8691bd7e0f93c3b9959bc1c14d5c5cbe8f7c9c336aa16e9de9faa12f3f048c66d04cb441eb2bbc5e8a91e052c0f9000856896f9b7ba30c1e2eead36fc7ac30a7d3ddfc65caaba0e3b292d26dfba46b5e2dc9bc9acadde1c9f52b2969299bd1281ddff65822b629cfba2928613200e73661b803afdcc4a817d9361389e975e67dfadd22a797bdaf991ddf42db18711c079ecec55925f9978e478612609bacd900172011c27e24bad639ffc24a23877278318872153aef6893ccb5b68b94b33154df7334375aadd3edbb35272cc7b672dec68faa62900873ded52f6049891b77f2d0311a84b19b73660e09d1f1998095c1da1edecfa9f741b5fd6db048dd68255085d43529279021d59ed853470d6863b7c8e07fcb0d1e6acfb1eb16f7f60bb1f46ce70493010e57930a3b4b8b87e065272f6f1dd31df057627f4214e58798b664e1e40960f2789d44ccacfb3dbd8b02a68a053976711f8034c1ed3a8", "5ac9275e02543410359a3f364b2ae3b85763321fd6d374d13fe54314e5561b01");
+ TestSHA3_256("a65da8277a3b3738432bca9822d43b3d810cdad3b0ed2468d02bd269f1a416cd77392190c2dde8630eeb28a297bda786017abe9cf82f14751422ac9fff6322d5d9a33173db49792d3bc37fff501af667f7ca3dd335d028551e04039ef5a9d42a9443e1b80ea872fd945ad8999514ae4a29a35f60b0f7e971b67ae04d1ba1b53470c03847a3225c3ddf593a57aed3599661ae2d2bb1cddd2fa62c4a94b8704c5c35c33e08e2debe54e567ae21e27e7eb36593ae1c807a8ef8b5c1495b15412108aaf3fce4130520aa6e2d3bdf7b3ea609fdf9ea1c64258435aae2e58a7b3abda198f979c17dbe0aa74253e979bf3a5800f388ea11a7f7454c4e36270a3083a790c77cbe89693205b32880c0d8f79b1c000ee9b5e58f175ba7696616c17c45673cff25d1221f899836e95cc9e26a887a7115c4537e65ad4eacc319ba98a9a8860c089cbc76e7ea4c984d900b80622afbbbd1c0cdc670e3a4c523f81c77fed38b6aa988876b097da8411cc48e9b25a826460a862aa3fadfe75952aa4347c2effebdac9138ebcc6c34991e9f5b19fc2b847a87be72ff49c99ecf19d837ee3e23686cd760d9dd7adc78091bca79e42fdb9bc0120faec1a6ca52913e2a0156ba9850e1f39d712859f7fdf7daedf0e206dff67e7121e5d1590a8a068947a8657d753e83c7f009b6b2e54acc24afc9fdc9601a1d6d9d1f17aab0ce96c4d83405d1e3baba1dffa86ecccee7f1c1b80b1bbf859106ce2b647ae1e4a6a9b584ae1dfc0a4deebb755638f1d95dcc79b1be263177e2a05c72bde545d09ba726f41d9547117e876af81bfc672e33c71442eb05675d9552df1b313d1f9934f9ddd08955fa21d6edf23000a277f6f149591299a0a96032861ecdc96bb76afa05a2bffb445d61dc891bc70c13695920b911cad0df3fa842a3e2318c57556974343f69794cb8fa18c1ad624835857e4781041198aa705c4d11f3ef82e941be2aee7a770e54521312fe6facbaf1138eee08fa90fae986a5d93719aeb30ac292a49c1d91bf4574d553a92a4a6c305ab09db6bbeffd84c7aa707f1c1628a0220d6ba4ee5e960566686228a6e766d8a30dddf30ed5aa637c949950c3d0e894a7560670b6879a7d70f3c7e5ab29aed236cc3527bdea076fec8add12d784fbcf9a", "68f62c418a6b97026cc70f6abf8419b671ee373709fa13074e37bd39f0a50fcb");
+ TestSHA3_256("460f8c7aac921fa9a55800b1d04cf981717c78217cd43f98f02c5c0e66865c2eea90bcce0971a0d22bc1c74d24d9bfea054e558b38b8502fccb85f190d394f2f58f581a02d3b9cc986f07f5a67d57ab4b707bd964ecc10f94f8cc538b81eeb743746c537407b7b575ced0e1ec4c691a72eb0978be798e8be22b278b390be99c730896fdc69b6a44456be5ee261366e8b1351cbb22aa53e45ec325ed2bca0bfeeebc867d7d07681581b6d56ed66ac78280df04053407a7b57561261dd644cc7b20f0a95709e42795b5402dd89fcb11746c597e0b650a008bc085c681bb24b17db4458e1effba3f414a883ddfc4bccb3ace24d9223839d4b3ca9185ad5cc24193134b9339b0e205a4cc0fa3d8f7a85b4230d1b3ee101fbae9ee14c2153da5f337c853573bd004114cb436ee58ab1648373ee07cc39f14198ac5a02a4dd0585cf83dfd4899df88e8859dae8bc351af286642c1c25737bf8712cb941cbbb741d540feb9f5d831f901fbe2d6facd7dab626bd705f2fd7c9a7a0e7a9127e3451af2ae8509dd7b79dce41c1e30b9dba1c38cb4861dad3ac00d68fa5d07ba591c1c3b9d6b7d6e08099d0572ca4c475240601decba894fa3c4b0ea52ed687281beee268a1c8535e283b1fc7c51aa31d5ec098c50fec958acdd0d54a49643bef170093a1102a1b3bf5ad42fb55ebaf7db07385eadcd6e66da8b7b6e6c022a1e3d01f5fccec86365d3014c159a3bff17d614751b3fa0e8e89152936e159b7c0ea8d71cd4ffd83adae209b254b793f6f06bb63838a303b95c85b4edfa4ddcca0ed952165930bca87140f67f5389d1233fe04f0a3d647050410c44d389513084ad53155af00de02cc7943a3b988d8e1454f85153aff0816e24b964ec91dc514c588a93634ff3dd485c40575faa2f254abdf86fbcf6d381337601a7b1ba5b99719f045eb7bf6f2e8b9dd9d053ef0b3126f984fc9ea87a2a70b3798fab593b83a4ff44d9c0c4ec3e570ac537c10d9e3c4996027a813b70d7867b858f31f508aa56e7b087370707974b2186f02f5c549112f2158c0d365402e52cba18fe245f77f7e6fbf952ec2dc3c880b38be771caea23bc22838b1f70472d558bdf585d9c77088b7ba2dceaeb3e6f96df7d91d47da1ec42be03936d621ecf747f24f9073c122923b4161d99bc8190e24f57b6fac952ed344c7eae86a5f43c08089c28c7daf3aa7e39c59d6f1e17ece1977caf6b4a77a6ff52774521b861f38ebc978005e5763cc97123e4d17c7bc4134c8f139c7d7a9a02646fef9525d2a6871fc99747e81430b3fec38c677427c6f5e2f16c14eee646ebf6eb16775ad0957f8684c7045f7826bc3736eca", "7d495ddf961cbff060f80b509f2b9e20bed95319eef61c7adb5edeec18e64713");
+ TestSHA3_256("c8a2a26587d0126abe9ba8031f37d8a7d18219c41fe639bc7281f32d7c83c376b7d8f9770e080d98d95b320c0f402d57b7ef680da04e42dd5211aacf4426ecca5050ca596312cfae79cee0e8c92e14913cc3c66b24ece86c2bfa99078991faad7b513e94f0b601b7853ddb1eb3c9345f47445a651389d070e482ea5db48d962820257daf1cbe4bb8e5f04a3637d836c8c1bc4d83d6eda5f165f2c2592be268412712ae324ef054bb812f56b8bc25c1d59071c64dd3e00df896924c84575817027861faa5f016c5c74142272daa767e8c9dacee4c732ab08b5fa9ad65a0b74c73fb5a889169f645e50d70e41d689415f7d0b4ec071e9238b5a88110856fc6ae9b9944817e21597d1ccd03b60e60472d1e11d3e9063de24a7b59609b6a2a4ee68238690cf2800614746941c48af9566e07494f0dd236e091e75a8f769e3b179b30c10f5277eec7b3f5c97337189b8b82bc5e717ff27355b2009356caa908e976ae1d7f7a94d36202a8d5e03641aeac0e453a8168ee5a0858ceecfcbf11fb8c1f033201add297a0a89476d2ea8b9a82bda8c3c7ef4f55c3295a4ecb7c607ac73d37eadc13b7a2494ec1928f7a80c8d534efe38a3d9ccb4ccdab9f092a1def6478532c5ad3cd5c259b3812600fa89e6d1e228114795d246cedc9c9fff0d1c1297a5ddfc1169c2efb3800df8dd18a8511214785abcc1bc7eb31bdb2f5f70358dfe860ed5a03ab7e95cc21df5ee7aee68be568d6985e5c1e91408e4432663b1c4e6d613d6dc382b5b900a4fc1b7a9c27a1138c5e2356ab9026c34465006602753daf6ab7427da93c307c901d0bb1ddb21c53bc0493dd8d857161e8ffa51fdecb75568243205aa979c2e7ed2a77b5f8edc34cffb0321a8c653bc381f96ab85a86bf0bb2c9518208d636eac40aa7ad754260a75d4a46362f994c90173b975afb0ee17601311b1c51ba562c1ca7e3c2dd18b90bdebb1858fe876c71b3ad742c4bcba33e7763c750098de856fde8731cb6d698218be9f0a98298630e5b374957d126cf0b1c489c48bab6b50f6fb59ee28be6c3916bbd16514234f80e1ac15d0215852b87f9c6e429eb9f85007bf6ae3de1af0202861fd177c7c4f51af533f956a051815815c6e51e25af20d02893e95442991f1de5f86a4397ae20d9f675657bf9f397267831e94cef4e4d287f759850350ce0898f2e29de3c5c41f4246fe998a8d1359a2bed36ded1e4d6b08682025843700fee8cab56703e342212870acdd53655255b35e414fa53d9810f47a37195f22d72f6e555392023a08adc282c585b2ae62e129efccdc9fe9617eecac12b2ecdabd247a1161a17750740f90ebed3520ceb17676f1fa87259815ff415c2794c5953f689c8d5407dbbd10d1241a986e265cea901af34ec1ded0323ca3290a317208ba865637af4797e65b9cfcad3b931bbf6ac896623e2f4408529172911f1b6a9bcae8279ec7e33452d0cd7b026b46a99cbe8a69cd4d21cdc6d3a84002fab527c4fd18a121526d49890ced3fb89beb384b524015a2e03c049241eb9", "b8d4b29b086ef6d6f73802b9e7a4f2001e384c8258e7046e6779662fd958517e");
+ TestSHA3_256("3a86a182b54704a3af811e3e660abcfbaef2fb8f39bab09115c1068976ff694bb6f5a3839ae44590d73e4996d45af5ceb26b03218ab3fef6f5f4ef48d22839fb4371c270f9535357b22142c4ffb54e854b64cab41932fe888d41ca702e908c63eae244715bfbf69f481250f16f848dc881c6996e6f9d76f0e491de2c129f2a2ab22e72b04644f610a2fabc45aa2d7b3e5d77b87a135d2fd502ca74a207bddaf9a43e945245961a53c7bfcfe73a1ae090e6606ffe8ddbf1e0f0d6d4fa94526578c6faf282dd592b10bf4bce00a7b1846625690623667e83b9b59b465d42c6944e224ad36698f5f2ee938404b7775c2e66207bc41025adaf07590312f398812d24c0178126fdd334964a54b8353482a83be17cf2ee52d23b72e5f57fe31eebf8a1a64742eb9459bcb0eca231a1658ab88b7056d8e47554f0a46058d6565c6cbf6edec45fdde6f051e38255b82493de27ffd3efbe1b179b9642d2166073db6d4832707420237a00bad7125795e645e5bc3e1431ecbabf0ff5f74416626322545c966241cce6d8f2c035a78f100e030741f13b02a9eaf618d468bc40274db98bc342be12ad4d892c2ba546e571c556ac7cbf4e4c3fd3431efd40457cf65a297845dd8cce09811418c3cef941ff32c43c375157f6f49c2e893625e4b216b1f985aa0fd25f29a9011d4f59c78b037ed71f384e5de8116e3fc148c0a3cad07cb119b9829aac55eed9a299edb9abc5d017be485f690add70ff2efbb889ac6ce0da9b3bdbeb9dd47823116733d58a8d510b7f2e2c8244a2cbf53816b59e413207fb75f9c5ce1af06e67d182d3250ea3283bcbb45cb07ea6a6aa486361eb6f69199c0eb8e6490beff82e4ab274b1204e7f2f0ba097fba0332aa4c4a861771f5b3d45ce43e667581a40fee4bebe7fa9d87b70a5bb876c928f7e6d16ae604b3a4e9c7f1d616e2deab96b6207705b9a8f87468503cdd20a3c02cc8da43d046da68b5ed163d926a5a714a4df1b8ef007bca408f68b9e20de86d6398ad81df5e74d5aaac40874b5d6787211ff88e128cf1676e84ca7f51aee5951efee1915dcc11502a8df74fac4c8451dda49b631a8fb87470f0ebe9b67449bbd1640ceee6101e8cd82aa1033fa84f75b28450e461b93f65da5c43759b0e83660d50961702bb1ad015dad42e600117475237cf6e7279d4a02d1f67cf59de0108355d03963e3d84ce7647173dd7d77a6b3f275d7de74236d7bbb2df437d536136dbe1dbe8f307facc7bc7d0cde1abf745cbeb81af1ab2c46138cf007e901f22668377958bcbbadb7e9905973b27ff0c5baaece25e974c1bd116cc81dd1c81a30bae86a6fb12c6a5494068e122153128313eb3e628d76e9babc823c9eb9d3b81bacfa7a6b372abe6b1246a350f23e2e95b09c9037a75aac255ef7d4f267cad3ce869531b4165db2e5a9792094efea4ae3d9ea4d0efdc712e63df21882a353743190e016b2166e4da8a2c78e48defc7155d5fdfc4e596624e6a19c91b43719a22c1204b1cefe05989d455773d3881fa8d3eefc255f81dfe90bd41dc6f1e9c265a753298a6e98c999acd9525a9db5f9f9456a0f51a93dd9693e1d9c3fa283f7c58a9c752afcaa635abea8dfc80e2c326b939260069457fdad68c341852dcb5fcbbd351318defd7ae3b9f827478eb77306a5ae14cf8895f2bc6f0f361ffc8aa37e286629dc7e59b73a8712525e851c64d363065631edc1609f3d49a09575876a", "b71ec00c0fcc4f8663312711540df1cd236eb52f237409415b749ff9436dc331");
+ TestSHA3_256("c041e23b6d55998681802114abc73d2776967cab715572698d3d497ec66a790b0531d32f45b3c432f5b2d8039ea47de5c6060a6514f3ff8fb5f58e61fd1b5b80524c812a46dad56c035a6e95ecb465ea8176d99b836e36f65977b7dbb3932a706d3af415b6f2549b7120ecb0db1e7d9e6f8df23607eda006436bccd32ef96d431fa434d9de22ca2608ab593eb50b4d6a57f45c1ce698c3283a77d330b876ad6030324a5c0693be7790a4bd26c0a25eb403531f37689829c20546d6dc97327131688b3d88766db8f5d1b22050450c37e53951446dd7155a3e6d7edcbe1354411d8f58154475d74008937e8ba48b706066c296d1a87936dd023ac8eebe7605a58c6c40da774cf9df189db0050adcf7629e66cbd1cf9824397834cb13c4066c26e6c8ec950b44fc1c8db8ef976a7ec8c4f4ec9849ca7a07f906223053b80db24b946b034ee7a30880d0ace348acba0d0ed21ea443816706a216ce9eb682d1fe9dfc1d2e0bf3b1449247413520b8d8ebc99fc298c6dca949be0ffebe450b9b79a387a615d617b8d9da5b3e8d2776208c7cc2a11bdbc387f9d4597b380739b24ae59dcd5fb63bfeefe0746d9266cfda18afa583d6891e483e6d5c0db305f5609beba75bb5b447ccac2dfb94ede4a94db6eaaf3070d8d5353f107f7bd74528eb913e0b19bed6236a3b48567c46a9eec28fb6486f92d0d09625452d8f4dd1b89c566533cc2326b820c2b9efed43be8481cb9ad809e47af7b31795cb0fbdb18fbb12e8853f8bacec366a092daf8f2a55d2911fc7c70ddd33d33e86c2c4ceeb9390ec506b399f6fa8f35abf7789d0f547fd09cb7e6fb6016a3fc2a27a762989ae620d234c810777d5a1bb633744af2844495d2963c986ef8540ca715bed7692c77b9dec90e06acc5986b47dd4a8d3ca3300b2bedf9f26ae6d1c7e7acef05c0fc521c3309e1e70771eea6e96b67de5e3fb6833145bb73d46081b074539498307929da779e003c27f0a171035458b8c7c86c905b23dda74c040878d5a05be94821537724ebd5608ec0754c3e3e99a719bbb6d5320eed07323fca637429b18378936364c389de1e9c6fce8af270a713b4b829b43e7d761e17724c22e84611e1322dde45cbee86a0e16d01cfb8910d99391c39afd8e5f5567c59f219aa8c19ad158f287cb6807ba1fe46d38d091639a217766b3aa9ded73ac14570ba236225218305d68c0be6099c336ba8455c86b7c8c04542e729ceb84596c33ca6eb7ec1091b406cf64495ccfa2169f47b3b590477d4073537c14c05015d51ba527b3869ae4ebd603df906323658b04cb11e13bc29b34ac69f18dd49f8958f7e3f5b05ab8b8ddb34e581bde5eb49dd15698d2d2b68fe7e8baf88d8f395cfcafcdff38cf34b59386f6f77333483655ee316f12bfeb00610d8cba9e59e637ca2cab6ed24dd584143844e61fcca994ba44a4c029682997ab04285f479a6dc2c854c569073c62cd68af804aa70f4976d5b9f6b09d3738fcccb6d60e11ba97a4001062195d05a43798d5f24e9466f082ac367169f892dfd6cc0adeef82212c867a49cba65e0e636bab91e2176d3865634aa45b13c1e3e7cdb4e7872b2437f40f3de5493792c06611a9ca97d0baed71bfb4e9fdd58191198a8b371aea7f65b6e851ce22f4808377d09b6a5a9f04eddc3ff4ef9fd8bf043bb559e1df5319113cb8beea9e06b0c05c50885873acd19f6e8a109c894403a415f627cd1e8f7ca54c288c230795aaddde3a787c2a20ac6dee4913da0240d6d971f3fce31fc53087afe0c45fa3c8f744c53673bec6231abe2623029053f4be0b1557e00b291ebb212d876e88bcc81e5bd9eb820691dca5fbdcc1e7a6c58945a2cac8db2d86c2a7d98dc5908598bda78ce202ac3cd174d48ad9cac9039e27f30658eef6317cd87c199944343e7fce1b3ea7", "ad635385a289163fbaf04b5850285bfe3759774aee7fd0211d770f63985e1b44");
+ TestSHA3_256("01ec0bfc6cc56e4964808e2f1e516416717dad133061e30cb6b66b1dc213103b86b3b017fa7935457631c79e801941e3e3a0e1a3016d435e69a390eaac64f3166d944c8eb8df29fe95fdf27adc34631e4a1f3ff1d5af430f3d6f5908e40c0f83df1447274dfe30bbe76b758bd9abb40ed18331c7552dcc6959a1303e11134ec904bd0aab62de33c39703b99920851afd9d531eeb28f1c4b2e6c17c55db8296320316fbe19e881b5fcb4d266c58ca7f31d9176e26f70315330b58a516ec60d10404a78393aa03ced7acd225cb2a83caf3ab5888406a69a534f1ed1346e9b5e68831f90b872d57367361191c803eb7e38b3b9cd601282d5efdbf082db07d89bd06b093f986d08d3a7b12aa74513b6eb241b26ebf31da5726d59e315d1b4ee53ec6a9fdb6583bacc136e90e9607cab01e5d3853ab9727ede706b6f10b4e04d0510f45c0abc515bcb5ed0bcce86a92861126f4d502fcb8f988d62ecf9d124853de2bab633f9506c6fde8a36cd4413cf773e50f7b2d283482f18e2f547c2fc275cd60056ed98fb8d0816fd777c1566f0c2ae3b1cd92e344910a75e006106d193e06f7786ae37dd0e529cacf74176fd4cc1f6500549af5902dbbd56a70c194f5b671372edec425f90add40b4eb3d55123f3ab62797ad25bf5eecf4f417f86b00e6f76a4f52e44fd949851aae649dd0d26d641d4c1f343c7a2c851ca7851bbbdfd57ed6024eabc518a909a1e4689ea7bc5f83e19872950368a06e93ab41944c3d8befc5705b814e5f33511a7f7ea8a4771c804b321a3a3f32c18fa127d3c9e6c011337dc100ceb156ed45d0a62f238dacac44a3429f89bb7f98d09043c42451106e30471cc6fab7a4e1ce0a8202772b0218b631f287ec3ef82b1aa6299a0b54d6aad06aa9346d28f117d20f3b7f0d462267bd3c685cca8f4584532dfee0e8b9bacefa3092d28fcce7953a28f82e4ba6b3a1430ecca58b770dab656bed1b224663e196dffc28c96a2c65ef9de1989a125ecf2fed47eb96bef8a636a91bd521c47aeb8bc011bf81cc688fd8b620446353cbf7692201b5552cb07fb02eb3954dfaa6f5c31bf91e20b84419dcbbdaba0c31a124d8f4218b2f88da3eba44dbe40eb290052538dccd0ff7670de5f33a83ff74895b66adcff58c9c21e93b31bb49ccb2e026995ee155b5517b72daa76526a2e42aa6fa94357cd42e2a8a1d3e7d4cefc33d5d07d6303d798d2551a21f862b5f492d0c7cf078a77007a02847b34675dfad4fb457e9f20dc5750fb127a3c31b9d6a3996d50ac3ffc6ef29cca1d8414d0438bf3271dc4f4e00cfe19a507b447dc310f74aeb2a3c0b3fae6d7d13f4935bc72c35df3efa6e879164421505ee32d93b030e32a7970b53430b1643855167278e5058c4a48a7840e2fcdb282e45b5b86c0b2756f19b595f3bcfc926df35e33ac26dd1e88cd394015a5f54deb4c9f4a0bef0eabcb27c4eb88dc2302f09e92f1bcc4b4754df1eeb536154543c7dbf181c9979fe6ed08311e5a3acf365ebb5745212b2630e83b3a5bd5fa4834c727248b165700c7435f8cb6ee455bad16ee0da68fe6acd2062dae9c8bc178b157b29ade98a9bbbd4c723a3dcb7852c7978b488e4f73a2c9163dbdffae175119f812b6f4b70c2b498704bc2b58603f167f277a74e64ec296a6dfdb0de3486c0f36ac1b55f80af9fc817ba4f84b898b2a3c5725e2faf466bb26a8a84f91e123d182033a7ae2029236aa4b673ceb50c1733d7edd60e3f119b7141c882d508e0331689c96fbfb9f7e888fe88561de427c721123036737c1460b0da00d3f958b948f68fcb321ab4e297290f781ff8afb06b755d82a7e6ce1963761d799eed786524bf19801b4877b2d856becdf7e87d71aa359f2d51f09de64bcbf27d0c3aceac70790e314fd06c2f5216f3d10574b7302d6bc2775b185145c1b741524567c456d42c5826f93afa20ae7196ca7224c3b69b1eada9eee752fb6d43f24170fcc02af7e1dea73f0f884f936f900165800acb9d57480a31e409d3f676ed92b6812cf182a088fc49d68082aa19c7be0711f436db1d7be44d97dc9405591a8d3e7f6f731c6f3e6c401749829b7624497f5eeac1fc782e7d6988340541f2617a317e", "2a6283b1c02c6aaf74c4155091ff54a904bb700077f96a9c4bd84e8e51b54d01");
+ TestSHA3_256("9271fd111dcf260c04cf4b748f269ac80f7485c41f7724352a7ed40b2e2125b0bf30f3984ee9d21aab6eb07ec976b557c2426e131ad32bd0485aa57172f0e4f1798760f8352067ac023fbeca7b9c8bf5851c724e90ffff44195b44ae73c9c317c85e8e585bddac6d0f2abf812d02e44b62eadb9d0765683aa56af8e9b91588c7b49dc3e146866a02dc18f9ca680f88006094ef29096c2d5af5700b4aca3dfcab462c48bb8085691671efb5ceb22b3ebd8702f71a1d7c184b1053c3fa30a7e76b85f3650d9140714fd4993bb496becf2ae01d3a98ccfdefb6fefd692173bd11af7adb61ffff214a550ffcd3a5993004ee72cb02ca9c577b42c85444e619e6411e2bca86bb548ebbd12a02c5c945eaa3b246f595d817f3849875429e72ac894160a2a91a6617f18e6b2b9258472152741d62843cebc537d25f0daebdedb410d71ee761662bd1b189ca1c93d648b5d141d8d05e3f2b2d8c2c40997fea7eb7e2cc0000d8b2300936759704ef85f38ad0d08a986de6bfd75b5be3209f6d4d3f67e7adf7f8469d47e81979ec8dae7127b5eadcc09779cf4b0a28efaaf58e83d307f2ced4a8699b142f3f19db5598e914e9577652c63f851203580d40699548fc2ab30a9dcf6452f673ad1ed92f8d84dad5dfff55e18107b3acb6e4e8e3c9c34038f40a5c577fe9771c2c31ef03d36a00e04a20d2d0877db66f091dac4b741d2a997b75182702881f9284fa23b9b3c20e715f80d07b9910a4b3185f9489dc7d3fb510f4da273559753d7d207f3975b48df2e7c857caffe703dfac53a786490c09f57d2fa93f60810186df4c0b6b616a04caab9f70a5002c5e5d8da0ed2805f20fbf89cd8d57ca2b4bd37125ce38bf09fb6170ae21f4e6043a9483ef6e585756d97cfb778e57bc7ddc8dfc54d086d6bcfa1f019c749ff79921ec56e833ff8660f0959cd4b5277e9f3b1d4880193fefa98a6c2512718e7c139acdcd324303db3adb70348d09b058baf0e91d52b24952f832b0a3b81fa9bc9a2e9fb276a64e9e0922778b4992d892f6845b4372a28e47d27b53443586d9015463cacb5b65c617f84e1168b15988737a7eda8187f1f4165fecbdd032ae04916cc4b6e18a87558d2ce6a5946c65a9446f66cda139a76506c60d560f56a013b508d6ccbbaa14e24ad0729dd823bf214efcc59e6932cdc860306687c84a63efb551237223641554940a7a60fa7e6ddad64a21b4a2176b046dc480b6c5b5ff7ed96e3211df609195b4028756c22479ba278105771493870372abe24dcc407daa69878b12b845908cf2e220e7fabeeaab88c8f64f864c2bacba0c14b2a693e45aacc6b7db76bc1a2195cfce7b68f3c99440477ea4c1ea5ee78c109f4f1b553c76eb513dd6e16c383ce7f3187ad66c1d5c982724de8e16299c2fde0a8af22e8de56e50a56ac0fef1c52e76864c0ad1eeedd8907065b37892b3eca0ddcdf5c8e0917dec78fedd194ea4b380a059ccc9452e48a9eba2f8b7a4150b7ba17feac83c61604c3cfcfe6655c2be37ef0ae6fc29072f9b1cfb277b64a8d499dd079ad9aa3d5e9a7ccbec8c100596c6fac51e13a260d78d8cd9066edc558e2219cfcda1310dc1fbbdd36f348756855349f33eb6b82186a8c1a55f361305833edd3e4ac8d9b9cf99897c4e06c19ed10765fd0c8c7433851445c5f87b119ef913b2bcdbf7aa2ad19c672e53a9c6c3c309d549513edd7c1cf8a0a399e6df0939cc1fb146d6ad460e2ce05144c69eafa3822141d473fbe5927c58a50c1e842f8b8fad85540ce9f6d06f7b4dea045248b999d24c5fd4d75631caf73518cc08f73684e2a1cd4266235d90c08a0d0ce8784c776fd1b80978b83f0705ba8498744884d5496b791f2db3ffb5377175856b25a643803aa8b9e7f1055e089c1929cf0cbba7674c204c4590fb076968e918e0390d268eeef78c2aebcbf58a429f28212a2425c6ad8970b6a09cadddd8336d519bca4820556d2c4b8cd9f41216de3c728a0774edf47d3489cd29cf1b2a192bc53325d0bed7d23e51be7684297f9d0ecb14acbf648bc440c5fde997acc464fb45e965e6f0dced6d4568ebcd55e5a64633b05a2cb4d8263b721a252b1710dc84d8a5d4b43fcc875e2e7281f621b0bf8bb3465be364456bcd990b26b3e474486f864fb85f320f68bc14c37d271249b18552bef50dfc385a9f41b831589c5a716357cf5a12520d582d00452a8ab21643dd180071d2041bbc5972099141c6292009540d02f3252f1f59f8dfcf4488803f3b0df41759055559a334e68c98ea491b0984f2f82a35db84ea0779b3801cf06b463a832e", "4e75bf3c580474575c96ec7faa03feb732379f95660b77149974133644f5d2a0");
+ TestSHA3_256("075997f09ab1980a3179d4da78c2e914a1ff48f34e5d3c2ab157281ef1841052d0b45a228c3cd6b5028efd2d190d76205e1fdf4cec83c9868fe504f429af1e7c5423267c48a7b5bc005f30a1980147a3fae5c100b95c7cb23d43af9f21d87311d9cc826598993e077015f59ebc476383bb7a78787d915c97039ab188a2a618f7a8d7f64542ba787e9dd7d48c4c87d2aaea068c1b00c9711b2812901673c11418096d0a850fb36b0acece56d311689dfeceb0835009adc427f6d2d6b05ed26f5a43b6478bc72c1f914a2202dbd393cb69b1a1e78162e55ca4b3030ac0298131a7a0d934c032cc9dfc5afa600c59b064d2d9013f15d1184278a8ccb5ad9d7563e666fe5a8c173cec34467ef9cf6d6671208ff714741fee7c8d1d565edf82570dffde4f3f584024142056d8548ad55df83d1babed06141114c95ac88dbea0ce35d950f16d8a732a1ea7d22dfaa75a3e0410c546523277261116a64bcbb2be83e55e040f6c8c79f911b301a8718cc4b19a81d5f0cb6312d87c5b4b079e23a61d247541cfc2c41a37f52b2c6e43a3db5dc47892d0e1feabcc5c808f2391791e45fb065159f99c1d8dd2f69baaf75267eb89dd460f1b6c0badb96cbbc8291cefa370fa7ad6997a4ca2b1fe968216032f02f29837d40215fa219c09161df074e1de8e37056e28c86d1f992a651e271dfc4b0592ad481c613fd00c3eea4b6deabb9f5aa63a4830ed49ab93624fa7b208966eccb1f293f4b9a46411f37d7928e4478dde2f608d3851a8efa68e9d45402bc5124fde4ddc0f83ef82b31019d0aacb4b5121bbc064c95c5292da97981f58f051df9502054bf728e9d4fb7e04787a0890922b30a3f66a760e3d3763855e82be017fa603630a33115a02f02386982001def905784f6ba307a598c6dbaf2946fe9e978acbaf3e4ba50ab49ae8e9582520fc2eb6790deafc77e04a8ee75da92d16f0d249403112c74bc09102b573e110ccb4d8461d249bfe2e85fc9770d606be6fbfd5ec4c30ac306d46412f736e5b696ccc9fbe4adea730955c55ea5c63678271d34b7bd6f6340e72626d290820eeb96a0d2d25ea81361a122ffe8e954cf4ff84f4dafcc5c9d3e7c2ddbdf95ed2c0862d3f2783e4566f450ec49e8b01d9d7bf11e92a7903f2b045c57ed8a65ccbfc5b1d2a38e020a57b38f2e4deea8a52354a7e7be4f977b8f5afe30f6738e955c8bda295064586b6827b245766b217fe39263572b0850965c7ae845611b8efb64c36244a39b9fed0ab970ee5ddeb8f2608dd9c963524a14050c9101d7f2d5537b24d0b0f7a45703c1e131656ec9edc12cdf71dae1cde2790b888ef2a589f03201f8bbfad71f0c4430477a6713ad2e50aaefa1f840cbb839e277389454517e0b9bd76a8ecc5c2e22b854c25ff708f9256d3700adeaec49eb2c4134638ee9bd649b4982f931ec3b23cc819fbc835ddcb3d65e04585aa005e13b7ef8fcafa36cc1a2c79ba6c26fc1dc0f6668f9432c578088cd33a41a778ac0b298fcac212edab724c9fb33d827409fd36bc4b2b0e4e81006fd050d94d3271e0427c61e9ddca599a3c9480cfdd33603cb1a196557281ce6a375fef17463893db293dba0704d4bfda25e08beadd4208c58ea0d8d9066448910b087fc13792fc44075a3fe42e13c5792f093a552aa8ebe0f63e7a807102d5bc145468a0cb469263035c5647049054c18199f7da6d6defd51105e2125c605e327aca137ca85e3f7f46ca69f92d5252f84418293f4e9afeeb067c79576e88cc3c64f3e61d76e1e9e2f72cdfc35261a9679f0c374d7436ff6cfe2ba71650810522fa554a4aded87ad23f0b206b1bc63f56bbff8bcc8849d99e209bd519a953f32c667aa8cd874ad99846ed94b92f88fe0dbf788c8431dc76ca9553692622077da2cdea666c1b3fee7c335da37737afccd3d400a23d18f5bd3784dbcd0663a38acb5a2beef03fc0a1c52ee0b56bda4493f2221e35bee59f962f16bc6781133204f032c7a6209dd3dabd6100325ec14e3ab0d05aadd03fdfe9f8737da15edab9d2598046f8c6dd8381aaf244821994d5a956073c733bcebf9edbc2a6e2676242dc4e6a2e4ba8a7d57ed509340d61fae2c82bee4dedc73b469e202cc0916250d40a1718090690a1d3b986cf593b019b7b7f79ae14843b2e7ccf0fd85218184f7844fbb35e934476841b056b3a75bf20abb6866e19a0614e6a1af0eee4de510535724363b6598cccf08a99066021653177559c57e5aaff4417670a98fe4bd41a137c384f98c0324c20ef8bc851a9b975e9440191ff08deb78c9fa6fc29c76b371a4a1fa08c30fc9d1b3323d897738495086bfd43ef24c650cfa80c42ecbadc0453c4437d1a11b467e93ca95fbae98d38dcb2da953e657fb7ea6c8493d08cf028c5d3eb0fcbcb205493f4658440719e076e02deb07332d093e4d256175ca56f4c785d5e7e26c6090a20429f70b3757daac54153bc16f5828dc6c1c9f5186e2117754be5f1b46b3631980d9e4a9a5c", "2e07737d271b9a0162eb2f4be1be54887118c462317eb6bd9f9baf1e24111848");
+ TestSHA3_256("119a356f8c0790bbd5e9f3b4c5c4a70e97f462364c88cad04d5435645342b35484e94e12df61908fd95546f74859849b817ee92fbd242435c210b7b9bfbffb3f77f965faa1a9073e8feb5a380f673add8fde32208402fa680c8b3e41d187a15131f1028f9d86feaf3fd4b6e0e094d2ba0839c67267c9535173ec51645343ad74fcfaae389aa17cca3137e2588488531c36ba2b8e2f2238d8415c798a0b9a258f1e3cef605fa18977ad3d6707c3ecc5ea5f86ebdaa4b4b0e5bc023d1bc335138ae0de506cb52f2d9efa0ecc546468310cccc88ec08d28c3602e07257f41bb7e4d8a0956c564f3712761d199a931a39e69c5a69aa7b3257931dd92b91e4ed56fbf64e48bd334945cfa2aaf576df04614eb914899f7df54db4012cc8261b12bedcab69876feedbbf7009dcf8d076af89b797ad71217d75cf07514dc07ae34640055c74c9faf560f491f015ac3e167623cfbc67b8e7163e7c1b92debd06e9d28b049e0298f4c38395a40a0778162af2cfe5abe5b946c4d9a54f2a321660ab521068c4957cd3f5be0324cc04f50f209fdea7caaa0ac705c1fb30abfa550e844f509074afde1ee87adda29aa09b7f93e7d064ad2715ee5571ee6e7c9a01672124cc2a22b4354c3844759c1a6ce3fdf17555cac7df73334073ef3730939410fe6cf37463352ad241958b7fafbc66e0d592df48bf55ab2c33428e494c6995826892572d9ab52747b1085fcbef318cfe9cfacd4cd80c164fba584c1344ae7e321c4f77b44db6b322f2f7831f4d4ede7dc407b065c6754cf4424f9903adb4c6b675ded58700bc36da83fd95e84e76c404f7342921ef23d7d07772f5b8ec2f077601cae13448385b04e074f895574be61a831a87efd68a1f6aa67cf291847fa8f74cbe3b4a78ad780895183bc51c30ad2514255d4e013abc097bc8103c0b1933b0b303341836ae167c1e31dfe5f1b791cb06ef29cae398065343eecf06e4ae2048d1547c4bf69ccec5e86c45867c633c62f7d27dc51234b6debb5b9f80a5810716240c64443d0c098c80220d0520a5f5834369b9eb019325e23e88f237c24440bf27959caf7e7e4f1671fda710630255a2962f7e9b3625dc243a0177aacf6a758a68aa85dc3f56181a4a59a406c7fae5575c9e2c64248f520b5a5f904821661e2e43a5a058f445fd0e55b07476c4122d18033053b45112201e0bfdcc9e7cb9931155018ca431a0564930aca8defbca954b2680753a4060bec2cb668d2c15e77cba29589b5c7c07bc7177a8b1adb3a6968732f9213476fd96901514626fa17243af1d156cd037eea81d773f1f71a018d942b524b851794b300c7591ecd783ec8066ccb261bdf9b7a183dbda42b92593b614297dcb0fabcc23ae69797d0251b8ab57a4da2a544615216b01f4dbe2d8c9b5520c7ed9cd9312e9ec6d05a36e7f693d1821d727518169b03976394b9d1e1d7fa2daa25529d391eb5d0cf0f07a8160be2ee043d9345037c655c4f2023689f14d8d2072dd92c1dba056a5b5d4c4fc4196e25caab05b1701ec666ac9a04d90f7d7575a7ac3970252c18fd3bec0cc448e5ff8f3765d546a4a8ad1d41a9640c79375b80534b7b50989976f238654fefea981c9413130beae943a3e9d8f64ce9256d1259d1b2a6b3c02ca5af1a701db8f25a4e9c255dad8785172f323728c3585a45206ae988c283e30a2f9ea9b47f07a7521b0f36e9c504c14bd96027e8d24161e70f196576d8a74a5e9c26acda7cc452a90e550e625a49e50829db70de808c827c67d00c23ee073d4e72aeed891dd73b86acd6756e753e3975a80cdab1d521052caef6a5380f8b03023ba0326a6928aa127ffb33b51dcb05bbdd592d0ad9e8321e6ef2f95c401be6a37e634425689fe7750e2a0fe05ad89001502b309095ca517b2e2ed0388b9f2c59c45feb61222539d6e1ccd397344d23708aebacec10ada96a7711f173f7ff1e4b94fceec6a0a0ea5d814a4581b412063012ff6ac5527b8314d00326b68c2304a276a217fde9fa4034750a2e47e10f816870d12fc4641a27a1c16c35a953f32685f2b92cae0519848045765591c42ddc402dc7c6914d74dd38d2b5e7f35358cb1d91a9f681fde7fd6c7af5840663525ee1d04bf6d3156fed018c44043d95383d92dada3d1cd84af51d9bee814ec8675073e1c48632c5f59e257682542e1f7dc20b56b5c92a9e2cb2be30cb1512fb55fa1de99a3f5864ed3acc19d79e6ffb0da3b08ba0615157747d75c1f308fa0202a4086f34e9eafa3e071dfbacaca731d228aa0304cf390c0a9e6ad7ce22ade758965cfbfc4c9390d24e41a667447fc7b29821464ad98bc5d65dc7f9c42bd4b23e174015592ff92c905660a2722f9fc7973d3cdad848ef88bf02b1b03dea16699b71dc46b35bc4d96069a0753335ae38685d244918e30c5fb0d45283a1281c1659ea591573999d9c2acd2ca9141d55230d41011b70748b518e1cd2fa58ad8dc05fcbdf0bffaf2c7fd6cb2ac67bb13b8f6d31fad64ac113664223599dca411270955c95aec06518894dabc352d2b70984727437040d944da7b42e0ef560ac532de3e4a4891e8509c275b51ed780f8660b0354e12c21b3e11bcc88198980b5f7ff31ad342182d5a933373164dced3cfb2a081720d7eee676cb7378a3e19326a7ee67fd6c00521f9de37c66bcea814b6feb6a061b8cdcf7b4bd8f45d48602c5", "c26d0b064e409df64819cd7c1a3b8076f19815b9823adac4e3ce0b4d3a29de18");
+ TestSHA3_256("72c57c359e10684d0517e46653a02d18d29eff803eb009e4d5eb9e95add9ad1a4ac1f38a70296f3a369a16985ca3c957de2084cdc9bdd8994eb59b8815e0debad4ec1f001feac089820db8becdaf896aaf95721e8674e5d476b43bd2b873a7d135cd685f545b438210f9319e4dcd55986c85303c1ddf18dc746fe63a409df0a998ed376eb683e16c09e6e9018504152b3e7628ef350659fb716e058a5263a18823d2f2f6ee6a8091945a48ae1c5cb1694cf2c1fe76ef9177953afe8899cfa2b7fe0603bfa3180937dadfb66fbbdd119bbf8063338aa4a699075a3bfdbae8db7e5211d0917e9665a702fc9b0a0a901d08bea97654162d82a9f05622b060b634244779c33427eb7a29353a5f48b07cbefa72f3622ac5900bef77b71d6b314296f304c8426f451f32049b1f6af156a9dab702e8907d3cd72bb2c50493f4d593e731b285b70c803b74825b3524cda3205a8897106615260ac93c01c5ec14f5b11127783989d1824527e99e04f6a340e827b559f24db9292fcdd354838f9339a5fa1d7f6b2087f04835828b13463dd40927866f16ae33ed501ec0e6c4e63948768c5aeea3e4f6754985954bea7d61088c44430204ef491b74a64bde1358cecb2cad28ee6a3de5b752ff6a051104d88478653339457ac45ba44cbb65f54d1969d047cda746931d5e6a8b48e211416aefd5729f3d60b56b54e7f85aa2f42de3cb69419240c24e67139a11790a709edef2ac52cf35dd0a08af45926ebe9761f498ff83bfe263d6897ee97943a4b982fe3404ef0b4a45e06113c60340e0664f14799bf59cb4b3934b465fabefd87155905ee5309ba41e9e402973311831ea600b16437f71df39ee77130490c4d0227e5d1757fdc66af3ae6b9953053ed9aafca0160209858a7d4dd38fe10e0cb153672d08633ed6c54977aa0a6e67f9ff2f8c9d22dd7b21de08192960fd0e0da68d77c8d810db11dcaa61c725cd4092cbff76c8e1debd8d0361bb3f2e607911d45716f53067bdc0d89dd4889177765166a424e9fc0cb711201099dda213355e6639ac7eb86eca2ae0ab38b7f674f37ef8a6fcca1a6f52f55d9e1dcd631d2c3c82bba129172feb991d5af51afecd9d61a88b6832e4107480e392aed61a8644f551665ebff6b20953b635737a4f895e429fddcfe801f606fbda74b3bf6f5767d0fac14907fcfd0aa1d4c11b9e91b01d68052399b51a29f1ae6acd965109977c14a555cbcbd21ad8cb9f8853506d4bc21c01e62d61d7b21be1b923be54914e6b0a7ca84dd11f1159193e1184568a6134a6bbadf5b4df986edcf2019390ae841cfaa44435e28ce877d3dae4177992fa5d4e5c005876dbe3d1e63bec7dcc0942762b48b1ecc6c1a918409a8a72812a1e245c0c67be6e729c2b49bc6ee4d24a8f63e78e75db45655c26a9a78aff36fcd67117f26b8f654dca664b9f0e30681874cb749e1a692720078856286c2560b0292cc837933423147569350955c9571bf8941ba128fd339cb4268f46b94bc6ee203eb7026813706ea51c4f24c91866fc23a724bf2501327e6ae89c29f8db315dc28d2c7c719514036367e018f4835f63fdecd71f9bdced7132b6c4f8b13c69a517026fcd3622d67cb632320d5e7308f78f4b7cea11f6291b137851dc6cd6366f2785c71c3f237f81a7658b2a8d512b61e0ad5a4710b7b124151689fcb2116063fbff7e9115fed7b93de834970b838e49f8f8ba5f1f874c354078b5810a55ae289a56da563f1da6cd80a3757d6073fa55e016e45ac6cec1f69d871c92fd0ae9670c74249045e6b464787f9504128736309fed205f8df4d90e332908581298d9c75a3fa36ab0c3c9272e62de53ab290c803d67b696fd615c260a47bffad16746f18ba1a10a061bacbea9369693b3c042eec36bed289d7d12e52bca8aa1c2dff88ca7816498d25626d0f1e106ebb0b4a12138e00f3df5b1c2f49d98b1756e69b641b7c6353d99dbff050f4d76842c6cf1c2a4b062fc8e6336fa689b7c9d5c6b4ab8c15a5c20e514ff070a602d85ae52fa7810c22f8eeffd34a095b93342144f7a98d024216b3d68ed7bea047517bfcd83ec83febd1ba0e5858e2bdc1d8b1f7b0f89e90ccc432a3f930cb8209462e64556c5054c56ca2a85f16b32eb83a10459d13516faa4d23302b7607b9bd38dab2239ac9e9440c314433fdfb3ceadab4b4f87415ed6f240e017221f3b5f7ac196cdf54957bec42fe6893994b46de3d27dc7fb58ca88feb5b9e79cf20053d12530ac524337b22a3629bea52f40b06d3e2128f32060f9105847daed81d35f20e2002817434659baff64494c5b5c7f9216bfda38412a0f70511159dc73bb6bae1f8eaa0ef08d99bcb31f94f6be12c29c83df45926430b366c99fca3270c15fc4056398fdf3135b7779e3066a006961d1ac0ad1c83179ce39e87a96b722ec23aabc065badf3e188347a360772ca6a447abac7e6a44f0d4632d52926332e44a0a86bff5ce699fd063bdda3ffd4c41b53ded49fecec67f40599b934e16e3fd1bc063ad7026f8d71bfd4cbaf56599586774723194b692036f1b6bb242e2ffb9c600b5215b412764599476ce475c9e5b396fbcebd6be323dcf4d0048077400aac7500db41dc95fc7f7edbe7c9c2ec5ea89943fe13b42217eef530bbd023671509e12dfce4e1c1c82955d965e6a68aa66f6967dba48feda572db1f099d9a6dc4bc8edade852b5e824a06890dc48a6a6510ecaf8cf7620d757290e3166d431abecc624fa9ac2234d2eb783308ead45544910c633a94964b2ef5fbc409cb8835ac4147d384e12e0a5e13951f7de0ee13eafcb0ca0c04946d7804040c0a3cd088352424b097adb7aad1ca4495952f3e6c0158c02d2bcec33bfda69301434a84d9027ce02c0b9725dad118", "d894b86261436362e64241e61f6b3e6589daf64dc641f60570c4c0bf3b1f2ca3");
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index c0a2fca9ca..e4ee08db61 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -47,7 +47,6 @@ struct CConnmanTest : public CConnman {
extern bool AddOrphanTx(const CTransactionRef& tx, NodeId peer);
extern void EraseOrphansFor(NodeId peer);
extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
-extern void Misbehaving(NodeId nodeid, int howmuch, const std::string& message="");
struct COrphanTx {
CTransactionRef tx;
@@ -79,12 +78,13 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup)
// work.
BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
{
+ const CChainParams& chainparams = Params();
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(*connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool);
// Mock an outbound peer
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::OUTBOUND);
+ CNode dummyNode1(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY);
dummyNode1.SetSendVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
@@ -133,10 +133,10 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
}
-static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic, CConnmanTest* connman)
+static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
{
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
- vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", ConnectionType::OUTBOUND));
+ vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK | NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", ConnectionType::OUTBOUND_FULL_RELAY));
CNode &node = *vNodes.back();
node.SetSendVersion(PROTOCOL_VERSION);
@@ -149,10 +149,10 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidat
BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
{
+ const CChainParams& chainparams = Params();
auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(*connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, nullptr, *m_node.scheduler, *m_node.chainman, *m_node.mempool);
- const Consensus::Params& consensusParams = Params().GetConsensus();
constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS;
CConnman::Options options;
options.nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS;
@@ -167,18 +167,18 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
}
- peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+ peerLogic->CheckForStaleTipAndEvictPeers();
// No nodes should be marked for disconnection while we have no extra peers
for (const CNode *node : vNodes) {
BOOST_CHECK(node->fDisconnect == false);
}
- SetMockTime(GetTime() + 3*consensusParams.nPowTargetSpacing + 1);
+ SetMockTime(GetTime() + 3 * chainparams.GetConsensus().nPowTargetSpacing + 1);
// Now tip should definitely be stale, and we should look for an extra
// outbound peer
- peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
+ peerLogic->CheckForStaleTipAndEvictPeers();
BOOST_CHECK(connman->GetTryNewOutboundPeer());
// Still no peers should be marked for disconnection
@@ -191,8 +191,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// required time connected check should be satisfied).
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
- peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
- for (int i=0; i<max_outbound_full_relay; ++i) {
+ peerLogic->CheckForStaleTipAndEvictPeers();
+ for (int i = 0; i < max_outbound_full_relay; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
// Last added node should get marked for eviction
@@ -204,8 +204,8 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
// peer, and check that the next newest node gets evicted.
UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());
- peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
- for (int i=0; i<max_outbound_full_relay-1; ++i) {
+ peerLogic->CheckForStaleTipAndEvictPeers();
+ for (int i = 0; i < max_outbound_full_relay - 1; ++i) {
BOOST_CHECK(vNodes[i]->fDisconnect == false);
}
BOOST_CHECK(vNodes[max_outbound_full_relay-1]->fDisconnect == true);
@@ -221,9 +221,10 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
BOOST_AUTO_TEST_CASE(peer_discouragement)
{
+ const CChainParams& chainparams = Params();
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(*connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
banman->ClearBanned();
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
@@ -232,10 +233,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
peerLogic->InitializeNode(&dummyNode1);
dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
- }
+ peerLogic->Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
{
LOCK(dummyNode1.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
@@ -249,20 +247,14 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
peerLogic->InitializeNode(&dummyNode2);
dummyNode2.nVersion = 1;
dummyNode2.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1);
- }
+ peerLogic->Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
{
LOCK(dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
}
BOOST_CHECK(!banman->IsDiscouraged(addr2)); // 2 not discouraged yet...
BOOST_CHECK(banman->IsDiscouraged(addr1)); // ... but 1 still should be
- {
- LOCK(cs_main);
- Misbehaving(dummyNode2.GetId(), 1); // 2 reaches discouragement threshold
- }
+ peerLogic->Misbehaving(dummyNode2.GetId(), 1, /* message */ ""); // 2 reaches discouragement threshold
{
LOCK(dummyNode2.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
@@ -277,9 +269,10 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
+ const CChainParams& chainparams = Params();
auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = MakeUnique<CConnman>(0x1337, 0x1337);
- auto peerLogic = MakeUnique<PeerLogicValidation>(*connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
+ auto peerLogic = MakeUnique<PeerManager>(chainparams, *connman, banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
banman->ClearBanned();
int64_t nStartTime = GetTime();
@@ -292,10 +285,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
dummyNode.nVersion = 1;
dummyNode.fSuccessfullyConnected = true;
- {
- LOCK(cs_main);
- Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);
- }
+ peerLogic->Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
{
LOCK(dummyNode.cs_sendProcessing);
BOOST_CHECK(peerLogic->SendMessages(&dummyNode));
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index 3edcf96495..664e65accc 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -7,6 +7,7 @@
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
+#include <crypto/sha3.h>
#include <crypto/sha512.h>
#include <hash.h>
#include <test/fuzz/FuzzedDataProvider.h>
@@ -32,6 +33,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
CSHA1 sha1;
CSHA256 sha256;
CSHA512 sha512;
+ SHA3_256 sha3;
CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
while (fuzzed_data_provider.ConsumeBool()) {
@@ -51,6 +53,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)ripemd160.Write(data.data(), data.size());
(void)sha1.Write(data.data(), data.size());
(void)sha256.Write(data.data(), data.size());
+ (void)sha3.Write(data);
(void)sha512.Write(data.data(), data.size());
(void)sip_hasher.Write(data.data(), data.size());
@@ -65,11 +68,12 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)ripemd160.Reset();
(void)sha1.Reset();
(void)sha256.Reset();
+ (void)sha3.Reset();
(void)sha512.Reset();
break;
}
case 2: {
- switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 8)) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 9)) {
case 0: {
data.resize(CHash160::OUTPUT_SIZE);
hash160.Finalize(data);
@@ -115,9 +119,21 @@ void test_one_input(const std::vector<uint8_t>& buffer)
data[0] = sip_hasher.Finalize() % 256;
break;
}
+ case 9: {
+ data.resize(SHA3_256::OUTPUT_SIZE);
+ sha3.Finalize(data);
+ break;
+ }
}
break;
}
}
}
+ if (fuzzed_data_provider.ConsumeBool()) {
+ uint64_t state[25];
+ for (size_t i = 0; i < 25; ++i) {
+ state[i] = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
+ }
+ KeccakF(state);
+ }
}
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
new file mode 100644
index 0000000000..cd0c93b8d0
--- /dev/null
+++ b/src/test/fuzz/net.cpp
@@ -0,0 +1,156 @@
+// Copyright (c) 2020 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 <chainparamsbase.h>
+#include <net.h>
+#include <net_permissions.h>
+#include <netaddress.h>
+#include <optional.h>
+#include <protocol.h>
+#include <random.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ static const BasicTestingSetup basic_testing_setup;
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ const std::optional<CAddress> address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!address) {
+ return;
+ }
+ const std::optional<CAddress> address_bind = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!address_bind) {
+ return;
+ }
+
+ CNode node{fuzzed_data_provider.ConsumeIntegral<NodeId>(),
+ static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()),
+ fuzzed_data_provider.ConsumeIntegral<int>(),
+ INVALID_SOCKET,
+ *address,
+ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
+ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
+ *address_bind,
+ fuzzed_data_provider.ConsumeRandomLengthString(32),
+ fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH})};
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 12)) {
+ case 0: {
+ node.CloseSocketDisconnect();
+ break;
+ }
+ case 1: {
+ node.MaybeSetAddrName(fuzzed_data_provider.ConsumeRandomLengthString(32));
+ break;
+ }
+ case 2: {
+ node.SetSendVersion(fuzzed_data_provider.ConsumeIntegral<int>());
+ break;
+ }
+ case 3: {
+ const std::vector<bool> asmap = ConsumeRandomLengthIntegralVector<bool>(fuzzed_data_provider, 128);
+ if (!SanityCheckASMap(asmap)) {
+ break;
+ }
+ CNodeStats stats;
+ node.copyStats(stats, asmap);
+ break;
+ }
+ case 4: {
+ node.SetRecvVersion(fuzzed_data_provider.ConsumeIntegral<int>());
+ break;
+ }
+ case 5: {
+ const CNode* add_ref_node = node.AddRef();
+ assert(add_ref_node == &node);
+ break;
+ }
+ case 6: {
+ if (node.GetRefCount() > 0) {
+ node.Release();
+ }
+ break;
+ }
+ case 7: {
+ if (node.m_addr_known == nullptr) {
+ break;
+ }
+ const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!addr_opt) {
+ break;
+ }
+ node.AddAddressKnown(*addr_opt);
+ break;
+ }
+ case 8: {
+ if (node.m_addr_known == nullptr) {
+ break;
+ }
+ const std::optional<CAddress> addr_opt = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!addr_opt) {
+ break;
+ }
+ FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
+ node.PushAddress(*addr_opt, fast_random_context);
+ break;
+ }
+ case 9: {
+ const std::optional<CInv> inv_opt = ConsumeDeserializable<CInv>(fuzzed_data_provider);
+ if (!inv_opt) {
+ break;
+ }
+ node.AddKnownTx(inv_opt->hash);
+ break;
+ }
+ case 10: {
+ node.PushTxInventory(ConsumeUInt256(fuzzed_data_provider));
+ break;
+ }
+ case 11: {
+ const std::optional<CService> service_opt = ConsumeDeserializable<CService>(fuzzed_data_provider);
+ if (!service_opt) {
+ break;
+ }
+ node.SetAddrLocal(*service_opt);
+ break;
+ }
+ case 12: {
+ const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ bool complete;
+ node.ReceiveMsgBytes((const char*)b.data(), b.size(), complete);
+ break;
+ }
+ }
+ }
+
+ (void)node.GetAddrLocal();
+ (void)node.GetAddrName();
+ (void)node.GetId();
+ (void)node.GetLocalNonce();
+ (void)node.GetLocalServices();
+ (void)node.GetMyStartingHeight();
+ (void)node.GetRecvVersion();
+ const int ref_count = node.GetRefCount();
+ assert(ref_count >= 0);
+ (void)node.GetSendVersion();
+ (void)node.RelayAddrsWithConn();
+
+ const NetPermissionFlags net_permission_flags = fuzzed_data_provider.ConsumeBool() ?
+ fuzzed_data_provider.PickValueInArray<NetPermissionFlags>({NetPermissionFlags::PF_NONE, NetPermissionFlags::PF_BLOOMFILTER, NetPermissionFlags::PF_RELAY, NetPermissionFlags::PF_FORCERELAY, NetPermissionFlags::PF_NOBAN, NetPermissionFlags::PF_MEMPOOL, NetPermissionFlags::PF_ISIMPLICIT, NetPermissionFlags::PF_ALL}) :
+ static_cast<NetPermissionFlags>(fuzzed_data_provider.ConsumeIntegral<uint32_t>());
+ (void)node.HasPermission(net_permission_flags);
+}
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index ec09acc6c6..3d6947ca92 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -68,16 +68,15 @@ void test_one_input(const std::vector<uint8_t>& buffer)
return;
}
CDataStream random_bytes_data_stream{fuzzed_data_provider.ConsumeRemainingBytes<unsigned char>(), SER_NETWORK, PROTOCOL_VERSION};
- CNode& p2p_node = *MakeUnique<CNode>(0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND).release();
+ CNode& p2p_node = *MakeUnique<CNode>(0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND_FULL_RELAY).release();
p2p_node.fSuccessfullyConnected = true;
p2p_node.nVersion = PROTOCOL_VERSION;
p2p_node.SetSendVersion(PROTOCOL_VERSION);
connman.AddTestNode(p2p_node);
- g_setup->m_node.peer_logic->InitializeNode(&p2p_node);
+ g_setup->m_node.peerman->InitializeNode(&p2p_node);
try {
- g_setup->m_node.peer_logic->ProcessMessage(p2p_node, random_message_type, random_bytes_data_stream,
- GetTime<std::chrono::microseconds>(), Params(),
- std::atomic<bool>{false});
+ g_setup->m_node.peerman->ProcessMessage(p2p_node, random_message_type, random_bytes_data_stream,
+ GetTime<std::chrono::microseconds>(), std::atomic<bool>{false});
} catch (const std::ios_base::failure&) {
}
SyncWithValidationInterfaceQueue();
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index ef427442e9..c9433d325a 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -44,7 +44,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
for (int i = 0; i < num_peers_to_add; ++i) {
const ServiceFlags service_flags = ServiceFlags(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
- const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH});
+ const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH});
peers.push_back(MakeUnique<CNode>(i, service_flags, 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, conn_type).release());
CNode& p2p_node = *peers.back();
@@ -52,7 +52,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
p2p_node.fPauseSend = false;
p2p_node.nVersion = PROTOCOL_VERSION;
p2p_node.SetSendVersion(PROTOCOL_VERSION);
- g_setup->m_node.peer_logic->InitializeNode(&p2p_node);
+ g_setup->m_node.peerman->InitializeNode(&p2p_node);
connman.AddTestNode(p2p_node);
}
diff --git a/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp b/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp
new file mode 100644
index 0000000000..d4f302a8d3
--- /dev/null
+++ b/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp
@@ -0,0 +1,38 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <key.h>
+#include <secp256k1.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+int ec_seckey_import_der(const secp256k1_context* ctx, unsigned char* out32, const unsigned char* seckey, size_t seckeylen);
+int ec_seckey_export_der(const secp256k1_context* ctx, unsigned char* seckey, size_t* seckeylen, const unsigned char* key32, bool compressed);
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ secp256k1_context* secp256k1_context_sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+ {
+ std::vector<uint8_t> out32(32);
+ (void)ec_seckey_import_der(secp256k1_context_sign, out32.data(), ConsumeFixedLengthByteVector(fuzzed_data_provider, CKey::SIZE).data(), CKey::SIZE);
+ }
+ {
+ std::vector<uint8_t> seckey(CKey::SIZE);
+ const std::vector<uint8_t> key32 = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
+ size_t seckeylen = CKey::SIZE;
+ const bool compressed = fuzzed_data_provider.ConsumeBool();
+ const bool exported = ec_seckey_export_der(secp256k1_context_sign, seckey.data(), &seckeylen, key32.data(), compressed);
+ if (exported) {
+ std::vector<uint8_t> out32(32);
+ const bool imported = ec_seckey_import_der(secp256k1_context_sign, out32.data(), seckey.data(), seckey.size()) == 1;
+ assert(imported && key32 == out32);
+ }
+ }
+ secp256k1_context_destroy(secp256k1_context_sign);
+}
diff --git a/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp b/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
new file mode 100644
index 0000000000..ed8c7aba89
--- /dev/null
+++ b/src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
@@ -0,0 +1,33 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <key.h>
+#include <secp256k1.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+bool SigHasLowR(const secp256k1_ecdsa_signature* sig);
+int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* input, size_t inputlen);
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ const std::vector<uint8_t> signature_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (signature_bytes.data() == nullptr) {
+ return;
+ }
+ secp256k1_context* secp256k1_context_verify = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
+ secp256k1_ecdsa_signature sig_der_lax;
+ const bool parsed_der_lax = ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig_der_lax, signature_bytes.data(), signature_bytes.size()) == 1;
+ if (parsed_der_lax) {
+ ECC_Start();
+ (void)SigHasLowR(&sig_der_lax);
+ ECC_Stop();
+ }
+ secp256k1_context_destroy(secp256k1_context_verify);
+}
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 917ae571f5..85ebc89673 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -183,10 +183,20 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
std::string pszDest;
- std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, ConnectionType::OUTBOUND);
+ std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY);
+ BOOST_CHECK(pnode1->IsFullOutboundConn() == true);
+ BOOST_CHECK(pnode1->IsManualConn() == false);
+ BOOST_CHECK(pnode1->IsBlockOnlyConn() == false);
+ BOOST_CHECK(pnode1->IsFeelerConn() == false);
+ BOOST_CHECK(pnode1->IsAddrFetchConn() == false);
BOOST_CHECK(pnode1->IsInboundConn() == false);
std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, ConnectionType::INBOUND);
+ BOOST_CHECK(pnode2->IsFullOutboundConn() == false);
+ BOOST_CHECK(pnode2->IsManualConn() == false);
+ BOOST_CHECK(pnode2->IsBlockOnlyConn() == false);
+ BOOST_CHECK(pnode2->IsFeelerConn() == false);
+ BOOST_CHECK(pnode2->IsAddrFetchConn() == false);
BOOST_CHECK(pnode2->IsInboundConn() == true);
}
@@ -283,7 +293,7 @@ BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
in_addr ipv4AddrPeer;
ipv4AddrPeer.s_addr = 0xa0b0c001;
CAddress addr = CAddress(CService(ipv4AddrPeer, 7777), NODE_NETWORK);
- std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND);
+ std::unique_ptr<CNode> pnode = MakeUnique<CNode>(0, NODE_NETWORK, 0, INVALID_SOCKET, addr, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND_FULL_RELAY);
pnode->fSuccessfullyConnected.store(true);
// the peer claims to be reaching us via IPv6
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index d9a00c2205..08aff07448 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -141,8 +141,11 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
+ m_node.mempool = MakeUnique<CTxMemPool>(&::feeEstimator);
+ m_node.mempool->setSanityCheck(1.0);
+
m_node.chainman = &::g_chainman;
- m_node.chainman->InitializeChainstate();
+ m_node.chainman->InitializeChainstate(*m_node.mempool);
::ChainstateActive().InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
assert(!::ChainstateActive().CanFlushToDisk());
@@ -164,14 +167,12 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
g_parallel_script_checks = true;
- m_node.mempool = &::mempool;
- m_node.mempool->setSanityCheck(1.0);
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
- m_node.peer_logic = MakeUnique<PeerLogicValidation>(*m_node.connman, m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
+ m_node.peerman = MakeUnique<PeerManager>(chainparams, *m_node.connman, m_node.banman.get(), *m_node.scheduler, *m_node.chainman, *m_node.mempool);
{
CConnman::Options options;
- options.m_msgproc = m_node.peer_logic.get();
+ options.m_msgproc = m_node.peerman.get();
m_node.connman->Init(options);
}
}
@@ -186,8 +187,8 @@ TestingSetup::~TestingSetup()
m_node.connman.reset();
m_node.banman.reset();
m_node.args = nullptr;
- UnloadBlockIndex(m_node.mempool);
- m_node.mempool = nullptr;
+ UnloadBlockIndex(m_node.mempool.get());
+ m_node.mempool.reset();
m_node.scheduler.reset();
m_node.chainman->Reset();
m_node.chainman = nullptr;
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 2076a1096a..c8a375275f 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -20,6 +20,7 @@ BOOST_FIXTURE_TEST_SUITE(validation_chainstate_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
{
ChainstateManager manager;
+ CTxMemPool mempool;
//! Create and add a Coin with DynamicMemoryUsage of 80 bytes to the given view.
auto add_coin = [](CCoinsViewCache& coins_view) -> COutPoint {
@@ -34,7 +35,7 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
return outp;
};
- CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate());
+ CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool));
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 887a48124f..36badafc4e 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -23,12 +23,13 @@ BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(chainstatemanager)
{
ChainstateManager manager;
+ CTxMemPool mempool;
std::vector<CChainState*> chainstates;
const CChainParams& chainparams = Params();
// Create a legacy (IBD) chainstate.
//
- CChainState& c1 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate());
+ CChainState& c1 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -54,7 +55,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
// Create a snapshot-based chainstate.
//
- CChainState& c2 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(GetRandHash()));
+ CChainState& c2 = *WITH_LOCK(::cs_main, return &manager.InitializeChainstate(mempool, GetRandHash()));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -104,6 +105,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
{
ChainstateManager manager;
+ CTxMemPool mempool;
size_t max_cache = 10000;
manager.m_total_coinsdb_cache = max_cache;
manager.m_total_coinstip_cache = max_cache;
@@ -112,7 +114,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a legacy (IBD) chainstate.
//
- CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate());
+ CChainState& c1 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool));
chainstates.push_back(&c1);
c1.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -129,7 +131,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
// Create a snapshot-based chainstate.
//
- CChainState& c2 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(GetRandHash()));
+ CChainState& c2 = *WITH_LOCK(cs_main, return &manager.InitializeChainstate(mempool, GetRandHash()));
chainstates.push_back(&c2);
c2.InitCoinsDB(
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
@@ -147,7 +149,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
BOOST_CHECK_CLOSE(c1.m_coinsdb_cache_size_bytes, max_cache * 0.05, 1);
BOOST_CHECK_CLOSE(c2.m_coinstip_cache_size_bytes, max_cache * 0.95, 1);
BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
-
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index 8bac914f05..a3b344d2c9 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -18,8 +18,9 @@ BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, BasicTestingSetup)
//!
BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
{
+ CTxMemPool mempool;
BlockManager blockman{};
- CChainState chainstate{blockman};
+ CChainState chainstate{mempool, blockman};
chainstate.InitCoinsDB(/*cache_size_bytes*/ 1 << 10, /*in_memory*/ true, /*should_wipe*/ false);
WITH_LOCK(::cs_main, chainstate.InitCoinsCache(1 << 10));
CTxMemPool tx_pool{};
diff --git a/src/threadsafety.h b/src/threadsafety.h
index 5f2c40bac6..52bf83b676 100644
--- a/src/threadsafety.h
+++ b/src/threadsafety.h
@@ -18,9 +18,7 @@
#define LOCKABLE __attribute__((lockable))
#define SCOPED_LOCKABLE __attribute__((scoped_lockable))
#define GUARDED_BY(x) __attribute__((guarded_by(x)))
-#define GUARDED_VAR __attribute__((guarded_var))
#define PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x)))
-#define PT_GUARDED_VAR __attribute__((pt_guarded_var))
#define ACQUIRED_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
#define ACQUIRED_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((exclusive_lock_function(__VA_ARGS__)))
@@ -33,14 +31,12 @@
#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__)))
#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis))
-#define ASSERT_EXCLUSIVE_LOCK(...) __attribute((assert_exclusive_lock(__VA_ARGS__)))
+#define ASSERT_EXCLUSIVE_LOCK(...) __attribute__((assert_exclusive_lock(__VA_ARGS__)))
#else
#define LOCKABLE
#define SCOPED_LOCKABLE
#define GUARDED_BY(x)
-#define GUARDED_VAR
#define PT_GUARDED_BY(x)
-#define PT_GUARDED_VAR
#define ACQUIRED_AFTER(...)
#define ACQUIRED_BEFORE(...)
#define EXCLUSIVE_LOCK_FUNCTION(...)
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index de1a3ec68f..6a525c97db 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -55,45 +55,45 @@ size_t CTxMemPoolEntry::GetTxSize() const
}
// Update the given tx for any in-mempool descendants.
-// Assumes that setMemPoolChildren is correct for the given tx and all
+// Assumes that CTxMemPool::m_children is correct for the given tx and all
// descendants.
void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, const std::set<uint256> &setExclude)
{
- setEntries stageEntries, setAllDescendants;
- stageEntries = GetMemPoolChildren(updateIt);
+ CTxMemPoolEntry::Children stageEntries, descendants;
+ stageEntries = updateIt->GetMemPoolChildrenConst();
while (!stageEntries.empty()) {
- const txiter cit = *stageEntries.begin();
- setAllDescendants.insert(cit);
- stageEntries.erase(cit);
- const setEntries &setChildren = GetMemPoolChildren(cit);
- for (txiter childEntry : setChildren) {
- cacheMap::iterator cacheIt = cachedDescendants.find(childEntry);
+ const CTxMemPoolEntry& descendant = *stageEntries.begin();
+ descendants.insert(descendant);
+ stageEntries.erase(descendant);
+ const CTxMemPoolEntry::Children& children = descendant.GetMemPoolChildrenConst();
+ for (const CTxMemPoolEntry& childEntry : children) {
+ cacheMap::iterator cacheIt = cachedDescendants.find(mapTx.iterator_to(childEntry));
if (cacheIt != cachedDescendants.end()) {
// We've already calculated this one, just add the entries for this set
// but don't traverse again.
for (txiter cacheEntry : cacheIt->second) {
- setAllDescendants.insert(cacheEntry);
+ descendants.insert(*cacheEntry);
}
- } else if (!setAllDescendants.count(childEntry)) {
+ } else if (!descendants.count(childEntry)) {
// Schedule for later processing
stageEntries.insert(childEntry);
}
}
}
- // setAllDescendants now contains all in-mempool descendants of updateIt.
+ // descendants now contains all in-mempool descendants of updateIt.
// Update and add to cached descendant map
int64_t modifySize = 0;
CAmount modifyFee = 0;
int64_t modifyCount = 0;
- for (txiter cit : setAllDescendants) {
- if (!setExclude.count(cit->GetTx().GetHash())) {
- modifySize += cit->GetTxSize();
- modifyFee += cit->GetModifiedFee();
+ for (const CTxMemPoolEntry& descendant : descendants) {
+ if (!setExclude.count(descendant.GetTx().GetHash())) {
+ modifySize += descendant.GetTxSize();
+ modifyFee += descendant.GetModifiedFee();
modifyCount++;
- cachedDescendants[updateIt].insert(cit);
+ cachedDescendants[updateIt].insert(mapTx.iterator_to(descendant));
// Update ancestor state for each descendant
- mapTx.modify(cit, update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost()));
+ mapTx.modify(mapTx.iterator_to(descendant), update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost()));
}
}
mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
@@ -119,7 +119,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
// Iterate in reverse, so that whenever we are looking at a transaction
// we are sure that all in-mempool descendants have already been processed.
// This maximizes the benefit of the descendant cache and guarantees that
- // setMemPoolChildren will be updated, an assumption made in
+ // CTxMemPool::m_children will be updated, an assumption made in
// UpdateForDescendants.
for (const uint256 &hash : reverse_iterate(vHashesToUpdate)) {
// calculate children from mapNextTx
@@ -128,8 +128,8 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
continue;
}
auto iter = mapNextTx.lower_bound(COutPoint(hash, 0));
- // First calculate the children, and update setMemPoolChildren to
- // include them, and update their setMemPoolParents to include this tx.
+ // First calculate the children, and update CTxMemPool::m_children to
+ // include them, and update their CTxMemPoolEntry::m_parents to include this tx.
// we cache the in-mempool children to avoid duplicate updates
{
const auto epoch = GetFreshEpoch();
@@ -151,7 +151,7 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents /* = true */) const
{
- setEntries parentHashes;
+ CTxMemPoolEntry::Parents staged_ancestors;
const CTransaction &tx = entry.GetTx();
if (fSearchForParents) {
@@ -161,8 +161,8 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
for (unsigned int i = 0; i < tx.vin.size(); i++) {
Optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);
if (piter) {
- parentHashes.insert(*piter);
- if (parentHashes.size() + 1 > limitAncestorCount) {
+ staged_ancestors.insert(**piter);
+ if (staged_ancestors.size() + 1 > limitAncestorCount) {
errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount);
return false;
}
@@ -172,16 +172,17 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
// If we're not searching for parents, we require this to be an
// entry in the mempool already.
txiter it = mapTx.iterator_to(entry);
- parentHashes = GetMemPoolParents(it);
+ staged_ancestors = it->GetMemPoolParentsConst();
}
size_t totalSizeWithAncestors = entry.GetTxSize();
- while (!parentHashes.empty()) {
- txiter stageit = *parentHashes.begin();
+ while (!staged_ancestors.empty()) {
+ const CTxMemPoolEntry& stage = staged_ancestors.begin()->get();
+ txiter stageit = mapTx.iterator_to(stage);
setAncestors.insert(stageit);
- parentHashes.erase(stageit);
+ staged_ancestors.erase(stage);
totalSizeWithAncestors += stageit->GetTxSize();
if (stageit->GetSizeWithDescendants() + entry.GetTxSize() > limitDescendantSize) {
@@ -195,13 +196,15 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
return false;
}
- const setEntries & setMemPoolParents = GetMemPoolParents(stageit);
- for (txiter phash : setMemPoolParents) {
+ const CTxMemPoolEntry::Parents& parents = stageit->GetMemPoolParentsConst();
+ for (const CTxMemPoolEntry& parent : parents) {
+ txiter parent_it = mapTx.iterator_to(parent);
+
// If this is a new ancestor, add it.
- if (setAncestors.count(phash) == 0) {
- parentHashes.insert(phash);
+ if (setAncestors.count(parent_it) == 0) {
+ staged_ancestors.insert(parent);
}
- if (parentHashes.size() + setAncestors.size() + 1 > limitAncestorCount) {
+ if (staged_ancestors.size() + setAncestors.size() + 1 > limitAncestorCount) {
errString = strprintf("too many unconfirmed ancestors [limit: %u]", limitAncestorCount);
return false;
}
@@ -213,10 +216,10 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors)
{
- setEntries parentIters = GetMemPoolParents(it);
+ CTxMemPoolEntry::Parents parents = it->GetMemPoolParents();
// add or remove this tx as a child of each parent
- for (txiter piter : parentIters) {
- UpdateChild(piter, it, add);
+ for (const CTxMemPoolEntry& parent : parents) {
+ UpdateChild(mapTx.iterator_to(parent), it, add);
}
const int64_t updateCount = (add ? 1 : -1);
const int64_t updateSize = updateCount * it->GetTxSize();
@@ -242,9 +245,9 @@ void CTxMemPool::UpdateEntryForAncestors(txiter it, const setEntries &setAncesto
void CTxMemPool::UpdateChildrenForRemoval(txiter it)
{
- const setEntries &setMemPoolChildren = GetMemPoolChildren(it);
- for (txiter updateIt : setMemPoolChildren) {
- UpdateParent(updateIt, it, false);
+ const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
+ for (const CTxMemPoolEntry& updateIt : children) {
+ UpdateParent(mapTx.iterator_to(updateIt), it, false);
}
}
@@ -257,9 +260,9 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b
// updateDescendants should be true whenever we're not recursively
// removing a tx and all its descendants, eg when a transaction is
// confirmed in a block.
- // Here we only update statistics and not data in mapLinks (which
- // we need to preserve until we're finished with all operations that
- // need to traverse the mempool).
+ // Here we only update statistics and not data in CTxMemPool::Parents
+ // and CTxMemPoolEntry::Children (which we need to preserve until we're
+ // finished with all operations that need to traverse the mempool).
for (txiter removeIt : entriesToRemove) {
setEntries setDescendants;
CalculateDescendants(removeIt, setDescendants);
@@ -282,24 +285,26 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b
// should be a bit faster.
// However, if we happen to be in the middle of processing a reorg, then
// the mempool can be in an inconsistent state. In this case, the set
- // of ancestors reachable via mapLinks will be the same as the set of
- // ancestors whose packages include this transaction, because when we
- // add a new transaction to the mempool in addUnchecked(), we assume it
- // has no children, and in the case of a reorg where that assumption is
- // false, the in-mempool children aren't linked to the in-block tx's
- // until UpdateTransactionsFromBlock() is called.
+ // of ancestors reachable via GetMemPoolParents()/GetMemPoolChildren()
+ // will be the same as the set of ancestors whose packages include this
+ // transaction, because when we add a new transaction to the mempool in
+ // addUnchecked(), we assume it has no children, and in the case of a
+ // reorg where that assumption is false, the in-mempool children aren't
+ // linked to the in-block tx's until UpdateTransactionsFromBlock() is
+ // called.
// So if we're being called during a reorg, ie before
- // UpdateTransactionsFromBlock() has been called, then mapLinks[] will
- // differ from the set of mempool parents we'd calculate by searching,
- // and it's important that we use the mapLinks[] notion of ancestor
- // transactions as the set of things to update for removal.
+ // UpdateTransactionsFromBlock() has been called, then
+ // GetMemPoolParents()/GetMemPoolChildren() will differ from the set of
+ // mempool parents we'd calculate by searching, and it's important that
+ // we use the cached notion of ancestor transactions as the set of
+ // things to update for removal.
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false);
// Note that UpdateAncestorsOf severs the child links that point to
// removeIt in the entries for the parents of removeIt.
UpdateAncestorsOf(false, removeIt, setAncestors);
}
// After updating all the ancestor sizes, we can now sever the link between each
- // transaction being removed and any mempool children (ie, update setMemPoolParents
+ // transaction being removed and any mempool children (ie, update CTxMemPoolEntry::m_parents
// for each direct child of a transaction being removed).
for (txiter removeIt : entriesToRemove) {
UpdateChildrenForRemoval(removeIt);
@@ -359,7 +364,6 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
// Used by AcceptToMemoryPool(), which DOES do
// all the appropriate checks.
indexed_transaction_set::iterator newit = mapTx.insert(entry).first;
- mapLinks.insert(make_pair(newit, TxLinks()));
// Update transaction for any feeDelta created by PrioritiseTransaction
// TODO: refactor so that the fee delta is calculated before inserting
@@ -430,15 +434,14 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
totalTxSize -= it->GetTxSize();
cachedInnerUsage -= it->DynamicMemoryUsage();
- cachedInnerUsage -= memusage::DynamicUsage(mapLinks[it].parents) + memusage::DynamicUsage(mapLinks[it].children);
- mapLinks.erase(it);
+ cachedInnerUsage -= memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
mapTx.erase(it);
nTransactionsUpdated++;
if (minerPolicyEstimator) {minerPolicyEstimator->removeTx(hash, false);}
}
// Calculates descendants of entry that are not already in setDescendants, and adds to
-// setDescendants. Assumes entryit is already a tx in the mempool and setMemPoolChildren
+// setDescendants. Assumes entryit is already a tx in the mempool and CTxMemPoolEntry::m_children
// is correct for tx and all descendants.
// Also assumes that if an entry is in setDescendants already, then all
// in-mempool descendants of it are already in setDescendants as well, so that we
@@ -457,8 +460,9 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries& setDescendants
setDescendants.insert(it);
stage.erase(it);
- const setEntries &setChildren = GetMemPoolChildren(it);
- for (txiter childiter : setChildren) {
+ const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
+ for (const CTxMemPoolEntry& child : children) {
+ txiter childiter = mapTx.iterator_to(child);
if (!setDescendants.count(childiter)) {
stage.insert(childiter);
}
@@ -584,7 +588,6 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
void CTxMemPool::_clear()
{
- mapLinks.clear();
mapTx.clear();
mapNextTx.clear();
totalTxSize = 0;
@@ -633,12 +636,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
checkTotal += it->GetTxSize();
innerUsage += it->DynamicMemoryUsage();
const CTransaction& tx = it->GetTx();
- txlinksMap::const_iterator linksiter = mapLinks.find(it);
- assert(linksiter != mapLinks.end());
- const TxLinks &links = linksiter->second;
- innerUsage += memusage::DynamicUsage(links.parents) + memusage::DynamicUsage(links.children);
+ innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
bool fDependsWait = false;
- setEntries setParentCheck;
+ CTxMemPoolEntry::Parents setParentCheck;
for (const CTxIn &txin : tx.vin) {
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
@@ -646,7 +646,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
const CTransaction& tx2 = it2->GetTx();
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
fDependsWait = true;
- setParentCheck.insert(it2);
+ setParentCheck.insert(*it2);
} else {
assert(pcoins->HaveCoin(txin.prevout));
}
@@ -657,7 +657,11 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
assert(it3->second == &tx);
i++;
}
- assert(setParentCheck == GetMemPoolParents(it));
+ auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
+ return a.GetTx().GetHash() == b.GetTx().GetHash();
+ };
+ assert(setParentCheck.size() == it->GetMemPoolParentsConst().size());
+ assert(std::equal(setParentCheck.begin(), setParentCheck.end(), it->GetMemPoolParentsConst().begin(), comp));
// Verify ancestor state is correct.
setEntries setAncestors;
uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
@@ -680,17 +684,18 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
assert(it->GetModFeesWithAncestors() == nFeesCheck);
// Check children against mapNextTx
- CTxMemPool::setEntries setChildrenCheck;
+ CTxMemPoolEntry::Children setChildrenCheck;
auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0));
uint64_t child_sizes = 0;
for (; iter != mapNextTx.end() && iter->first->hash == it->GetTx().GetHash(); ++iter) {
txiter childit = mapTx.find(iter->second->GetHash());
assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions
- if (setChildrenCheck.insert(childit).second) {
+ if (setChildrenCheck.insert(*childit).second) {
child_sizes += childit->GetTxSize();
}
}
- assert(setChildrenCheck == GetMemPoolChildren(it));
+ assert(setChildrenCheck.size() == it->GetMemPoolChildrenConst().size());
+ assert(std::equal(setChildrenCheck.begin(), setChildrenCheck.end(), it->GetMemPoolChildrenConst().begin(), comp));
// Also check to make sure size is greater than sum with immediate children.
// just a sanity check, not definitive that this calc is correct...
assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize());
@@ -852,9 +857,9 @@ void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeD
LogPrintf("PrioritiseTransaction: %s feerate += %s\n", hash.ToString(), FormatMoney(nFeeDelta));
}
-void CTxMemPool::ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const
+void CTxMemPool::ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const
{
- LOCK(cs);
+ AssertLockHeld(cs);
std::map<uint256, CAmount>::const_iterator pos = mapDeltas.find(hash);
if (pos == mapDeltas.end())
return;
@@ -862,9 +867,9 @@ void CTxMemPool::ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const
nFeeDelta += delta;
}
-void CTxMemPool::ClearPrioritisation(const uint256 hash)
+void CTxMemPool::ClearPrioritisation(const uint256& hash)
{
- LOCK(cs);
+ AssertLockHeld(cs);
mapDeltas.erase(hash);
}
@@ -920,7 +925,7 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
size_t CTxMemPool::DynamicMemoryUsage() const {
LOCK(cs);
// Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
- return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
+ return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
}
void CTxMemPool::RemoveUnbroadcastTx(const uint256& txid, const bool unchecked) {
@@ -968,40 +973,26 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, bool validFeeEstimat
void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add)
{
- setEntries s;
- if (add && mapLinks[entry].children.insert(child).second) {
+ AssertLockHeld(cs);
+ CTxMemPoolEntry::Children s;
+ if (add && entry->GetMemPoolChildren().insert(*child).second) {
cachedInnerUsage += memusage::IncrementalDynamicUsage(s);
- } else if (!add && mapLinks[entry].children.erase(child)) {
+ } else if (!add && entry->GetMemPoolChildren().erase(*child)) {
cachedInnerUsage -= memusage::IncrementalDynamicUsage(s);
}
}
void CTxMemPool::UpdateParent(txiter entry, txiter parent, bool add)
{
- setEntries s;
- if (add && mapLinks[entry].parents.insert(parent).second) {
+ AssertLockHeld(cs);
+ CTxMemPoolEntry::Parents s;
+ if (add && entry->GetMemPoolParents().insert(*parent).second) {
cachedInnerUsage += memusage::IncrementalDynamicUsage(s);
- } else if (!add && mapLinks[entry].parents.erase(parent)) {
+ } else if (!add && entry->GetMemPoolParents().erase(*parent)) {
cachedInnerUsage -= memusage::IncrementalDynamicUsage(s);
}
}
-const CTxMemPool::setEntries & CTxMemPool::GetMemPoolParents(txiter entry) const
-{
- assert (entry != mapTx.end());
- txlinksMap::const_iterator it = mapLinks.find(entry);
- assert(it != mapLinks.end());
- return it->second.parents;
-}
-
-const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) const
-{
- assert (entry != mapTx.end());
- txlinksMap::const_iterator it = mapLinks.find(entry);
- assert(it != mapLinks.end());
- return it->second.children;
-}
-
CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
LOCK(cs);
if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0)
@@ -1087,12 +1078,12 @@ uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const {
txiter candidate = candidates.back();
candidates.pop_back();
if (!counted.insert(candidate).second) continue;
- const setEntries& parents = GetMemPoolParents(candidate);
+ const CTxMemPoolEntry::Parents& parents = candidate->GetMemPoolParentsConst();
if (parents.size() == 0) {
maximum = std::max(maximum, candidate->GetCountWithDescendants());
} else {
- for (txiter i : parents) {
- candidates.push_back(i);
+ for (const CTxMemPoolEntry& i : parents) {
+ candidates.push_back(mapTx.iterator_to(i));
}
}
}
diff --git a/src/txmempool.h b/src/txmempool.h
index 4743e1b63a..664fb5986a 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -49,6 +49,20 @@ struct LockPoints
LockPoints() : height(0), time(0), maxInputBlock(nullptr) { }
};
+struct CompareIteratorByHash {
+ // SFINAE for T where T is either a pointer type (e.g., a txiter) or a reference_wrapper<T>
+ // (e.g. a wrapped CTxMemPoolEntry&)
+ template <typename T>
+ bool operator()(const std::reference_wrapper<T>& a, const std::reference_wrapper<T>& b) const
+ {
+ return a.get().GetTx().GetHash() < b.get().GetTx().GetHash();
+ }
+ template <typename T>
+ bool operator()(const T& a, const T& b) const
+ {
+ return a->GetTx().GetHash() < b->GetTx().GetHash();
+ }
+};
/** \class CTxMemPoolEntry
*
* CTxMemPoolEntry stores data about the corresponding transaction, as well
@@ -63,8 +77,16 @@ struct LockPoints
class CTxMemPoolEntry
{
+public:
+ typedef std::reference_wrapper<const CTxMemPoolEntry> CTxMemPoolEntryRef;
+ // two aliases, should the types ever diverge
+ typedef std::set<CTxMemPoolEntryRef, CompareIteratorByHash> Parents;
+ typedef std::set<CTxMemPoolEntryRef, CompareIteratorByHash> Children;
+
private:
const CTransactionRef tx;
+ mutable Parents m_parents;
+ mutable Children m_children;
const CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups
const size_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize())
const size_t nUsageSize; //!< ... and total memory usage
@@ -127,6 +149,11 @@ public:
CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
int64_t GetSigOpCostWithAncestors() const { return nSigOpCostWithAncestors; }
+ const Parents& GetMemPoolParentsConst() const { return m_parents; }
+ const Children& GetMemPoolChildrenConst() const { return m_children; }
+ Parents& GetMemPoolParents() const { return m_parents; }
+ Children& GetMemPoolChildren() const { return m_children; }
+
mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes
mutable uint64_t m_epoch; //!< epoch when last touched, useful for graph algorithms
};
@@ -547,37 +574,22 @@ public:
using txiter = indexed_transaction_set::nth_index<0>::type::const_iterator;
std::vector<std::pair<uint256, txiter>> vTxHashes GUARDED_BY(cs); //!< All tx witness hashes/entries in mapTx, in random order
- struct CompareIteratorByHash {
- bool operator()(const txiter &a, const txiter &b) const {
- return a->GetTx().GetHash() < b->GetTx().GetHash();
- }
- };
typedef std::set<txiter, CompareIteratorByHash> setEntries;
- const setEntries & GetMemPoolParents(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
- const setEntries & GetMemPoolChildren(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
uint64_t CalculateDescendantMaximum(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
private:
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
- struct TxLinks {
- setEntries parents;
- setEntries children;
- };
-
- typedef std::map<txiter, TxLinks, CompareIteratorByHash> txlinksMap;
- txlinksMap mapLinks;
- void UpdateParent(txiter entry, txiter parent, bool add);
- void UpdateChild(txiter entry, txiter child, bool add);
+ void UpdateParent(txiter entry, txiter parent, bool add) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void UpdateChild(txiter entry, txiter child, bool add) EXCLUSIVE_LOCKS_REQUIRED(cs);
std::vector<indexed_transaction_set::const_iterator> GetSortedDepthAndScore() const EXCLUSIVE_LOCKS_REQUIRED(cs);
/**
- * track locally submitted transactions to periodically retry initial broadcast
- * map of txid -> wtxid
+ * Track locally submitted transactions to periodically retry initial broadcast.
*/
- std::map<uint256, uint256> m_unbroadcast_txids GUARDED_BY(cs);
+ std::set<uint256> m_unbroadcast_txids GUARDED_BY(cs);
public:
indirectmap<COutPoint, const CTransaction*> mapNextTx GUARDED_BY(cs);
@@ -626,8 +638,8 @@ public:
/** Affect CreateNewBlock prioritisation of transactions */
void PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta);
- void ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const;
- void ClearPrioritisation(const uint256 hash);
+ void ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void ClearPrioritisation(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs);
/** Get the transaction in the pool that spends the same prevout */
const CTransaction* GetConflictTx(const COutPoint& prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -710,9 +722,9 @@ public:
return mapTx.size();
}
- uint64_t GetTotalTxSize() const
+ uint64_t GetTotalTxSize() const EXCLUSIVE_LOCKS_REQUIRED(cs)
{
- LOCK(cs);
+ AssertLockHeld(cs);
return totalTxSize;
}
@@ -739,27 +751,29 @@ public:
size_t DynamicMemoryUsage() const;
/** Adds a transaction to the unbroadcast set */
- void AddUnbroadcastTx(const uint256& txid, const uint256& wtxid) {
+ void AddUnbroadcastTx(const uint256& txid)
+ {
LOCK(cs);
- // Sanity Check: the transaction should also be in the mempool
- if (exists(txid)) {
- m_unbroadcast_txids[txid] = wtxid;
- }
- }
+ // Sanity check the transaction is in the mempool & insert into
+ // unbroadcast set.
+ if (exists(txid)) m_unbroadcast_txids.insert(txid);
+ };
/** Removes a transaction from the unbroadcast set */
void RemoveUnbroadcastTx(const uint256& txid, const bool unchecked = false);
/** Returns transactions in unbroadcast set */
- std::map<uint256, uint256> GetUnbroadcastTxs() const {
+ std::set<uint256> GetUnbroadcastTxs() const
+ {
LOCK(cs);
return m_unbroadcast_txids;
}
/** Returns whether a txid is in the unbroadcast set */
- bool IsUnbroadcastTx(const uint256& txid) const {
- LOCK(cs);
- return (m_unbroadcast_txids.count(txid) != 0);
+ bool IsUnbroadcastTx(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
+ {
+ AssertLockHeld(cs);
+ return m_unbroadcast_txids.count(txid) != 0;
}
private:
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 00aa53df70..999937d906 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -1019,7 +1019,7 @@ bool FileCommit(FILE *file)
return false;
}
#else
- #if defined(HAVE_FDATASYNC)
+ #if HAVE_FDATASYNC
if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
return false;
diff --git a/src/validation.cpp b/src/validation.cpp
index cf2f9dde62..0823c6b124 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -148,7 +148,6 @@ arith_uint256 nMinimumChainWork;
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CBlockPolicyEstimator feeEstimator;
-CTxMemPool mempool(&feeEstimator);
// Internal stuff
namespace {
@@ -370,9 +369,10 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
* and instead just erase from the mempool as needed.
*/
-static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs)
+static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs)
{
AssertLockHeld(cs_main);
+ AssertLockHeld(mempool.cs);
std::vector<uint256> vHashUpdate;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
@@ -475,6 +475,7 @@ public:
*/
std::vector<COutPoint>& m_coins_to_uncache;
const bool m_test_accept;
+ CAmount* m_fee_out;
};
// Single transaction acceptance
@@ -687,6 +688,11 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return false; // state filled in by CheckTxInputs
}
+ // If fee_out is passed, return the fee to the caller
+ if (args.m_fee_out) {
+ *args.m_fee_out = nFees;
+ }
+
// Check for non-standard pay-to-script-hash in inputs
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
@@ -1061,10 +1067,10 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
/** (try to) add transaction to memory pool with a specified acceptance time **/
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+ bool bypass_limits, const CAmount nAbsurdFee, bool test_accept, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
std::vector<COutPoint> coins_to_uncache;
- MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept };
+ MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept, fee_out };
bool res = MemPoolAccept(pool).AcceptSingleTransaction(tx, args);
if (!res) {
// Remove coins that were not present in the coins cache before calling ATMPW;
@@ -1083,10 +1089,10 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
+ bool bypass_limits, const CAmount nAbsurdFee, bool test_accept, CAmount* fee_out)
{
const CChainParams& chainparams = Params();
- return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept);
+ return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept, fee_out);
}
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
@@ -1254,8 +1260,9 @@ void CoinsViews::InitCache()
m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview);
}
-CChainState::CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash)
+CChainState::CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash)
: m_blockman(blockman),
+ m_mempool(mempool),
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
void CChainState::InitCoinsDB(
@@ -2280,7 +2287,7 @@ bool CChainState::FlushStateToDisk(
{
bool fFlushForPrune = false;
bool fDoFullFlush = false;
- CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&::mempool);
+ CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&m_mempool);
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
if (nManualPruneHeight > 0) {
@@ -2426,7 +2433,7 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
}
/** Check warning conditions and do some notifications on new chain tip set. */
-void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainParams)
+static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
// New best block
@@ -2472,7 +2479,6 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
-
}
/** Disconnect m_chain's tip.
@@ -2485,8 +2491,11 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
* disconnectpool (note that the caller is responsible for mempool consistency
* in any case).
*/
-bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool)
+bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool)
{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_mempool.cs);
+
CBlockIndex *pindexDelete = m_chain.Tip();
assert(pindexDelete);
// Read block from disk.
@@ -2517,14 +2526,14 @@ bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams&
while (disconnectpool->DynamicMemoryUsage() > MAX_DISCONNECTED_TX_POOL_SIZE * 1000) {
// Drop the earliest entry, and remove its children from the mempool.
auto it = disconnectpool->queuedTx.get<insertion_order>().begin();
- mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
+ m_mempool.removeRecursive(**it, MemPoolRemovalReason::REORG);
disconnectpool->removeEntry(it);
}
}
m_chain.SetTip(pindexDelete->pprev);
- UpdateTip(pindexDelete->pprev, chainparams);
+ UpdateTip(m_mempool, pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
GetMainSignals().BlockDisconnected(pblock, pindexDelete);
@@ -2585,6 +2594,9 @@ public:
*/
bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_mempool.cs);
+
assert(pindexNew->pprev == m_chain.Tip());
// Read block from disk.
int64_t nTime1 = GetTimeMicros();
@@ -2625,11 +2637,11 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
// Remove conflicting transactions from the mempool.;
- mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
+ m_mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
disconnectpool.removeForBlock(blockConnecting.vtx);
// Update m_chain & related variables.
m_chain.SetTip(pindexNew);
- UpdateTip(pindexNew, chainparams);
+ UpdateTip(m_mempool, pindexNew, chainparams);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal);
@@ -2719,6 +2731,7 @@ void CChainState::PruneBlockIndexCandidates() {
bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
AssertLockHeld(cs_main);
+ AssertLockHeld(m_mempool.cs);
const CBlockIndex *pindexOldTip = m_chain.Tip();
const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork);
@@ -2730,7 +2743,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (!DisconnectTip(state, chainparams, &disconnectpool)) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
- UpdateMempoolForReorg(disconnectpool, false);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, false);
// If we're unable to disconnect a block during normal operation,
// then that is a failure of our local system -- we should abort
@@ -2774,7 +2787,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
// A system error occurred (disk space, database error, ...).
// Make the mempool consistent with the current tip, just in case
// any observers try to use it before shutdown.
- UpdateMempoolForReorg(disconnectpool, false);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, false);
return false;
}
} else {
@@ -2791,9 +2804,9 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
if (fBlocksDisconnected) {
// If any blocks were disconnected, disconnectpool may be non empty. Add
// any disconnected transactions back to the mempool.
- UpdateMempoolForReorg(disconnectpool, true);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, true);
}
- mempool.check(&CoinsTip());
+ m_mempool.check(&CoinsTip());
// Callbacks/notifications for a new best chain.
if (fInvalidFound)
@@ -2867,7 +2880,8 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
LimitValidationInterfaceQueue();
{
- LOCK2(cs_main, ::mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
+ LOCK(cs_main);
+ LOCK(m_mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
CBlockIndex* starting_tip = m_chain.Tip();
bool blocks_connected = false;
do {
@@ -3020,7 +3034,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
LimitValidationInterfaceQueue();
LOCK(cs_main);
- LOCK(::mempool.cs); // Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is called after DisconnectTip without unlocking in between
+ LOCK(m_mempool.cs); // Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is called after DisconnectTip without unlocking in between
if (!m_chain.Contains(pindex)) break;
pindex_was_in_chain = true;
CBlockIndex *invalid_walk_tip = m_chain.Tip();
@@ -3034,7 +3048,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParam
// transactions back to the mempool if disconnecting was successful,
// and we're not doing a very deep invalidation (in which case
// keeping the mempool up to date is probably futile anyway).
- UpdateMempoolForReorg(disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
+ UpdateMempoolForReorg(m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret);
if (!ret) return false;
assert(invalid_walk_tip->pprev == m_chain.Tip());
@@ -4218,6 +4232,14 @@ bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& ch
return true;
}
+void CChainState::LoadMempool(const ArgsManager& args)
+{
+ if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
+ ::LoadMempool(m_mempool);
+ }
+ m_mempool.SetIsLoaded(!ShutdownRequested());
+}
+
bool CChainState::LoadChainTip(const CChainParams& chainparams)
{
AssertLockHeld(cs_main);
@@ -4517,7 +4539,8 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
// Loop until the tip is below nHeight, or we reach a pruned block.
while (!ShutdownRequested()) {
{
- LOCK2(cs_main, ::mempool.cs);
+ LOCK(cs_main);
+ LOCK(m_mempool.cs);
// Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
assert(tip == m_chain.Tip());
if (tip == nullptr || tip->nHeight < nHeight) break;
@@ -5106,7 +5129,7 @@ bool LoadMempool(CTxMemPool& pool)
}
// TODO: remove this try except in v0.22
- std::map<uint256, uint256> unbroadcast_txids;
+ std::set<uint256> unbroadcast_txids;
try {
file >> unbroadcast_txids;
unbroadcast = unbroadcast_txids.size();
@@ -5114,13 +5137,10 @@ bool LoadMempool(CTxMemPool& pool)
// mempool.dat files created prior to v0.21 will not have an
// unbroadcast set. No need to log a failure if parsing fails here.
}
- for (const auto& elem : unbroadcast_txids) {
- // Don't add unbroadcast transactions that didn't get back into the
- // mempool.
- const CTransactionRef& added_tx = pool.get(elem.first);
- if (added_tx != nullptr) {
- pool.AddUnbroadcastTx(elem.first, added_tx->GetWitnessHash());
- }
+ for (const auto& txid : unbroadcast_txids) {
+ // Ensure transactions were accepted to mempool then add to
+ // unbroadcast set.
+ if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
}
} catch (const std::exception& e) {
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
@@ -5137,7 +5157,7 @@ bool DumpMempool(const CTxMemPool& pool)
std::map<uint256, CAmount> mapDeltas;
std::vector<TxMempoolInfo> vinfo;
- std::map<uint256, uint256> unbroadcast_txids;
+ std::set<uint256> unbroadcast_txids;
static Mutex dump_mutex;
LOCK(dump_mutex);
@@ -5246,7 +5266,7 @@ std::vector<CChainState*> ChainstateManager::GetAll()
return out;
}
-CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blockhash)
+CChainState& ChainstateManager::InitializeChainstate(CTxMemPool& mempool, const uint256& snapshot_blockhash)
{
bool is_snapshot = !snapshot_blockhash.IsNull();
std::unique_ptr<CChainState>& to_modify =
@@ -5255,8 +5275,7 @@ CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blo
if (to_modify) {
throw std::logic_error("should not be overwriting a chainstate");
}
-
- to_modify.reset(new CChainState(m_blockman, snapshot_blockhash));
+ to_modify.reset(new CChainState(mempool, m_blockman, snapshot_blockhash));
// Snapshot chainstates and initial IBD chaintates always become active.
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
diff --git a/src/validation.h b/src/validation.h
index 534162d64a..0bc80e1cee 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -113,7 +113,6 @@ enum class SynchronizationState {
extern RecursiveMutex cs_main;
extern CBlockPolicyEstimator feeEstimator;
-extern CTxMemPool mempool;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern Mutex g_best_block_mutex;
extern std::condition_variable g_best_block_cv;
@@ -200,10 +199,11 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
void PruneBlockFilesManual(int nManualPruneHeight);
/** (try to) add transaction to memory pool
- * plTxnReplaced will be appended to with all transactions replaced from mempool **/
+ * plTxnReplaced will be appended to with all transactions replaced from mempool
+ * @param[out] fee_out optional argument to return tx fee to the caller **/
bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx,
std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false, CAmount* fee_out=nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Get the BIP9 state for a given deployment at the current tip. */
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
@@ -245,7 +245,7 @@ bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
*
* See consensus/consensus.h for flag definitions.
*/
-bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs);
/**
* Closure representing one script verification
@@ -511,11 +511,14 @@ private:
//! easily as opposed to referencing a global.
BlockManager& m_blockman;
+ //! mempool that is kept in sync with the chain
+ CTxMemPool& m_mempool;
+
//! Manages the UTXO set, which is a reflection of the contents of `m_chain`.
std::unique_ptr<CoinsViews> m_coins_views;
public:
- explicit CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash = uint256());
+ explicit CChainState(CTxMemPool& mempool, BlockManager& blockman, uint256 from_snapshot_blockhash = uint256());
/**
* Initialize the CoinsViews UTXO set database management data structures. The in-memory
@@ -642,7 +645,7 @@ public:
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Apply the effects of a block disconnection on the UTXO set.
- bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
+ bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
// Manual block validity manipulation:
bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
@@ -668,6 +671,9 @@ public:
*/
void CheckBlockIndex(const Consensus::Params& consensusParams);
+ /** Load the persisted mempool from disk */
+ void LoadMempool(const ArgsManager& args);
+
/** Update the chain tip based on database information, i.e. CoinsTip()'s best block. */
bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -685,8 +691,8 @@ public:
std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
private:
- bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
- bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
+ bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
+ bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs);
void InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -818,9 +824,11 @@ public:
//! Instantiate a new chainstate and assign it based upon whether it is
//! from a snapshot.
//!
+ //! @param[in] mempool The mempool to pass to the chainstate
+ // constructor
//! @param[in] snapshot_blockhash If given, signify that this chainstate
//! is based on a snapshot.
- CChainState& InitializeChainstate(const uint256& snapshot_blockhash = uint256())
+ CChainState& InitializeChainstate(CTxMemPool& mempool, const uint256& snapshot_blockhash = uint256())
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Get all chainstates currently being used.
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index 24eb2ee34c..fbb3d2cac5 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -52,18 +52,6 @@ bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
return memcmp(value, &rhs.value, sizeof(value)) == 0;
}
-bool IsBDBWalletLoaded(const fs::path& wallet_path)
-{
- fs::path env_directory;
- std::string database_filename;
- SplitWalletPath(wallet_path, env_directory, database_filename);
- LOCK(cs_db);
- auto env = g_dbenvs.find(env_directory.string());
- if (env == g_dbenvs.end()) return false;
- auto database = env->second.lock();
- return database && database->IsDatabaseLoaded(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.
@@ -371,7 +359,6 @@ void BerkeleyDatabase::Open(const char* pszMode)
if (ret != 0) {
throw std::runtime_error(strprintf("BerkeleyDatabase: Error %d, can't open database %s", ret, strFile));
}
- m_file_path = (env->Directory() / strFile).string();
// Call CheckUniqueFileid on the containing BDB environment to
// avoid BDB data consistency bugs that happen when different data
@@ -824,3 +811,35 @@ std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(const char* mode, boo
{
return MakeUnique<BerkeleyBatch>(*this, mode, flush_on_close);
}
+
+bool ExistsBerkeleyDatabase(const fs::path& path)
+{
+ fs::path env_directory;
+ std::string data_filename;
+ SplitWalletPath(path, env_directory, data_filename);
+ return IsBerkeleyBtree(env_directory / data_filename);
+}
+
+std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
+{
+ std::unique_ptr<BerkeleyDatabase> db;
+ {
+ LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
+ std::string data_filename;
+ std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(path, data_filename);
+ if (env->m_databases.count(data_filename)) {
+ error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", (env->Directory() / data_filename).string()));
+ status = DatabaseStatus::FAILED_ALREADY_LOADED;
+ return nullptr;
+ }
+ db = MakeUnique<BerkeleyDatabase>(std::move(env), std::move(data_filename));
+ }
+
+ if (options.verify && !db->Verify(error)) {
+ status = DatabaseStatus::FAILED_VERIFY;
+ return nullptr;
+ }
+
+ status = DatabaseStatus::SUCCESS;
+ return db;
+}
diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h
index 75546924e8..fd5a49acc3 100644
--- a/src/wallet/bdb.h
+++ b/src/wallet/bdb.h
@@ -63,7 +63,6 @@ public:
bool IsMock() const { return fMockDb; }
bool IsInitialized() const { return fDbEnvInit; }
- bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
fs::path Directory() const { return strPath; }
bool Open(bilingual_str& error);
@@ -87,8 +86,8 @@ public:
/** Get BerkeleyEnvironment and database filename given a wallet path. */
std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
-/** Return whether a BDB wallet database is currently loaded. */
-bool IsBDBWalletLoaded(const fs::path& wallet_path);
+/** Check format of database file */
+bool IsBerkeleyBtree(const fs::path& path);
class BerkeleyBatch;
@@ -143,7 +142,10 @@ public:
void ReloadDbEnv() override;
/** Verifies the environment and database file */
- bool Verify(bilingual_str& error) override;
+ bool Verify(bilingual_str& error);
+
+ /** Return path to main database filename */
+ std::string Filename() override { return (env->Directory() / strFile).string(); }
/**
* Pointer to shared database environment.
@@ -224,4 +226,10 @@ public:
std::string BerkeleyDatabaseVersion();
+//! Check if Berkeley database exists at specified path.
+bool ExistsBerkeleyDatabase(const fs::path& path);
+
+//! Return object giving access to Berkeley database at specified path.
+std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
+
#endif // BITCOIN_WALLET_BDB_H
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 1eb82a03c7..bd1d114730 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -23,11 +23,3 @@ void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::
database_filename = "wallet.dat";
}
}
-
-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;
-}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index 0afaba5fd1..617ed46141 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -9,6 +9,7 @@
#include <clientversion.h>
#include <fs.h>
#include <streams.h>
+#include <support/allocators/secure.h>
#include <util/memory.h>
#include <atomic>
@@ -17,8 +18,6 @@
struct bilingual_str;
-/** 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);
void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);
/** RAII class that provides access to a WalletDatabase */
@@ -141,16 +140,14 @@ public:
virtual void ReloadDbEnv() = 0;
+ /** Return path to main database file for logs and error messages. */
+ virtual std::string Filename() = 0;
+
std::atomic<unsigned int> nUpdateCounter;
unsigned int nLastSeen;
unsigned int nLastFlushed;
int64_t nLastWalletUpdate;
- /** Verifies the environment and database file */
- virtual bool Verify(bilingual_str& error) = 0;
-
- std::string m_file_path;
-
/** Make a DatabaseBatch connected to this database */
virtual std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) = 0;
};
@@ -191,8 +188,35 @@ public:
bool PeriodicFlush() override { return true; }
void IncrementUpdateCounter() override { ++nUpdateCounter; }
void ReloadDbEnv() override {}
- bool Verify(bilingual_str& errorStr) override { return true; }
+ std::string Filename() override { return "dummy"; }
std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) override { return MakeUnique<DummyBatch>(); }
};
+enum class DatabaseFormat {
+ BERKELEY,
+};
+
+struct DatabaseOptions {
+ bool require_existing = false;
+ bool require_create = false;
+ uint64_t create_flags = 0;
+ SecureString create_passphrase;
+ bool verify = true;
+};
+
+enum class DatabaseStatus {
+ SUCCESS,
+ FAILED_BAD_PATH,
+ FAILED_BAD_FORMAT,
+ FAILED_ALREADY_LOADED,
+ FAILED_ALREADY_EXISTS,
+ FAILED_NOT_FOUND,
+ FAILED_CREATE,
+ FAILED_LOAD,
+ FAILED_VERIFY,
+ FAILED_ENCRYPT,
+};
+
+std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
+
#endif // BITCOIN_WALLET_DB_H
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index bf05ef844a..5d8c4fba29 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -5,6 +5,7 @@
#include <init.h>
#include <interfaces/chain.h>
+#include <interfaces/wallet.h>
#include <net.h>
#include <node/context.h>
#include <node/ui_interface.h>
@@ -66,13 +67,13 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- argsman.AddArg("-zapwallettxes=<mode>", "Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup"
- " (1 = keep tx meta data e.g. payment request information, 2 = drop tx meta data)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
+
+ argsman.AddHiddenArgs({"-zapwallettxes"});
}
bool WalletInit::ParameterInteraction() const
@@ -85,26 +86,12 @@ bool WalletInit::ParameterInteraction() const
return true;
}
- const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
-
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
}
- bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false);
- // -zapwallettxes implies dropping the mempool on startup
- if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) {
- LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -persistmempool=0\n", __func__);
- }
-
- // -zapwallettxes implies a rescan
- if (zapwallettxes) {
- if (is_multiwallet) {
- return InitError(strprintf(Untranslated("%s is only allowed with a single wallet file"), "-zapwallettxes"));
- }
- if (gArgs.SoftSetBoolArg("-rescan", true)) {
- LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__);
- }
+ if (gArgs.IsArgSet("-zapwallettxes")) {
+ return InitError(Untranslated("-zapwallettxes has been removed. If you are attempting to remove a stuck transaction from your wallet, please use abandontransaction instead."));
}
if (gArgs.GetBoolArg("-sysperms", false))
@@ -120,14 +107,7 @@ void WalletInit::Construct(NodeContext& node) const
LogPrintf("Wallet disabled!\n");
return;
}
- // If there's no -wallet setting with a list of wallets to load, set it to
- // load the default "" wallet.
- if (!args.IsArgSet("wallet")) {
- args.LockSettings([&](util::Settings& settings) {
- util::SettingsValue wallets(util::SettingsValue::VARR);
- wallets.push_back(""); // Default wallet name is ""
- settings.rw_settings["wallet"] = wallets;
- });
- }
- node.chain_clients.emplace_back(interfaces::MakeWalletClient(*node.chain, args, args.GetArgs("-wallet")));
+ auto wallet_client = interfaces::MakeWalletClient(*node.chain, args);
+ node.wallet_client = wallet_client.get();
+ node.chain_clients.emplace_back(std::move(wallet_client));
}
diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp
index ae14769edb..1b057000d2 100644
--- a/src/wallet/load.cpp
+++ b/src/wallet/load.cpp
@@ -5,6 +5,7 @@
#include <wallet/load.h>
+#include <fs.h>
#include <interfaces/chain.h>
#include <scheduler.h>
#include <util/string.h>
@@ -15,7 +16,7 @@
#include <univalue.h>
-bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
+bool VerifyWallets(interfaces::Chain& chain)
{
if (gArgs.IsArgSet("-walletdir")) {
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
@@ -40,22 +41,39 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
chain.initMessage(_("Verifying wallet(s)...").translated);
+ // For backwards compatibility if an unnamed top level wallet exists in the
+ // wallets directory, include it in the default list of wallets to load.
+ if (!gArgs.IsArgSet("wallet")) {
+ DatabaseOptions options;
+ DatabaseStatus status;
+ bilingual_str error_string;
+ options.require_existing = true;
+ options.verify = false;
+ if (MakeWalletDatabase("", options, status, error_string)) {
+ gArgs.LockSettings([&](util::Settings& settings) {
+ util::SettingsValue wallets(util::SettingsValue::VARR);
+ wallets.push_back(""); // Default wallet name is ""
+ settings.rw_settings["wallet"] = wallets;
+ });
+ }
+ }
+
// Keep track of each wallet absolute path to detect duplicates.
std::set<fs::path> wallet_paths;
- for (const auto& wallet_file : wallet_files) {
- WalletLocation location(wallet_file);
+ for (const auto& wallet_file : gArgs.GetArgs("-wallet")) {
+ const fs::path path = fs::absolute(wallet_file, GetWalletDir());
- if (!wallet_paths.insert(location.GetPath()).second) {
+ if (!wallet_paths.insert(path).second) {
chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
return false;
}
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.verify = true;
bilingual_str error_string;
- std::vector<bilingual_str> warnings;
- bool verify_success = CWallet::Verify(chain, location, error_string, warnings);
- if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
- if (!verify_success) {
+ if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
chain.initError(error_string);
return false;
}
@@ -64,13 +82,17 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
return true;
}
-bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
+bool LoadWallets(interfaces::Chain& chain)
{
try {
- for (const std::string& walletFile : wallet_files) {
+ for (const std::string& name : gArgs.GetArgs("-wallet")) {
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
bilingual_str error;
std::vector<bilingual_str> warnings;
- std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile), error, warnings);
+ std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
+ std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(chain, name, std::move(database), options.create_flags, error, warnings) : nullptr;
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
if (!pwallet) {
chain.initError(error);
@@ -118,30 +140,8 @@ void UnloadWallets()
while (!wallets.empty()) {
auto wallet = wallets.back();
wallets.pop_back();
- RemoveWallet(wallet);
+ std::vector<bilingual_str> warnings;
+ RemoveWallet(wallet, nullopt, warnings);
UnloadWallet(std::move(wallet));
}
}
-
-bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
-{
- util::SettingsValue setting_value = chain.getRwSetting("wallet");
- if (!setting_value.isArray()) setting_value.setArray();
- for (const util::SettingsValue& value : setting_value.getValues()) {
- if (value.isStr() && value.get_str() == wallet_name) return true;
- }
- setting_value.push_back(wallet_name);
- return chain.updateRwSetting("wallet", setting_value);
-}
-
-bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
-{
- util::SettingsValue setting_value = chain.getRwSetting("wallet");
- if (!setting_value.isArray()) return true;
- util::SettingsValue new_value(util::SettingsValue::VARR);
- for (const util::SettingsValue& value : setting_value.getValues()) {
- if (!value.isStr() || value.get_str() != wallet_name) new_value.push_back(value);
- }
- if (new_value.size() == setting_value.size()) return true;
- return chain.updateRwSetting("wallet", new_value);
-}
diff --git a/src/wallet/load.h b/src/wallet/load.h
index 30f1a4c90d..e12343de27 100644
--- a/src/wallet/load.h
+++ b/src/wallet/load.h
@@ -17,10 +17,10 @@ class Chain;
} // namespace interfaces
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
-bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
+bool VerifyWallets(interfaces::Chain& chain);
//! Load wallet databases.
-bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
+bool LoadWallets(interfaces::Chain& chain);
//! Complete startup of wallets.
void StartWallets(CScheduler& scheduler, const ArgsManager& args);
@@ -34,10 +34,4 @@ void StopWallets();
//! Close all wallets.
void UnloadWallets();
-//! Add wallet name to persistent configuration so it will be loaded on startup.
-bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
-
-//! Remove wallet name from persistent configuration so it will not be loaded on startup.
-bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
-
#endif // BITCOIN_WALLET_LOAD_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index e0c3a1287a..9e36a09780 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -90,9 +90,9 @@ static void RescanWallet(CWallet& wallet, const WalletRescanReserver& reserver,
}
}
-UniValue importprivkey(const JSONRPCRequest& request)
+RPCHelpMan importprivkey()
{
- RPCHelpMan{"importprivkey",
+ return RPCHelpMan{"importprivkey",
"\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one private key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
@@ -116,8 +116,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -189,11 +189,13 @@ UniValue importprivkey(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue abortrescan(const JSONRPCRequest& request)
+RPCHelpMan abortrescan()
{
- RPCHelpMan{"abortrescan",
+ return RPCHelpMan{"abortrescan",
"\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{},
@@ -206,8 +208,8 @@ UniValue abortrescan(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("abortrescan", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -215,11 +217,13 @@ UniValue abortrescan(const JSONRPCRequest& request)
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan();
return true;
+},
+ };
}
-UniValue importaddress(const JSONRPCRequest& request)
+RPCHelpMan importaddress()
{
- RPCHelpMan{"importaddress",
+ return RPCHelpMan{"importaddress",
"\nAdds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported address exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
@@ -243,8 +247,8 @@ UniValue importaddress(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -315,11 +319,13 @@ UniValue importaddress(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue importprunedfunds(const JSONRPCRequest& request)
+RPCHelpMan importprunedfunds()
{
- RPCHelpMan{"importprunedfunds",
+ return RPCHelpMan{"importprunedfunds",
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n",
{
{"rawtransaction", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A raw transaction in hex funding an already-existing address in wallet"},
@@ -327,8 +333,8 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -371,11 +377,13 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
}
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
+},
+ };
}
-UniValue removeprunedfunds(const JSONRPCRequest& request)
+RPCHelpMan removeprunedfunds()
{
- RPCHelpMan{"removeprunedfunds",
+ return RPCHelpMan{"removeprunedfunds",
"\nDeletes the specified transaction from the wallet. Meant for use with pruned wallets and as a companion to importprunedfunds. This will affect wallet balances.\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex-encoded id of the transaction you are deleting"},
@@ -386,8 +394,8 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -408,11 +416,13 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue importpubkey(const JSONRPCRequest& request)
+RPCHelpMan importpubkey()
{
- RPCHelpMan{"importpubkey",
+ return RPCHelpMan{"importpubkey",
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"Hint: use importmulti to import more than one public key.\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
@@ -432,8 +442,8 @@ UniValue importpubkey(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -492,12 +502,14 @@ UniValue importpubkey(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-UniValue importwallet(const JSONRPCRequest& request)
+RPCHelpMan importwallet()
{
- RPCHelpMan{"importwallet",
+ return RPCHelpMan{"importwallet",
"\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
@@ -512,8 +524,8 @@ UniValue importwallet(const JSONRPCRequest& request)
"\nImport using the json rpc call\n"
+ HelpExampleRpc("importwallet", "\"test\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -649,11 +661,13 @@ UniValue importwallet(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys/scripts to wallet");
return NullUniValue;
+},
+ };
}
-UniValue dumpprivkey(const JSONRPCRequest& request)
+RPCHelpMan dumpprivkey()
{
- RPCHelpMan{"dumpprivkey",
+ return RPCHelpMan{"dumpprivkey",
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n",
{
@@ -667,8 +681,8 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
+ HelpExampleCli("importprivkey", "\"mykey\"")
+ HelpExampleRpc("dumpprivkey", "\"myaddress\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -693,12 +707,14 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known");
}
return EncodeSecret(vchSecret);
+},
+ };
}
-UniValue dumpwallet(const JSONRPCRequest& request)
+RPCHelpMan dumpwallet()
{
- RPCHelpMan{"dumpwallet",
+ return RPCHelpMan{"dumpwallet",
"\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n"
"Imported scripts are included in the dumpfile, but corresponding BIP173 addresses, etc. may not be added automatically by importwallet.\n"
"Note that if your wallet contains keys which are not derived from your HD seed (e.g. imported keys), these are not covered by\n"
@@ -716,8 +732,8 @@ UniValue dumpwallet(const JSONRPCRequest& request)
HelpExampleCli("dumpwallet", "\"test\"")
+ HelpExampleRpc("dumpwallet", "\"test\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
@@ -829,6 +845,8 @@ UniValue dumpwallet(const JSONRPCRequest& request)
reply.pushKV("filename", filepath.string());
return reply;
+},
+ };
}
struct ImportData
@@ -1239,9 +1257,9 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now)
throw JSONRPCError(RPC_TYPE_ERROR, "Missing required timestamp field for key");
}
-UniValue importmulti(const JSONRPCRequest& mainRequest)
+RPCHelpMan importmulti()
{
- RPCHelpMan{"importmulti",
+ return RPCHelpMan{"importmulti",
"\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), optionally rescanning the blockchain from the earliest creation time of the imported scripts. Requires a new wallet backup.\n"
"If an address/script is imported without all of the private keys required to spend from that address, it will be watchonly. The 'watchonly' option must be set to true in this case or a warning will be returned.\n"
"Conversely, if all the private keys are provided and the address/script is spendable, the watchonly option must be set to false, or a warning will be returned.\n"
@@ -1314,8 +1332,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'")
},
- }.Check(mainRequest);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& mainRequest) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1423,6 +1441,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
}
return response;
+},
+ };
}
static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
@@ -1564,9 +1584,9 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
return result;
}
-UniValue importdescriptors(const JSONRPCRequest& main_request)
+RPCHelpMan importdescriptors()
{
- RPCHelpMan{"importdescriptors",
+ return RPCHelpMan{"importdescriptors",
"\nImport descriptors. This will trigger a rescan of the blockchain based on the earliest timestamp of all descriptors being imported. Requires a new wallet backup.\n"
"\nNote: This call can take over an hour to complete if using an early timestamp; during that time, other rpc calls\n"
"may report that the imported keys, addresses or scripts exist but related transactions are still missing.\n",
@@ -1615,8 +1635,8 @@ UniValue importdescriptors(const JSONRPCRequest& main_request)
"{ \"desc\": \"<my desccriptor 2>\", \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importdescriptors", "'[{ \"desc\": \"<my descriptor>\", \"timestamp\":1455191478, \"active\": true, \"range\": [0,100], \"label\": \"<my bech32 wallet>\" }]'")
},
- }.Check(main_request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1713,4 +1733,6 @@ UniValue importdescriptors(const JSONRPCRequest& main_request)
}
return response;
+},
+ };
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 7a03c4f544..62a3206802 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -11,6 +11,7 @@
#include <outputtype.h>
#include <policy/feerate.h>
#include <policy/fees.h>
+#include <policy/policy.h>
#include <policy/rbf.h>
#include <rpc/rawtransaction_util.h>
#include <rpc/server.h>
@@ -230,18 +231,6 @@ static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const U
}
}
-static void UpdateWalletSetting(interfaces::Chain& chain,
- const std::string& wallet_name,
- const UniValue& load_on_startup,
- std::vector<bilingual_str>& warnings)
-{
- if (load_on_startup.isTrue() && !AddWalletSetting(chain, wallet_name)) {
- warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup."));
- } else if (load_on_startup.isFalse() && !RemoveWalletSetting(chain, wallet_name)) {
- warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may still be loaded next node startup."));
- }
-}
-
static UniValue getnewaddress(const JSONRPCRequest& request)
{
RPCHelpMan{"getnewaddress",
@@ -2061,6 +2050,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
"A locked transaction output will not be chosen by automatic coin selection, when spending bitcoins.\n"
+ "Manually selected coins are automatically unlocked.\n"
"Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n"
"is always cleared (by virtue of process exit) when a node stops or fails.\n"
"Also see the listunspent call\n",
@@ -2494,7 +2484,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
RPCHelpMan{"loadwallet",
"\nLoads a wallet from a wallet file or directory."
"\nNote that all wallet command-line options used when starting bitcoind will be"
- "\napplied to the new wallet (eg -zapwallettxes, rescan, etc).\n",
+ "\napplied to the new wallet (eg -rescan, etc).\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
{"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
@@ -2513,24 +2503,21 @@ static UniValue loadwallet(const JSONRPCRequest& request)
}.Check(request);
WalletContext& context = EnsureWalletContext(request.context);
- WalletLocation location(request.params[0].get_str());
-
- if (!location.Exists()) {
- throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + location.GetName() + " not found.");
- } else if (fs::is_directory(location.GetPath())) {
- // The given filename is a directory. Check that there's a wallet.dat file.
- fs::path wallet_dat_file = location.GetPath() / "wallet.dat";
- if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) {
- throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + location.GetName() + " does not contain a wallet.dat file.");
- }
- }
+ const std::string name(request.params[0].get_str());
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.require_existing = true;
bilingual_str error;
std::vector<bilingual_str> warnings;
- std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, location, error, warnings);
- if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error.original);
-
- UpdateWalletSetting(*context.chain, location.GetName(), request.params[1], warnings);
+ Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool());
+ std::shared_ptr<CWallet> const wallet = LoadWallet(*context.chain, name, load_on_start, options, status, error, warnings);
+ if (!wallet) {
+ // Map bad format to not found, since bad format is returned when the
+ // wallet directory exists, but doesn't contain a data file.
+ RPCErrorCode code = status == DatabaseStatus::FAILED_NOT_FOUND || status == DatabaseStatus::FAILED_BAD_FORMAT ? RPC_WALLET_NOT_FOUND : RPC_WALLET_ERROR;
+ throw JSONRPCError(code, error.original);
+ }
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
@@ -2659,21 +2646,19 @@ static UniValue createwallet(const JSONRPCRequest& request)
warnings.emplace_back(Untranslated("Wallet is an experimental descriptor wallet"));
}
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.require_create = true;
+ options.create_flags = flags;
+ options.create_passphrase = passphrase;
bilingual_str error;
- std::shared_ptr<CWallet> wallet;
- WalletCreationStatus status = CreateWallet(*context.chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet);
- switch (status) {
- case WalletCreationStatus::CREATION_FAILED:
- throw JSONRPCError(RPC_WALLET_ERROR, error.original);
- case WalletCreationStatus::ENCRYPTION_FAILED:
- throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, error.original);
- case WalletCreationStatus::SUCCESS:
- break;
- // no default case, so the compiler can warn about missing cases
+ Optional<bool> load_on_start = request.params[6].isNull() ? nullopt : Optional<bool>(request.params[6].get_bool());
+ std::shared_ptr<CWallet> wallet = CreateWallet(*context.chain, request.params[0].get_str(), load_on_start, options, status, error, warnings);
+ if (!wallet) {
+ RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
+ throw JSONRPCError(code, error.original);
}
- UpdateWalletSetting(*context.chain, request.params[0].get_str(), request.params[6], warnings);
-
UniValue obj(UniValue::VOBJ);
obj.pushKV("name", wallet->GetName());
obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
@@ -2716,15 +2701,13 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
// Release the "main" shared pointer and prevent further notifications.
// Note that any attempt to load the same wallet would fail until the wallet
// is destroyed (see CheckUniqueFileid).
- if (!RemoveWallet(wallet)) {
+ std::vector<bilingual_str> warnings;
+ Optional<bool> load_on_start = request.params[1].isNull() ? nullopt : Optional<bool>(request.params[1].get_bool());
+ if (!RemoveWallet(wallet, load_on_start, warnings)) {
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
}
- interfaces::Chain& chain = wallet->chain();
- std::vector<bilingual_str> warnings;
-
UnloadWallet(std::move(wallet));
- UpdateWalletSetting(chain, wallet_name, request.params[1], warnings);
UniValue result(UniValue::VOBJ);
result.pushKV("warning", Join(warnings, Untranslated("\n")).original);
@@ -2836,6 +2819,15 @@ static UniValue listunspent(const JSONRPCRequest& request)
if (!request.params[4].isNull()) {
const UniValue& options = request.params[4].get_obj();
+ RPCTypeCheckObj(options,
+ {
+ {"minimumAmount", UniValueType()},
+ {"maximumAmount", UniValueType()},
+ {"minimumSumAmount", UniValueType()},
+ {"maximumCount", UniValueType(UniValue::VNUM)},
+ },
+ true, true);
+
if (options.exists("minimumAmount"))
nMinimumAmount = AmountFromValue(options["minimumAmount"]);
@@ -2964,13 +2956,22 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
RPCTypeCheckObj(options,
{
{"add_inputs", UniValueType(UniValue::VBOOL)},
+ {"add_to_wallet", UniValueType(UniValue::VBOOL)},
{"changeAddress", UniValueType(UniValue::VSTR)},
+ {"change_address", UniValueType(UniValue::VSTR)},
{"changePosition", UniValueType(UniValue::VNUM)},
+ {"change_position", UniValueType(UniValue::VNUM)},
{"change_type", UniValueType(UniValue::VSTR)},
{"includeWatching", UniValueType(UniValue::VBOOL)},
+ {"include_watching", UniValueType(UniValue::VBOOL)},
+ {"inputs", UniValueType(UniValue::VARR)},
{"lockUnspents", UniValueType(UniValue::VBOOL)},
- {"feeRate", UniValueType()}, // will be checked below
+ {"lock_unspents", UniValueType(UniValue::VBOOL)},
+ {"locktime", UniValueType(UniValue::VNUM)},
+ {"feeRate", UniValueType()}, // will be checked below,
+ {"psbt", UniValueType(UniValue::VBOOL)},
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
+ {"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
{"replaceable", UniValueType(UniValue::VBOOL)},
{"conf_target", UniValueType(UniValue::VNUM)},
{"estimate_mode", UniValueType(UniValue::VSTR)},
@@ -2981,22 +2982,24 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
coinControl.m_add_inputs = options["add_inputs"].get_bool();
}
- if (options.exists("changeAddress")) {
- CTxDestination dest = DecodeDestination(options["changeAddress"].get_str());
+ if (options.exists("changeAddress") || options.exists("change_address")) {
+ const std::string change_address_str = (options.exists("change_address") ? options["change_address"] : options["changeAddress"]).get_str();
+ CTxDestination dest = DecodeDestination(change_address_str);
if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Change address must be a valid bitcoin address");
}
coinControl.destChange = dest;
}
- if (options.exists("changePosition"))
- change_position = options["changePosition"].get_int();
+ if (options.exists("changePosition") || options.exists("change_position")) {
+ change_position = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).get_int();
+ }
if (options.exists("change_type")) {
- if (options.exists("changeAddress")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both changeAddress and address_type options");
+ if (options.exists("changeAddress") || options.exists("change_address")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both change address and address type options");
}
OutputType out_type;
if (!ParseOutputType(options["change_type"].get_str(), out_type)) {
@@ -3005,10 +3008,12 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
coinControl.m_change_type.emplace(out_type);
}
- coinControl.fAllowWatchOnly = ParseIncludeWatchonly(options["includeWatching"], *pwallet);
+ const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"];
+ coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, *pwallet);
- if (options.exists("lockUnspents"))
- lockUnspents = options["lockUnspents"].get_bool();
+ if (options.exists("lockUnspents") || options.exists("lock_unspents")) {
+ lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
+ }
if (options.exists("feeRate"))
{
@@ -3022,8 +3027,8 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
coinControl.fOverrideFeeRate = true;
}
- if (options.exists("subtractFeeFromOutputs"))
- subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array();
+ if (options.exists("subtractFeeFromOutputs") || options.exists("subtract_fee_from_outputs") )
+ subtractFeeFromOutputs = (options.exists("subtract_fee_from_outputs") ? options["subtract_fee_from_outputs"] : options["subtractFeeFromOutputs"]).get_array();
if (options.exists("replaceable")) {
coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
@@ -3866,6 +3871,185 @@ static UniValue listlabels(const JSONRPCRequest& request)
return ret;
}
+static RPCHelpMan send()
+{
+ return RPCHelpMan{"send",
+ "\nSend a transaction.\n",
+ {
+ {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ "That is, each address can only appear once and there can only be one 'data' object.\n"
+ "For convenience, a dictionary, which holds the key-value pairs directly, is also accepted.",
+ {
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {
+ {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + ""},
+ },
+ },
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
+ {
+ {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"},
+ },
+ },
+ },
+ },
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
+ {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
+ {
+ {"add_inputs", RPCArg::Type::BOOL, /* default */ "false", "If inputs are specified, automatically include more if they are not enough."},
+ {"add_to_wallet", RPCArg::Type::BOOL, /* default */ "true", "When false, returns a serialized transaction which will not be added to the wallet or broadcast"},
+ {"change_address", RPCArg::Type::STR_HEX, /* default */ "pool address", "The bitcoin address to receive the change"},
+ {"change_position", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
+ {"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
+ {"include_watching", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also select inputs which are watch only.\n"
+ "Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
+ "e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
+ {"inputs", RPCArg::Type::ARR, /* default */ "empty array", "Specify inputs instead of adding them automatically. A json array of json objects",
+ {
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
+ {"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"},
+ },
+ },
+ {"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
+ {"lock_unspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
+ {"psbt", RPCArg::Type::BOOL, /* default */ "automatic", "Always return a PSBT, implies add_to_wallet=false."},
+ {"subtract_fee_from_outputs", RPCArg::Type::ARR, /* default */ "empty array", "A json array of integers.\n"
+ "The fee will be equally deducted from the amount of each specified output.\n"
+ "Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
+ "If no outputs are specified here, the sender pays the fee.",
+ {
+ {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
+ },
+ },
+ {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
+ " Allows this transaction to be replaced by a transaction with higher fees"},
+ },
+ "options"},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of the number of addresses."},
+ {RPCResult::Type::STR_HEX, "hex", "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
+ {RPCResult::Type::STR, "psbt", "If more signatures are needed, or if add_to_wallet is false, the base64-encoded (partially) signed transaction"}
+ }
+ },
+ RPCExamples{""
+ "\nSend with a fee rate of 1 satoshi per byte\n"
+ + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 sat/b\n" +
+ "\nCreate a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network\n")
+ + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 economical '{\"add_to_wallet\": false, \"inputs\": [{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\", \"vout\":1}]}'")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ RPCTypeCheck(request.params, {
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VSTR,
+ UniValue::VOBJ
+ }, true
+ );
+
+ std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
+ if (!wallet) return NullUniValue;
+ CWallet* const pwallet = wallet.get();
+
+ UniValue options = request.params[3];
+ if (options.exists("feeRate") || options.exists("fee_rate") || options.exists("estimate_mode") || options.exists("conf_target")) {
+ if (!request.params[1].isNull() || !request.params[2].isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use either conf_target and estimate_mode or the options dictionary to control fee rate");
+ }
+ } else {
+ options.pushKV("conf_target", request.params[1]);
+ options.pushKV("estimate_mode", request.params[2]);
+ }
+ if (!options["conf_target"].isNull() && (options["estimate_mode"].isNull() || (options["estimate_mode"].get_str() == "unset"))) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Specify estimate_mode");
+ }
+ if (options.exists("changeAddress")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_address");
+ }
+ if (options.exists("changePosition")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_position");
+ }
+ if (options.exists("includeWatching")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use include_watching");
+ }
+ if (options.exists("lockUnspents")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use lock_unspents");
+ }
+ if (options.exists("subtractFeeFromOutputs")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Use subtract_fee_from_outputs");
+ }
+
+ const bool psbt_opt_in = options.exists("psbt") && options["psbt"].get_bool();
+
+ CAmount fee;
+ int change_position;
+ bool rbf = pwallet->m_signal_rbf;
+ if (options.exists("replaceable")) {
+ rbf = options["add_to_wallet"].get_bool();
+ }
+ CMutableTransaction rawTx = ConstructTransaction(options["inputs"], request.params[0], options["locktime"], rbf);
+ CCoinControl coin_control;
+ // Automatically select coins, unless at least one is manually selected. Can
+ // be overriden by options.add_inputs.
+ coin_control.m_add_inputs = rawTx.vin.size() == 0;
+ FundTransaction(pwallet, rawTx, fee, change_position, options, coin_control);
+
+ bool add_to_wallet = true;
+ if (options.exists("add_to_wallet")) {
+ add_to_wallet = options["add_to_wallet"].get_bool();
+ }
+
+ // Make a blank psbt
+ PartiallySignedTransaction psbtx(rawTx);
+
+ // Fill transaction with out data and sign
+ bool complete = true;
+ const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false);
+ if (err != TransactionError::OK) {
+ throw JSONRPCTransactionError(err);
+ }
+
+ CMutableTransaction mtx;
+ complete = FinalizeAndExtractPSBT(psbtx, mtx);
+
+ UniValue result(UniValue::VOBJ);
+
+ // Serialize the PSBT
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << psbtx;
+ const std::string result_str = EncodeBase64(ssTx.str());
+
+ if (psbt_opt_in || !complete || !add_to_wallet) {
+ result.pushKV("psbt", result_str);
+ }
+
+ if (complete) {
+ std::string err_string;
+ std::string hex = EncodeHexTx(CTransaction(mtx));
+ CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
+ result.pushKV("txid", tx->GetHash().GetHex());
+ if (add_to_wallet && !psbt_opt_in) {
+ pwallet->CommitTransaction(tx, {}, {} /* orderForm */);
+ } else {
+ result.pushKV("hex", hex);
+ }
+ }
+ result.pushKV("complete", complete);
+
+ return result;
+ }
+ };
+}
+
UniValue sethdseed(const JSONRPCRequest& request)
{
RPCHelpMan{"sethdseed",
@@ -4006,7 +4190,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
"\nCreates and funds a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator and Updater roles.\n",
{
- {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs. Leave empty to add inputs automatically. See add_inputs option.",
+ {"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Leave empty to add inputs automatically. See add_inputs option.",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
@@ -4161,17 +4345,17 @@ static UniValue upgradewallet(const JSONRPCRequest& request)
return error.original;
}
-UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
-UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
-UniValue importprivkey(const JSONRPCRequest& request);
-UniValue importaddress(const JSONRPCRequest& request);
-UniValue importpubkey(const JSONRPCRequest& request);
-UniValue dumpwallet(const JSONRPCRequest& request);
-UniValue importwallet(const JSONRPCRequest& request);
-UniValue importprunedfunds(const JSONRPCRequest& request);
-UniValue removeprunedfunds(const JSONRPCRequest& request);
-UniValue importmulti(const JSONRPCRequest& request);
-UniValue importdescriptors(const JSONRPCRequest& request);
+RPCHelpMan abortrescan();
+RPCHelpMan dumpprivkey();
+RPCHelpMan importprivkey();
+RPCHelpMan importaddress();
+RPCHelpMan importpubkey();
+RPCHelpMan dumpwallet();
+RPCHelpMan importwallet();
+RPCHelpMan importprunedfunds();
+RPCHelpMan removeprunedfunds();
+RPCHelpMan importmulti();
+RPCHelpMan importdescriptors();
Span<const CRPCCommand> GetWalletRPCCommands()
{
@@ -4223,6 +4407,7 @@ static const CRPCCommand commands[] =
{ "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} },
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
+ { "wallet", "send", &send, {"outputs","conf_target","estimate_mode","options"} },
{ "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
{ "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode","avoid_reuse"} },
{ "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} },
diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp
index c0755db751..225b975067 100644
--- a/src/wallet/salvage.cpp
+++ b/src/wallet/salvage.cpp
@@ -16,8 +16,20 @@ static const char *HEADER_END = "HEADER=END";
static const char *DATA_END = "DATA=END";
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
+static bool KeyFilter(const std::string& type)
+{
+ return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN;
+}
+
bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
+ DatabaseOptions options;
+ DatabaseStatus status;
+ options.require_existing = true;
+ options.verify = false;
+ std::unique_ptr<WalletDatabase> database = MakeDatabase(file_path, options, status, error);
+ if (!database) return false;
+
std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
@@ -118,7 +130,7 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v
}
DbTxn* ptxn = env->TxnBegin();
- CWallet dummyWallet(nullptr, WalletLocation(), CreateDummyWalletDatabase());
+ CWallet dummyWallet(nullptr, "", CreateDummyWalletDatabase());
for (KeyValPair& row : salvagedData)
{
/* Filter for only private key type KV pairs to be added to the salvaged wallet */
@@ -129,9 +141,9 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v
{
// Required in LoadKeyMetadata():
LOCK(dummyWallet.cs_wallet);
- fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, strType, strErr);
+ fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, strType, strErr, KeyFilter);
}
- if (!WalletBatch::IsKeyType(strType) && strType != DBKeys::HDCHAIN) {
+ if (!KeyFilter(strType)) {
continue;
}
if (!fReadOK)
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index a96d971734..14fb1fa89f 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -535,7 +535,7 @@ private:
//! keeps track of whether Unlock has run a thorough check before
bool m_decryption_thoroughly_checked = false;
- bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey);
+ bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 1deedede4c..f38ccba384 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -29,7 +29,7 @@ typedef std::set<CInputCoin> CoinSet;
static std::vector<COutput> vCoins;
static NodeContext testNode;
static auto testChain = interfaces::MakeChain(testNode);
-static CWallet testWallet(testChain.get(), WalletLocation(), CreateDummyWalletDatabase());
+static CWallet testWallet(testChain.get(), "", CreateDummyWalletDatabase());
static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0);
@@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// Make sure that can use BnB when there are preset inputs
empty_wallet();
{
- std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase());
+ std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);
wallet->SetupLegacyScriptPubKeyMan();
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
index 35bd965673..c80310045a 100644
--- a/src/wallet/test/init_test_fixture.cpp
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -10,7 +10,7 @@
InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{
- m_chain_client = MakeWalletClient(*m_chain, *Assert(m_node.args), {});
+ m_wallet_client = MakeWalletClient(*m_chain, *Assert(m_node.args));
std::string sep;
sep += fs::path::preferred_separator;
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
index c95b4f1f6e..f5bade77df 100644
--- a/src/wallet/test/init_test_fixture.h
+++ b/src/wallet/test/init_test_fixture.h
@@ -6,6 +6,7 @@
#define BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
#include <interfaces/chain.h>
+#include <interfaces/wallet.h>
#include <node/context.h>
#include <test/util/setup_common.h>
@@ -19,7 +20,7 @@ struct InitWalletDirTestingSetup: public BasicTestingSetup {
fs::path m_cwd;
std::map<std::string, fs::path> m_walletdir_path_cases;
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
- std::unique_ptr<interfaces::ChainClient> m_chain_client;
+ std::unique_ptr<interfaces::WalletClient> m_wallet_client;
};
#endif // BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp
index c228e06009..9b905569fc 100644
--- a/src/wallet/test/init_tests.cpp
+++ b/src/wallet/test/init_tests.cpp
@@ -15,7 +15,7 @@ BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
{
SetWalletDir(m_walletdir_path_cases["default"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
@@ -25,7 +25,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
{
SetWalletDir(m_walletdir_path_cases["custom"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]);
@@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
SetWalletDir(m_walletdir_path_cases["nonexistent"]);
{
ASSERT_DEBUG_LOG("does not exist");
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == false);
}
}
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory)
SetWalletDir(m_walletdir_path_cases["file"]);
{
ASSERT_DEBUG_LOG("is not a directory");
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == false);
}
}
@@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
SetWalletDir(m_walletdir_path_cases["relative"]);
{
ASSERT_DEBUG_LOG("is a relative path");
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == false);
}
}
@@ -65,7 +65,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
{
SetWalletDir(m_walletdir_path_cases["trailing"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
@@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
{
SetWalletDir(m_walletdir_path_cases["trailing2"]);
- bool result = m_chain_client->verify();
+ bool result = m_wallet_client->verify();
BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetArg("-walletdir", "");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp
index e416f16044..d5aed99d99 100644
--- a/src/wallet/test/ismine_tests.cpp
+++ b/src/wallet/test/ismine_tests.cpp
@@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK compressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(pubkeys[0]);
@@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PK uncompressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey);
@@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH compressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0]));
@@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2PKH uncompressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey));
@@ -103,7 +103,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2SH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2SH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// (P2PKH inside) P2WSH inside P2WSH (invalid)
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH compressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WPKH uncompressed
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -231,7 +231,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// scriptPubKey multisig
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -262,7 +262,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2SH multisig
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with compressed keys
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -309,7 +309,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig with uncompressed key
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
@@ -335,7 +335,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// P2WSH multisig wrapped in P2SH
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
@@ -362,7 +362,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// OP_RETURN
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -376,7 +376,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unspendable
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -390,7 +390,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// witness unknown
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
@@ -404,7 +404,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
// Nonstandard
{
- CWallet keystore(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet keystore(chain.get(), "", CreateDummyWalletDatabase());
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp
index 4f12079768..f7c1337b0d 100644
--- a/src/wallet/test/scriptpubkeyman_tests.cpp
+++ b/src/wallet/test/scriptpubkeyman_tests.cpp
@@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(CanProvide)
// Set up wallet and keyman variables.
NodeContext node;
std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan();
// Make a 1 of 2 multisig script
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index 44f9eb5ddd..4d6f427618 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -6,10 +6,10 @@
WalletTestingSetup::WalletTestingSetup(const std::string& chainName)
: TestingSetup(chainName),
- m_wallet(m_chain.get(), WalletLocation(), CreateMockWalletDatabase())
+ m_wallet(m_chain.get(), "", CreateMockWalletDatabase())
{
bool fFirstRun;
m_wallet.LoadWallet(fFirstRun);
m_chain_notifications_handler = m_chain->handleNotifications({ &m_wallet, [](CWallet*) {} });
- m_chain_client->registerRpcs();
+ m_wallet_client->registerRpcs();
}
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index 99d7cfe921..ba8a5ff1f3 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -21,7 +21,7 @@ struct WalletTestingSetup : public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain(m_node);
- std::unique_ptr<interfaces::ChainClient> m_chain_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args), {});
+ std::unique_ptr<interfaces::WalletClient> m_wallet_client = interfaces::MakeWalletClient(*m_chain, *Assert(m_node.args));
CWallet m_wallet;
std::unique_ptr<interfaces::Handler> m_chain_notifications_handler;
};
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 7ef06663b5..6b98482f98 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -24,9 +24,9 @@
#include <boost/test/unit_test.hpp>
#include <univalue.h>
-extern UniValue importmulti(const JSONRPCRequest& request);
-extern UniValue dumpwallet(const JSONRPCRequest& request);
-extern UniValue importwallet(const JSONRPCRequest& request);
+RPCHelpMan importmulti();
+RPCHelpMan dumpwallet();
+RPCHelpMan importwallet();
// Ensure that fee levels defined in the wallet are at least as high
// as the default levels for node policy.
@@ -37,9 +37,12 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
static std::shared_ptr<CWallet> TestLoadWallet(interfaces::Chain& chain)
{
+ DatabaseOptions options;
+ DatabaseStatus status;
bilingual_str error;
std::vector<bilingual_str> warnings;
- auto wallet = CWallet::CreateWalletFromFile(chain, WalletLocation(""), error, warnings);
+ auto database = MakeWalletDatabase("", options, status, error);
+ auto wallet = CWallet::Create(chain, "", std::move(database), options.create_flags, error, warnings);
wallet->postInitProcess();
return wallet;
}
@@ -85,7 +88,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions fails to read an unknown start block.
{
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -104,7 +107,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -130,7 +133,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -155,7 +158,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Verify ScanForWalletTransactions scans no blocks.
{
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -194,7 +197,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// before the missing block, and success for a key whose creation time is
// after.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
AddWallet(wallet);
@@ -219,7 +222,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(keys);
- UniValue response = importmulti(request);
+ UniValue response = importmulti().HandleRequest(request);
BOOST_CHECK_EQUAL(response.write(),
strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation "
"timestamp %d. There was an error reading a block from time %d, which is after or within %d "
@@ -229,7 +232,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
"downloading and rescanning the relevant blocks (see -reindex and -rescan "
"options).\"}},{\"success\":true}]",
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
- RemoveWallet(wallet);
+ RemoveWallet(wallet, nullopt);
}
}
@@ -259,7 +262,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Import key into wallet and call dumpwallet to create backup file.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
{
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore);
@@ -274,14 +277,14 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(backup_file);
- ::dumpwallet(request);
- RemoveWallet(wallet);
+ ::dumpwallet().HandleRequest(request);
+ RemoveWallet(wallet, nullopt);
}
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan();
@@ -291,8 +294,8 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.push_back(backup_file);
AddWallet(wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
- ::importwallet(request);
- RemoveWallet(wallet);
+ ::importwallet().HandleRequest(request);
+ RemoveWallet(wallet, nullopt);
BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U);
@@ -317,7 +320,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
NodeContext node;
auto chain = interfaces::MakeChain(node);
- CWallet wallet(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
auto spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
CWalletTx wtx(&wallet, m_coinbase_txns.back());
@@ -492,7 +495,7 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), CreateMockWalletDatabase());
+ wallet = MakeUnique<CWallet>(m_chain.get(), "", CreateMockWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
@@ -610,7 +613,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), CreateDummyWalletDatabase());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), "", CreateDummyWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
wallet->SetMinVersion(FEATURE_LATEST);
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index c132a4be42..73e11a5b52 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -33,6 +33,8 @@
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
+#include <univalue.h>
+
#include <algorithm>
#include <assert.h>
@@ -54,6 +56,42 @@ static RecursiveMutex cs_wallets;
static std::vector<std::shared_ptr<CWallet>> vpwallets GUARDED_BY(cs_wallets);
static std::list<LoadWalletFn> g_load_wallet_fns GUARDED_BY(cs_wallets);
+bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
+{
+ util::SettingsValue setting_value = chain.getRwSetting("wallet");
+ if (!setting_value.isArray()) setting_value.setArray();
+ for (const util::SettingsValue& value : setting_value.getValues()) {
+ if (value.isStr() && value.get_str() == wallet_name) return true;
+ }
+ setting_value.push_back(wallet_name);
+ return chain.updateRwSetting("wallet", setting_value);
+}
+
+bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
+{
+ util::SettingsValue setting_value = chain.getRwSetting("wallet");
+ if (!setting_value.isArray()) return true;
+ util::SettingsValue new_value(util::SettingsValue::VARR);
+ for (const util::SettingsValue& value : setting_value.getValues()) {
+ if (!value.isStr() || value.get_str() != wallet_name) new_value.push_back(value);
+ }
+ if (new_value.size() == setting_value.size()) return true;
+ return chain.updateRwSetting("wallet", new_value);
+}
+
+static void UpdateWalletSetting(interfaces::Chain& chain,
+ const std::string& wallet_name,
+ Optional<bool> load_on_startup,
+ std::vector<bilingual_str>& warnings)
+{
+ if (load_on_startup == nullopt) return;
+ if (load_on_startup.get() && !AddWalletSetting(chain, wallet_name)) {
+ warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup."));
+ } else if (!load_on_startup.get() && !RemoveWalletSetting(chain, wallet_name)) {
+ warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may still be loaded next node startup."));
+ }
+}
+
bool AddWallet(const std::shared_ptr<CWallet>& wallet)
{
LOCK(cs_wallets);
@@ -65,18 +103,32 @@ bool AddWallet(const std::shared_ptr<CWallet>& wallet)
return true;
}
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet)
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings)
{
assert(wallet);
+
+ interfaces::Chain& chain = wallet->chain();
+ std::string name = wallet->GetName();
+
// Unregister with the validation interface which also drops shared ponters.
wallet->m_chain_notifications_handler.reset();
LOCK(cs_wallets);
std::vector<std::shared_ptr<CWallet>>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet);
if (i == vpwallets.end()) return false;
vpwallets.erase(i);
+
+ // Write the wallet setting
+ UpdateWalletSetting(chain, name, load_on_start, warnings);
+
return true;
}
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start)
+{
+ std::vector<bilingual_str> warnings;
+ return RemoveWallet(wallet, load_on_start, warnings);
+}
+
std::vector<std::shared_ptr<CWallet>> GetWallets()
{
LOCK(cs_wallets);
@@ -148,48 +200,54 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
}
namespace {
-std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
try {
- if (!CWallet::Verify(chain, location, error, warnings)) {
+ std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
+ if (!database) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
return nullptr;
}
- std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings);
+ std::shared_ptr<CWallet> wallet = CWallet::Create(chain, name, std::move(database), options.create_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
+ status = DatabaseStatus::FAILED_LOAD;
return nullptr;
}
AddWallet(wallet);
wallet->postInitProcess();
+
+ // Write the wallet setting
+ UpdateWalletSetting(chain, name, load_on_start, warnings);
+
return wallet;
} catch (const std::runtime_error& e) {
error = Untranslated(e.what());
+ status = DatabaseStatus::FAILED_LOAD;
return nullptr;
}
}
} // namespace
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
- auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(location.GetName()));
+ auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name));
if (!result.second) {
error = Untranslated("Wallet already being loading.");
+ status = DatabaseStatus::FAILED_LOAD;
return nullptr;
}
- auto wallet = LoadWalletInternal(chain, location, error, warnings);
+ auto wallet = LoadWalletInternal(chain, name, load_on_start, options, status, error, warnings);
WITH_LOCK(g_loading_wallet_mutex, g_loading_wallet_set.erase(result.first));
return wallet;
}
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
- return LoadWallet(chain, WalletLocation(name), error, warnings);
-}
+ uint64_t wallet_creation_flags = options.create_flags;
+ const SecureString& passphrase = options.create_passphrase;
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result)
-{
// Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET);
@@ -198,43 +256,42 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET;
}
- // Check the wallet file location
- WalletLocation location(name);
- if (location.Exists()) {
- error = strprintf(Untranslated("Wallet %s already exists."), location.GetName());
- return WalletCreationStatus::CREATION_FAILED;
- }
-
// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
- if (!CWallet::Verify(chain, location, error, warnings)) {
+ std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
+ if (!database) {
error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_VERIFY;
+ return nullptr;
}
// Do not allow a passphrase when private keys are disabled
if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.");
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
}
// Make the wallet
- std::shared_ptr<CWallet> wallet = CWallet::CreateWalletFromFile(chain, location, error, warnings, wallet_creation_flags);
+ std::shared_ptr<CWallet> wallet = CWallet::Create(chain, name, std::move(database), wallet_creation_flags, error, warnings);
if (!wallet) {
error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
}
// Encrypt the wallet
if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (!wallet->EncryptWallet(passphrase)) {
error = Untranslated("Error: Wallet created but failed to encrypt.");
- return WalletCreationStatus::ENCRYPTION_FAILED;
+ status = DatabaseStatus::FAILED_ENCRYPT;
+ return nullptr;
}
if (!create_blank) {
// Unlock the wallet
if (!wallet->Unlock(passphrase)) {
error = Untranslated("Error: Wallet was encrypted but could not be unlocked");
- return WalletCreationStatus::ENCRYPTION_FAILED;
+ status = DatabaseStatus::FAILED_ENCRYPT;
+ return nullptr;
}
// Set a seed for the wallet
@@ -246,7 +303,8 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
for (auto spk_man : wallet->GetActiveScriptPubKeyMans()) {
if (!spk_man->SetupGeneration()) {
error = Untranslated("Unable to generate initial keys");
- return WalletCreationStatus::CREATION_FAILED;
+ status = DatabaseStatus::FAILED_CREATE;
+ return nullptr;
}
}
}
@@ -258,8 +316,12 @@ WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString&
}
AddWallet(wallet);
wallet->postInitProcess();
- result = wallet;
- return WalletCreationStatus::SUCCESS;
+
+ // Write the wallet settings
+ UpdateWalletSetting(chain, name, load_on_start, warnings);
+
+ status = DatabaseStatus::SUCCESS;
+ return wallet;
}
const uint256 CWalletTx::ABANDON_HASH(UINT256_ONE());
@@ -276,7 +338,7 @@ std::string COutput::ToString() const
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
- LOCK(cs_wallet);
+ AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
if (it == mapWallet.end())
return nullptr;
@@ -1210,15 +1272,13 @@ void CWallet::BlockUntilSyncedToCurrentChain() const {
isminetype CWallet::IsMine(const CTxIn &txin) const
{
+ AssertLockHeld(cs_wallet);
+ std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
+ if (mi != mapWallet.end())
{
- LOCK(cs_wallet);
- std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
- if (mi != mapWallet.end())
- {
- const CWalletTx& prev = (*mi).second;
- if (txin.prevout.n < prev.tx->vout.size())
- return IsMine(prev.tx->vout[txin.prevout.n]);
- }
+ const CWalletTx& prev = (*mi).second;
+ if (txin.prevout.n < prev.tx->vout.size())
+ return IsMine(prev.tx->vout[txin.prevout.n]);
}
return ISMINE_NO;
}
@@ -1243,16 +1303,19 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
isminetype CWallet::IsMine(const CTxOut& txout) const
{
+ AssertLockHeld(cs_wallet);
return IsMine(txout.scriptPubKey);
}
isminetype CWallet::IsMine(const CTxDestination& dest) const
{
+ AssertLockHeld(cs_wallet);
return IsMine(GetScriptForDestination(dest));
}
isminetype CWallet::IsMine(const CScript& script) const
{
+ AssertLockHeld(cs_wallet);
isminetype result = ISMINE_NO;
for (const auto& spk_man_pair : m_spk_managers) {
result = std::max(result, spk_man_pair.second->IsMine(script));
@@ -1264,6 +1327,7 @@ CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) cons
{
if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range");
+ LOCK(cs_wallet);
return ((IsMine(txout) & filter) ? txout.nValue : 0);
}
@@ -1281,13 +1345,12 @@ bool CWallet::IsChange(const CScript& script) const
// a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change).
+ AssertLockHeld(cs_wallet);
if (IsMine(script))
{
CTxDestination address;
if (!ExtractDestination(script, address))
return true;
-
- LOCK(cs_wallet);
if (!FindAddressBookEntry(address)) {
return true;
}
@@ -1297,6 +1360,7 @@ bool CWallet::IsChange(const CScript& script) const
CAmount CWallet::GetChange(const CTxOut& txout) const
{
+ AssertLockHeld(cs_wallet);
if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range");
return (IsChange(txout) ? txout.nValue : 0);
@@ -1304,6 +1368,7 @@ CAmount CWallet::GetChange(const CTxOut& txout) const
bool CWallet::IsMine(const CTransaction& tx) const
{
+ AssertLockHeld(cs_wallet);
for (const CTxOut& txout : tx.vout)
if (IsMine(txout))
return true;
@@ -1362,6 +1427,7 @@ CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) c
CAmount CWallet::GetChange(const CTransaction& tx) const
{
+ LOCK(cs_wallet);
CAmount nChange = 0;
for (const CTxOut& txout : tx.vout)
{
@@ -1597,6 +1663,7 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
nFee = nDebit - nValueOut;
}
+ LOCK(pwallet->cs_wallet);
// Sent/received.
for (unsigned int i = 0; i < tx->vout.size(); ++i)
{
@@ -1965,36 +2032,38 @@ bool CWalletTx::InMempool() const
bool CWalletTx::IsTrusted() const
{
- std::set<uint256> s;
- return IsTrusted(s);
+ std::set<uint256> trusted_parents;
+ LOCK(pwallet->cs_wallet);
+ return pwallet->IsTrusted(*this, trusted_parents);
}
-bool CWalletTx::IsTrusted(std::set<uint256>& trusted_parents) const
+bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const
{
+ AssertLockHeld(cs_wallet);
// Quick answer in most cases
- if (!pwallet->chain().checkFinalTx(*tx)) return false;
- int nDepth = GetDepthInMainChain();
+ if (!chain().checkFinalTx(*wtx.tx)) return false;
+ int nDepth = wtx.GetDepthInMainChain();
if (nDepth >= 1) return true;
if (nDepth < 0) return false;
// using wtx's cached debit
- if (!pwallet->m_spend_zero_conf_change || !IsFromMe(ISMINE_ALL)) return false;
+ if (!m_spend_zero_conf_change || !wtx.IsFromMe(ISMINE_ALL)) return false;
// Don't trust unconfirmed transactions from us unless they are in the mempool.
- if (!InMempool()) return false;
+ if (!wtx.InMempool()) return false;
// Trusted if all inputs are from us and are in the mempool:
- for (const CTxIn& txin : tx->vin)
+ for (const CTxIn& txin : wtx.tx->vin)
{
// Transactions not sent by us: not trusted
- const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash);
+ const CWalletTx* parent = GetWalletTx(txin.prevout.hash);
if (parent == nullptr) return false;
const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
// Check that this specific input being spent is trusted
- if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) return false;
+ if (IsMine(parentOut) != ISMINE_SPENDABLE) return false;
// If we've already trusted this parent, continue
if (trusted_parents.count(parent->GetHash())) continue;
// Recurse to check that the parent is also trusted
- if (!parent->IsTrusted(trusted_parents)) return false;
+ if (!IsTrusted(*parent, trusted_parents)) return false;
trusted_parents.insert(parent->GetHash());
}
return true;
@@ -2080,7 +2149,7 @@ CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) cons
for (const auto& entry : mapWallet)
{
const CWalletTx& wtx = entry.second;
- const bool is_trusted{wtx.IsTrusted(trusted_parents)};
+ const bool is_trusted{IsTrusted(wtx, trusted_parents)};
const int tx_depth{wtx.GetDepthInMainChain()};
const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)};
const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)};
@@ -2148,7 +2217,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
if (nDepth == 0 && !wtx.InMempool())
continue;
- bool safeTx = wtx.IsTrusted(trusted_parents);
+ bool safeTx = IsTrusted(wtx, trusted_parents);
// We should not consider coins from transactions that are replacing
// other transactions.
@@ -2284,6 +2353,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const
{
+ AssertLockHeld(cs_wallet);
const CTransaction* ptx = &tx;
int n = output;
while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) {
@@ -2580,10 +2650,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
if (!coinControl.IsSelected(txin.prevout)) {
tx.vin.push_back(txin);
- if (lockUnspents) {
- LockCoin(txin.prevout);
- }
}
+ if (lockUnspents) {
+ LockCoin(txin.prevout);
+ }
+
}
return true;
@@ -3172,28 +3243,10 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
return DBErrors::LOAD_OK;
}
-DBErrors CWallet::ZapWalletTx(std::list<CWalletTx>& vWtx)
-{
- DBErrors nZapWalletTxRet = WalletBatch(*database,"cr+").ZapWalletTx(vWtx);
- if (nZapWalletTxRet == DBErrors::NEED_REWRITE)
- {
- if (database->Rewrite("\x04pool"))
- {
- for (const auto& spk_man_pair : m_spk_managers) {
- spk_man_pair.second->RewriteDB();
- }
- }
- }
-
- if (nZapWalletTxRet != DBErrors::LOAD_OK)
- return nZapWalletTxRet;
-
- return DBErrors::LOAD_OK;
-}
-
bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
{
bool fUpdated = false;
+ bool is_mine;
{
LOCK(cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address);
@@ -3201,8 +3254,9 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add
m_address_book[address].SetLabel(strName);
if (!strPurpose.empty()) /* update purpose only if requested */
m_address_book[address].purpose = strPurpose;
+ is_mine = IsMine(address) != ISMINE_NO;
}
- NotifyAddressBookChanged(this, address, strName, IsMine(address) != ISMINE_NO,
+ NotifyAddressBookChanged(this, address, strName, is_mine,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false;
@@ -3217,30 +3271,31 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
bool CWallet::DelAddressBook(const CTxDestination& address)
{
- // If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
- // NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
- // When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
- if (IsMine(address)) {
- WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
- return false;
- }
-
+ bool is_mine;
+ WalletBatch batch(*database);
{
LOCK(cs_wallet);
-
+ // If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
+ // NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
+ // When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
+ if (IsMine(address)) {
+ WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
+ return false;
+ }
// Delete destdata tuples associated with address
std::string strAddress = EncodeDestination(address);
for (const std::pair<const std::string, std::string> &item : m_address_book[address].destdata)
{
- WalletBatch(*database).EraseDestData(strAddress, item.first);
+ batch.EraseDestData(strAddress, item.first);
}
m_address_book.erase(address);
+ is_mine = IsMine(address) != ISMINE_NO;
}
- NotifyAddressBookChanged(this, address, "", IsMine(address) != ISMINE_NO, "", CT_DELETED);
+ NotifyAddressBookChanged(this, address, "", is_mine, "", CT_DELETED);
- WalletBatch(*database).ErasePurpose(EncodeDestination(address));
- return WalletBatch(*database).EraseName(EncodeDestination(address));
+ batch.ErasePurpose(EncodeDestination(address));
+ return batch.EraseName(EncodeDestination(address));
}
size_t CWallet::KeypoolCountExternalKeys() const
@@ -3345,7 +3400,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
{
const CWalletTx& wtx = walletEntry.second;
- if (!wtx.IsTrusted(trusted_parents))
+ if (!IsTrusted(wtx, trusted_parents))
continue;
if (wtx.IsImmatureCoinBase())
@@ -3364,9 +3419,6 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const
continue;
CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
-
- if (!balances.count(addr))
- balances[addr] = 0;
balances[addr] += n;
}
}
@@ -3727,7 +3779,7 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
return values;
}
-bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
+std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string)
{
// Do some checking on wallet path. It should be either a:
//
@@ -3735,54 +3787,25 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
// 2. Path to an existing directory.
// 3. Path to a symlink to a directory.
// 4. For backwards compatibility, the name of a data file in -walletdir.
- LOCK(cs_wallets);
- const fs::path& wallet_path = location.GetPath();
+ const fs::path& wallet_path = fs::absolute(name, GetWalletDir());
fs::file_type path_type = fs::symlink_status(wallet_path).type();
if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
(path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
- (path_type == fs::regular_file && fs::path(location.GetName()).filename() == location.GetName()))) {
+ (path_type == fs::regular_file && fs::path(name).filename() == name))) {
error_string = Untranslated(strprintf(
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
"database/log.?????????? files can be stored, a location where such a directory could be created, "
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
- location.GetName(), GetWalletDir()));
- return false;
- }
-
- // Make sure that the wallet path doesn't clash with an existing wallet path
- if (IsWalletLoaded(wallet_path)) {
- error_string = Untranslated(strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName()));
- return false;
- }
-
- // Keep same database environment instance across Verify/Recover calls below.
- std::unique_ptr<WalletDatabase> database = CreateWalletDatabase(wallet_path);
-
- try {
- return database->Verify(error_string);
- } catch (const fs::filesystem_error& e) {
- error_string = Untranslated(strprintf("Error loading wallet %s. %s", location.GetName(), fsbridge::get_filesystem_error_message(e)));
- return false;
+ name, GetWalletDir()));
+ status = DatabaseStatus::FAILED_BAD_PATH;
+ return nullptr;
}
+ return MakeDatabase(wallet_path, options, status, error_string);
}
-std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags)
+std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
- const std::string walletFile = WalletDataFilePath(location.GetPath()).string();
-
- // needed to restore wallet transaction meta data after -zapwallettxes
- std::list<CWalletTx> vWtx;
-
- if (gArgs.GetBoolArg("-zapwallettxes", false)) {
- chain.initMessage(_("Zapping all transactions from wallet...").translated);
-
- std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(&chain, location, CreateWalletDatabase(location.GetPath()));
- DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
- if (nZapWalletRet != DBErrors::LOAD_OK) {
- error = strprintf(_("Error loading %s: Wallet corrupted"), walletFile);
- return nullptr;
- }
- }
+ const std::string& walletFile = database->Filename();
chain.initMessage(_("Loading wallet...").translated);
@@ -3790,7 +3813,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
bool fFirstRun = true;
// TODO: Can't use std::make_shared because we need a custom deleter but
// should be possible to use std::allocate_shared.
- std::shared_ptr<CWallet> walletInstance(new CWallet(&chain, location, CreateWalletDatabase(location.GetPath())), ReleaseWallet);
+ std::shared_ptr<CWallet> walletInstance(new CWallet(&chain, name, std::move(database)), ReleaseWallet);
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK) {
if (nLoadWalletRet == DBErrors::CORRUPT) {
@@ -4060,30 +4083,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
walletInstance->chainStateFlushed(chain.getTipLocator());
walletInstance->database->IncrementUpdateCounter();
-
- // Restore wallet transaction metadata after -zapwallettxes=1
- if (gArgs.GetBoolArg("-zapwallettxes", false) && gArgs.GetArg("-zapwallettxes", "1") != "2")
- {
- WalletBatch batch(*walletInstance->database);
-
- for (const CWalletTx& wtxOld : vWtx)
- {
- uint256 hash = wtxOld.GetHash();
- std::map<uint256, CWalletTx>::iterator mi = walletInstance->mapWallet.find(hash);
- if (mi != walletInstance->mapWallet.end())
- {
- const CWalletTx* copyFrom = &wtxOld;
- CWalletTx* copyTo = &mi->second;
- copyTo->mapValue = copyFrom->mapValue;
- copyTo->vOrderForm = copyFrom->vOrderForm;
- copyTo->nTimeReceived = copyFrom->nTimeReceived;
- copyTo->nTimeSmart = copyFrom->nTimeSmart;
- copyTo->fFromMe = copyFrom->fFromMe;
- copyTo->nOrderPos = copyFrom->nOrderPos;
- batch.WriteTx(*copyTo);
- }
- }
- }
}
{
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 2f9d301000..c54480612a 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -50,19 +50,14 @@ struct bilingual_str;
void UnloadWallet(std::shared_ptr<CWallet>&& wallet);
bool AddWallet(const std::shared_ptr<CWallet>& wallet);
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings);
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start);
std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name);
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings);
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
+std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet);
-
-enum class WalletCreationStatus {
- SUCCESS,
- CREATION_FAILED,
- ENCRYPTION_FAILED
-};
-
-WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, bilingual_str& error, std::vector<bilingual_str>& warnings, std::shared_ptr<CWallet>& result);
+std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
//! -paytxfee default
constexpr CAmount DEFAULT_PAY_TX_FEE = 0;
@@ -275,7 +270,7 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet,
class CWalletTx
{
private:
- const CWallet* pwallet;
+ const CWallet* const pwallet;
/** Constant used in hashBlock to indicate tx has been abandoned, only used at
* serialization/deserialization to avoid ambiguity with conflicted.
@@ -502,7 +497,6 @@ public:
bool InMempool() const;
bool IsTrusted() const;
- bool IsTrusted(std::set<uint256>& trusted_parents) const;
int64_t GetTxTime() const;
@@ -700,8 +694,8 @@ private:
/** Interface for accessing chain state. */
interfaces::Chain* m_chain;
- /** Wallet location which includes wallet name (see WalletLocation). */
- WalletLocation m_location;
+ /** Wallet name: relative directory name or "" for default wallet. */
+ std::string m_name;
/** Internal database handle. */
std::unique_ptr<WalletDatabase> database;
@@ -755,20 +749,18 @@ public:
bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- const WalletLocation& GetLocation() const { return m_location; }
-
/** Get a name for this wallet for logging/debugging purposes.
*/
- const std::string& GetName() const { return m_location.GetName(); }
+ const std::string& GetName() const { return m_name; }
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */
- CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database)
+ CWallet(interfaces::Chain* chain, const std::string& name, std::unique_ptr<WalletDatabase> database)
: m_chain(chain),
- m_location(location),
+ m_name(name),
database(std::move(database))
{
}
@@ -805,7 +797,8 @@ public:
/** Interface for accessing chain state. */
interfaces::Chain& chain() const { assert(m_chain); return *m_chain; }
- const CWalletTx* GetWalletTx(const uint256& hash) const;
+ const CWalletTx* GetWalletTx(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; }
@@ -1051,20 +1044,20 @@ public:
bool GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error);
bool GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error);
- isminetype IsMine(const CTxDestination& dest) const;
- isminetype IsMine(const CScript& script) const;
- isminetype IsMine(const CTxIn& txin) const;
+ isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ isminetype IsMine(const CTxIn& txin) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Returns amount of debit if the input matches the
* filter, otherwise returns 0
*/
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
- isminetype IsMine(const CTxOut& txout) const;
+ isminetype IsMine(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
- bool IsChange(const CTxOut& txout) const;
- bool IsChange(const CScript& script) const;
- CAmount GetChange(const CTxOut& txout) const;
- bool IsMine(const CTransaction& tx) const;
+ bool IsChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsChange(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ CAmount GetChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsMine(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
@@ -1075,7 +1068,6 @@ public:
void chainStateFlushed(const CBlockLocator& loc) override;
DBErrors LoadWallet(bool& fFirstRunRet);
- DBErrors ZapWalletTx(std::list<CWalletTx>& vWtx);
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose);
@@ -1153,11 +1145,8 @@ public:
/** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
- //! Verify wallet naming and perform salvage on the wallet if required
- static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error_string, std::vector<bilingual_str>& warnings);
-
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
- static std::shared_ptr<CWallet> CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, bilingual_str& error, std::vector<bilingual_str>& warnings, uint64_t wallet_creation_flags = 0);
+ static std::shared_ptr<CWallet> Create(interfaces::Chain& chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings);
/**
* Wallet post-init setup
@@ -1179,7 +1168,7 @@ public:
* Obviously holding cs_main/cs_wallet when going into this call may cause
* deadlock
*/
- void BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(cs_main, cs_wallet);
+ void BlockUntilSyncedToCurrentChain() const EXCLUSIVE_LOCKS_REQUIRED(!::cs_main, !cs_wallet);
/** set a single wallet flag */
void SetWalletFlag(uint64_t flags);
@@ -1285,7 +1274,7 @@ public:
void LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal);
//! Create new DescriptorScriptPubKeyMans and add them to the wallet
- void SetupDescriptorScriptPubKeyMans();
+ void SetupDescriptorScriptPubKeyMans() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Return the DescriptorScriptPubKeyMan for a WalletDescriptor if it is already in the wallet
DescriptorScriptPubKeyMan* GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const;
@@ -1340,4 +1329,11 @@ public:
// be IsAllFromMe).
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
+
+//! Add wallet name to persistent configuration so it will be loaded on startup.
+bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
+
+//! Remove wallet name from persistent configuration so it will not be loaded on startup.
+bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
+
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index fa6814d0d3..5bf21eb91f 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -13,6 +13,8 @@
#include <util/bip32.h>
#include <util/system.h>
#include <util/time.h>
+#include <util/translation.h>
+#include <wallet/bdb.h>
#include <wallet/wallet.h>
#include <atomic>
@@ -263,13 +265,17 @@ public:
static bool
ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
- CWalletScanState &wss, std::string& strType, std::string& strErr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
+ CWalletScanState &wss, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
try {
// Unserialize
// Taking advantage of the fact that pair serialization
// is just the two items serialized one after the other
ssKey >> strType;
+ // If we have a filter, check if this matches the filter
+ if (filter_fn && !filter_fn(strType)) {
+ return true;
+ }
if (strType == DBKeys::NAME) {
std::string strAddress;
ssKey >> strAddress;
@@ -668,11 +674,11 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
return true;
}
-bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr)
+bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn)
{
CWalletScanState dummy_wss;
LOCK(pwallet->cs_wallet);
- return ReadKeyValue(pwallet, ssKey, ssValue, dummy_wss, strType, strErr);
+ return ReadKeyValue(pwallet, ssKey, ssValue, dummy_wss, strType, strErr, filter_fn);
}
bool WalletBatch::IsKeyType(const std::string& strType)
@@ -926,23 +932,6 @@ DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<u
return DBErrors::LOAD_OK;
}
-DBErrors WalletBatch::ZapWalletTx(std::list<CWalletTx>& vWtx)
-{
- // build list of wallet TXs
- std::vector<uint256> vTxHash;
- DBErrors err = FindWalletTx(vTxHash, vWtx);
- if (err != DBErrors::LOAD_OK)
- return err;
-
- // erase each wallet TX
- for (const uint256& hash : vTxHash) {
- if (!EraseTx(hash))
- return DBErrors::CORRUPT;
- }
-
- return DBErrors::LOAD_OK;
-}
-
void MaybeCompactWalletDB()
{
static std::atomic<bool> fOneThread(false);
@@ -1006,16 +995,41 @@ bool WalletBatch::TxnAbort()
return m_batch->TxnAbort();
}
-bool IsWalletLoaded(const fs::path& wallet_path)
+std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
{
- return IsBDBWalletLoaded(wallet_path);
-}
+ bool exists;
+ try {
+ exists = fs::symlink_status(path).type() != fs::file_not_found;
+ } catch (const fs::filesystem_error& e) {
+ error = Untranslated(strprintf("Failed to access database path '%s': %s", path.string(), fsbridge::get_filesystem_error_message(e)));
+ status = DatabaseStatus::FAILED_BAD_PATH;
+ return nullptr;
+ }
-/** Return object for accessing database at specified path. */
-std::unique_ptr<WalletDatabase> CreateWalletDatabase(const fs::path& path)
-{
- std::string filename;
- return MakeUnique<BerkeleyDatabase>(GetWalletEnv(path, filename), std::move(filename));
+ Optional<DatabaseFormat> format;
+ if (exists) {
+ if (ExistsBerkeleyDatabase(path)) {
+ format = DatabaseFormat::BERKELEY;
+ }
+ } else if (options.require_existing) {
+ error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", path.string()));
+ status = DatabaseStatus::FAILED_NOT_FOUND;
+ return nullptr;
+ }
+
+ if (!format && options.require_existing) {
+ error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", path.string()));
+ status = DatabaseStatus::FAILED_BAD_FORMAT;
+ return nullptr;
+ }
+
+ if (format && options.require_create) {
+ error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", path.string()));
+ status = DatabaseStatus::FAILED_ALREADY_EXISTS;
+ return nullptr;
+ }
+
+ return MakeBerkeleyDatabase(path, options, status, error);
}
/** Return object for accessing dummy database with no read/write capabilities. */
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index 64d60b1f44..eda810ed8a 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -257,7 +257,6 @@ public:
DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWalletTx>& vWtx);
- DBErrors ZapWalletTx(std::list<CWalletTx>& vWtx);
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut);
/* Function to determine if a certain KV/key-type is a key (cryptographical key) type */
static bool IsKeyType(const std::string& strType);
@@ -280,14 +279,11 @@ private:
//! Compacts BDB state so that wallet.dat is self-contained (if there are changes)
void MaybeCompactWalletDB();
-//! Unserialize a given Key-Value pair and load it into the wallet
-bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr);
-
-/** Return whether a wallet database is currently loaded. */
-bool IsWalletLoaded(const fs::path& wallet_path);
+//! Callback for filtering key types to deserialize in ReadKeyValue
+using KeyFilterFn = std::function<bool(const std::string&)>;
-/** Return object for accessing database at specified path. */
-std::unique_ptr<WalletDatabase> CreateWalletDatabase(const fs::path& path);
+//! Unserialize a given Key-Value pair and load it into the wallet
+bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr);
/** Return object for accessing dummy database with no read/write capabilities. */
std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase();
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index 9b51461843..4452840eb1 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -21,21 +21,9 @@ static void WalletToolReleaseWallet(CWallet* wallet)
delete wallet;
}
-static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path)
+static void WalletCreate(CWallet* wallet_instance)
{
- if (fs::exists(path)) {
- tfm::format(std::cerr, "Error: File exists already\n");
- return nullptr;
- }
- // dummy chain interface
- std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), CreateWalletDatabase(path)), WalletToolReleaseWallet);
LOCK(wallet_instance->cs_wallet);
- bool first_run = true;
- DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run);
- if (load_wallet_ret != DBErrors::LOAD_OK) {
- tfm::format(std::cerr, "Error creating %s", name);
- return nullptr;
- }
wallet_instance->SetMinVersion(FEATURE_HD_SPLIT);
@@ -46,18 +34,26 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
tfm::format(std::cout, "Topping up keypool...\n");
wallet_instance->TopUpKeyPool();
- return wallet_instance;
}
-static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path)
+static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, bool create)
{
- if (!fs::exists(path)) {
- tfm::format(std::cerr, "Error: Wallet files does not exist\n");
+ DatabaseOptions options;
+ DatabaseStatus status;
+ if (create) {
+ options.require_create = true;
+ } else {
+ options.require_existing = true;
+ }
+ bilingual_str error;
+ std::unique_ptr<WalletDatabase> database = MakeDatabase(path, options, status, error);
+ if (!database) {
+ tfm::format(std::cerr, "%s\n", error.original);
return nullptr;
}
// dummy chain interface
- std::shared_ptr<CWallet> wallet_instance(new CWallet(nullptr /* chain */, WalletLocation(name), CreateWalletDatabase(path)), WalletToolReleaseWallet);
+ std::shared_ptr<CWallet> wallet_instance{new CWallet(nullptr /* chain */, name, std::move(database)), WalletToolReleaseWallet};
DBErrors load_wallet_ret;
try {
bool first_run;
@@ -89,6 +85,8 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
}
}
+ if (create) WalletCreate(wallet_instance.get());
+
return wallet_instance;
}
@@ -109,19 +107,14 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
fs::path path = fs::absolute(name, GetWalletDir());
if (command == "create") {
- std::shared_ptr<CWallet> wallet_instance = CreateWallet(name, path);
+ std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ true);
if (wallet_instance) {
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
}
} else if (command == "info" || command == "salvage") {
- if (!fs::exists(path)) {
- tfm::format(std::cerr, "Error: no wallet file at %s\n", name);
- return false;
- }
-
if (command == "info") {
- std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
+ std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, /* create= */ false);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h
index 8ee3355f02..d0b8d6812a 100644
--- a/src/wallet/wallettool.h
+++ b/src/wallet/wallettool.h
@@ -9,8 +9,6 @@
namespace WalletTool {
-std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path);
-std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path);
void WalletShowInfo(CWallet* wallet_instance);
bool ExecuteWalletToolFunc(const std::string& command, const std::string& file);
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index 8bac0608a9..e4c72aed98 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -29,7 +29,7 @@ fs::path GetWalletDir()
return path;
}
-static bool IsBerkeleyBtree(const fs::path& path)
+bool IsBerkeleyBtree(const fs::path& path)
{
if (!fs::exists(path)) return false;
@@ -91,19 +91,3 @@ std::vector<fs::path> ListWalletDir()
return paths;
}
-
-WalletLocation::WalletLocation(const std::string& name)
- : m_name(name)
- , m_path(fs::absolute(name, GetWalletDir()))
-{
-}
-
-bool WalletLocation::Exists() const
-{
- fs::path path = m_path;
- // For the default wallet, check specifically for the wallet.dat file
- if (m_name.empty()) {
- path = fs::absolute("wallet.dat", m_path);
- }
- return fs::symlink_status(path).type() != fs::file_not_found;
-}
diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h
index a4e4fda8a1..afdcb2e18a 100644
--- a/src/wallet/walletutil.h
+++ b/src/wallet/walletutil.h
@@ -67,26 +67,6 @@ fs::path GetWalletDir();
//! Get wallets in wallet directory.
std::vector<fs::path> ListWalletDir();
-//! The WalletLocation class provides wallet information.
-class WalletLocation final
-{
- std::string m_name;
- fs::path m_path;
-
-public:
- explicit WalletLocation() {}
- explicit WalletLocation(const std::string& name);
-
- //! Get wallet name.
- const std::string& GetName() const { return m_name; }
-
- //! Get wallet absolute path.
- const fs::path& GetPath() const { return m_path; }
-
- //! Return whether the wallet exists.
- bool Exists() const;
-};
-
/** Descriptor with some wallet metadata */
class WalletDescriptor
{
diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp
index aae760adde..0d0428f3c0 100644
--- a/src/zmq/zmqabstractnotifier.cpp
+++ b/src/zmq/zmqabstractnotifier.cpp
@@ -4,6 +4,8 @@
#include <zmq/zmqabstractnotifier.h>
+#include <cassert>
+
const int CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM;
CZMQAbstractNotifier::~CZMQAbstractNotifier()
diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h
index 887dde7b27..34d7e5ef03 100644
--- a/src/zmq/zmqabstractnotifier.h
+++ b/src/zmq/zmqabstractnotifier.h
@@ -5,12 +5,16 @@
#ifndef BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
#define BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
-#include <zmq/zmqconfig.h>
+#include <util/memory.h>
+
+#include <memory>
+#include <string>
class CBlockIndex;
+class CTransaction;
class CZMQAbstractNotifier;
-typedef CZMQAbstractNotifier* (*CZMQNotifierFactory)();
+using CZMQNotifierFactory = std::unique_ptr<CZMQAbstractNotifier> (*)();
class CZMQAbstractNotifier
{
@@ -21,9 +25,9 @@ public:
virtual ~CZMQAbstractNotifier();
template <typename T>
- static CZMQAbstractNotifier* Create()
+ static std::unique_ptr<CZMQAbstractNotifier> Create()
{
- return new T();
+ return MakeUnique<T>();
}
std::string GetType() const { return type; }
diff --git a/src/zmq/zmqconfig.h b/src/zmq/zmqconfig.h
deleted file mode 100644
index 5f0036206d..0000000000
--- a/src/zmq/zmqconfig.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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.
-
-#ifndef BITCOIN_ZMQ_ZMQCONFIG_H
-#define BITCOIN_ZMQ_ZMQCONFIG_H
-
-#if defined(HAVE_CONFIG_H)
-#include <config/bitcoin-config.h>
-#endif
-
-#include <stdarg.h>
-
-#if ENABLE_ZMQ
-#include <zmq.h>
-#endif
-
-#include <primitives/transaction.h>
-
-void zmqError(const char *str);
-
-#endif // BITCOIN_ZMQ_ZMQCONFIG_H
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index d55b106e04..a22772baed 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -4,15 +4,13 @@
#include <zmq/zmqnotificationinterface.h>
#include <zmq/zmqpublishnotifier.h>
+#include <zmq/zmqutil.h>
+
+#include <zmq.h>
#include <validation.h>
#include <util/system.h>
-void zmqError(const char *str)
-{
- LogPrint(BCLog::ZMQ, "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno));
-}
-
CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(nullptr)
{
}
@@ -20,61 +18,52 @@ CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(nullptr)
CZMQNotificationInterface::~CZMQNotificationInterface()
{
Shutdown();
-
- for (std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin(); i!=notifiers.end(); ++i)
- {
- delete *i;
- }
}
std::list<const CZMQAbstractNotifier*> CZMQNotificationInterface::GetActiveNotifiers() const
{
std::list<const CZMQAbstractNotifier*> result;
- for (const auto* n : notifiers) {
- result.push_back(n);
+ for (const auto& n : notifiers) {
+ result.push_back(n.get());
}
return result;
}
CZMQNotificationInterface* CZMQNotificationInterface::Create()
{
- CZMQNotificationInterface* notificationInterface = nullptr;
std::map<std::string, CZMQNotifierFactory> factories;
- std::list<CZMQAbstractNotifier*> notifiers;
-
factories["pubhashblock"] = CZMQAbstractNotifier::Create<CZMQPublishHashBlockNotifier>;
factories["pubhashtx"] = CZMQAbstractNotifier::Create<CZMQPublishHashTransactionNotifier>;
factories["pubrawblock"] = CZMQAbstractNotifier::Create<CZMQPublishRawBlockNotifier>;
factories["pubrawtx"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionNotifier>;
+ std::list<std::unique_ptr<CZMQAbstractNotifier>> notifiers;
for (const auto& entry : factories)
{
std::string arg("-zmq" + entry.first);
if (gArgs.IsArgSet(arg))
{
- CZMQNotifierFactory factory = entry.second;
- std::string address = gArgs.GetArg(arg, "");
- CZMQAbstractNotifier *notifier = factory();
+ const auto& factory = entry.second;
+ const std::string address = gArgs.GetArg(arg, "");
+ std::unique_ptr<CZMQAbstractNotifier> notifier = factory();
notifier->SetType(entry.first);
notifier->SetAddress(address);
notifier->SetOutboundMessageHighWaterMark(static_cast<int>(gArgs.GetArg(arg + "hwm", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM)));
- notifiers.push_back(notifier);
+ notifiers.push_back(std::move(notifier));
}
}
if (!notifiers.empty())
{
- notificationInterface = new CZMQNotificationInterface();
- notificationInterface->notifiers = notifiers;
+ std::unique_ptr<CZMQNotificationInterface> notificationInterface(new CZMQNotificationInterface());
+ notificationInterface->notifiers = std::move(notifiers);
- if (!notificationInterface->Initialize())
- {
- delete notificationInterface;
- notificationInterface = nullptr;
+ if (notificationInterface->Initialize()) {
+ return notificationInterface.release();
}
}
- return notificationInterface;
+ return nullptr;
}
// Called at startup to conditionally set up ZMQ socket(s)
@@ -95,26 +84,15 @@ bool CZMQNotificationInterface::Initialize()
return false;
}
- std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin();
- for (; i!=notifiers.end(); ++i)
- {
- CZMQAbstractNotifier *notifier = *i;
- if (notifier->Initialize(pcontext))
- {
+ for (auto& notifier : notifiers) {
+ if (notifier->Initialize(pcontext)) {
LogPrint(BCLog::ZMQ, "zmq: Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress());
- }
- else
- {
+ } else {
LogPrint(BCLog::ZMQ, "zmq: Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress());
- break;
+ return false;
}
}
- if (i!=notifiers.end())
- {
- return false;
- }
-
return true;
}
@@ -124,9 +102,7 @@ void CZMQNotificationInterface::Shutdown()
LogPrint(BCLog::ZMQ, "zmq: Shutdown notification interface\n");
if (pcontext)
{
- for (std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin(); i!=notifiers.end(); ++i)
- {
- CZMQAbstractNotifier *notifier = *i;
+ for (auto& notifier : notifiers) {
LogPrint(BCLog::ZMQ, "zmq: Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress());
notifier->Shutdown();
}
@@ -136,45 +112,43 @@ void CZMQNotificationInterface::Shutdown()
}
}
-void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
-{
- if (fInitialDownload || pindexNew == pindexFork) // In IBD or blocks were disconnected without any new ones
- return;
+namespace {
- for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
- {
- CZMQAbstractNotifier *notifier = *i;
- if (notifier->NotifyBlock(pindexNew))
- {
- i++;
- }
- else
- {
+template <typename Function>
+void TryForEachAndRemoveFailed(std::list<std::unique_ptr<CZMQAbstractNotifier>>& notifiers, const Function& func)
+{
+ for (auto i = notifiers.begin(); i != notifiers.end(); ) {
+ CZMQAbstractNotifier* notifier = i->get();
+ if (func(notifier)) {
+ ++i;
+ } else {
notifier->Shutdown();
i = notifiers.erase(i);
}
}
}
+} // anonymous namespace
+
+void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
+{
+ if (fInitialDownload || pindexNew == pindexFork) // In IBD or blocks were disconnected without any new ones
+ return;
+
+ TryForEachAndRemoveFailed(notifiers, [pindexNew](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyBlock(pindexNew);
+ });
+}
+
void CZMQNotificationInterface::TransactionAddedToMempool(const CTransactionRef& ptx)
{
// Used by BlockConnected and BlockDisconnected as well, because they're
// all the same external callback.
const CTransaction& tx = *ptx;
- for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
- {
- CZMQAbstractNotifier *notifier = *i;
- if (notifier->NotifyTransaction(tx))
- {
- i++;
- }
- else
- {
- notifier->Shutdown();
- i = notifiers.erase(i);
- }
- }
+ TryForEachAndRemoveFailed(notifiers, [&tx](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyTransaction(tx);
+ });
}
void CZMQNotificationInterface::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected)
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index 60f3b6148a..0686960ed4 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -7,6 +7,7 @@
#include <validationinterface.h>
#include <list>
+#include <memory>
class CBlockIndex;
class CZMQAbstractNotifier;
@@ -34,7 +35,7 @@ private:
CZMQNotificationInterface();
void *pcontext;
- std::list<CZMQAbstractNotifier*> notifiers;
+ std::list<std::unique_ptr<CZMQAbstractNotifier>> notifiers;
};
extern CZMQNotificationInterface* g_zmq_notification_interface;
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 04806903c2..d4d21b05ba 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -2,13 +2,23 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <zmq/zmqpublishnotifier.h>
+
#include <chain.h>
#include <chainparams.h>
+#include <rpc/server.h>
#include <streams.h>
-#include <zmq/zmqpublishnotifier.h>
-#include <validation.h>
#include <util/system.h>
-#include <rpc/server.h>
+#include <validation.h>
+#include <zmq/zmqutil.h>
+
+#include <zmq.h>
+
+#include <cstdarg>
+#include <cstddef>
+#include <map>
+#include <string>
+#include <utility>
static std::multimap<std::string, CZMQAbstractPublishNotifier*> mapPublishNotifiers;
@@ -86,6 +96,14 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
return false;
}
+ const int so_keepalive_option {1};
+ rc = zmq_setsockopt(psocket, ZMQ_TCP_KEEPALIVE, &so_keepalive_option, sizeof(so_keepalive_option));
+ if (rc != 0) {
+ zmqError("Failed to set SO_KEEPALIVE");
+ zmq_close(psocket);
+ return false;
+ }
+
rc = zmq_bind(psocket, address.c_str());
if (rc != 0)
{
@@ -141,7 +159,7 @@ void CZMQAbstractPublishNotifier::Shutdown()
psocket = nullptr;
}
-bool CZMQAbstractPublishNotifier::SendMessage(const char *command, const void* data, size_t size)
+bool CZMQAbstractPublishNotifier::SendZmqMessage(const char *command, const void* data, size_t size)
{
assert(psocket);
@@ -165,7 +183,7 @@ bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
char data[32];
for (unsigned int i = 0; i < 32; i++)
data[31 - i] = hash.begin()[i];
- return SendMessage(MSG_HASHBLOCK, data, 32);
+ return SendZmqMessage(MSG_HASHBLOCK, data, 32);
}
bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
@@ -175,7 +193,7 @@ bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &t
char data[32];
for (unsigned int i = 0; i < 32; i++)
data[31 - i] = hash.begin()[i];
- return SendMessage(MSG_HASHTX, data, 32);
+ return SendZmqMessage(MSG_HASHTX, data, 32);
}
bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
@@ -196,7 +214,7 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
ss << block;
}
- return SendMessage(MSG_RAWBLOCK, &(*ss.begin()), ss.size());
+ return SendZmqMessage(MSG_RAWBLOCK, &(*ss.begin()), ss.size());
}
bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
@@ -205,5 +223,5 @@ bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &tr
LogPrint(BCLog::ZMQ, "zmq: Publish rawtx %s\n", hash.GetHex());
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ss << transaction;
- return SendMessage(MSG_RAWTX, &(*ss.begin()), ss.size());
+ return SendZmqMessage(MSG_RAWTX, &(*ss.begin()), ss.size());
}
diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h
index 278fdb94d2..eb9ae881be 100644
--- a/src/zmq/zmqpublishnotifier.h
+++ b/src/zmq/zmqpublishnotifier.h
@@ -22,7 +22,7 @@ public:
* data
* message sequence number
*/
- bool SendMessage(const char *command, const void* data, size_t size);
+ bool SendZmqMessage(const char *command, const void* data, size_t size);
bool Initialize(void *pcontext) override;
void Shutdown() override;
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
index cce6210129..1dd751b493 100644
--- a/src/zmq/zmqrpc.cpp
+++ b/src/zmq/zmqrpc.cpp
@@ -13,9 +13,9 @@
namespace {
-UniValue getzmqnotifications(const JSONRPCRequest& request)
+static RPCHelpMan getzmqnotifications()
{
- RPCHelpMan{"getzmqnotifications",
+ return RPCHelpMan{"getzmqnotifications",
"\nReturns information about the active ZeroMQ notifications.\n",
{},
RPCResult{
@@ -33,8 +33,8 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
HelpExampleCli("getzmqnotifications", "")
+ HelpExampleRpc("getzmqnotifications", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue result(UniValue::VARR);
if (g_zmq_notification_interface != nullptr) {
for (const auto* n : g_zmq_notification_interface->GetActiveNotifiers()) {
@@ -47,6 +47,8 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
const CRPCCommand commands[] =
diff --git a/src/zmq/zmqutil.cpp b/src/zmq/zmqutil.cpp
new file mode 100644
index 0000000000..f07a4ae9fd
--- /dev/null
+++ b/src/zmq/zmqutil.cpp
@@ -0,0 +1,14 @@
+// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <zmq/zmqutil.h>
+
+#include <logging.h>
+
+#include <zmq.h>
+
+void zmqError(const char* str)
+{
+ LogPrint(BCLog::ZMQ, "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno));
+}
diff --git a/src/zmq/zmqutil.h b/src/zmq/zmqutil.h
new file mode 100644
index 0000000000..4c1df5d6db
--- /dev/null
+++ b/src/zmq/zmqutil.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2014-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_ZMQ_ZMQUTIL_H
+#define BITCOIN_ZMQ_ZMQUTIL_H
+
+void zmqError(const char* str);
+
+#endif // BITCOIN_ZMQ_ZMQUTIL_H