aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am53
-rw-r--r--src/Makefile.bench.include3
-rw-r--r--src/Makefile.qt.include32
-rw-r--r--src/Makefile.qttest.include14
-rw-r--r--src/Makefile.test.include29
-rw-r--r--src/addrdb.cpp2
-rw-r--r--src/addrman.h55
-rw-r--r--src/amount.h1
-rw-r--r--src/arith_uint256.cpp4
-rw-r--r--src/attributes.h22
-rw-r--r--src/base58.cpp7
-rw-r--r--src/base58.h10
-rw-r--r--src/bench/base58.cpp2
-rw-r--r--src/bench/bech32.cpp2
-rw-r--r--src/bench/bench_bitcoin.cpp6
-rw-r--r--src/bench/block_assemble.cpp3
-rw-r--r--src/bench/ccoins_caching.cpp16
-rw-r--r--src/bench/checkblock.cpp4
-rw-r--r--src/bench/checkqueue.cpp2
-rw-r--r--src/bench/coin_selection.cpp38
-rw-r--r--src/bench/crypto_hash.cpp3
-rw-r--r--src/bench/duplicate_inputs.cpp100
-rw-r--r--src/bench/examples.cpp2
-rw-r--r--src/bench/prevector.cpp10
-rw-r--r--src/bitcoin-cli.cpp18
-rw-r--r--src/bitcoin-tx.cpp21
-rw-r--r--src/bitcoind.cpp18
-rw-r--r--src/blockencodings.cpp7
-rw-r--r--src/blockencodings.h9
-rw-r--r--src/blockfilter.cpp1
-rw-r--r--src/blockfilter.h5
-rw-r--r--src/chainparams.cpp71
-rw-r--r--src/chainparams.h8
-rw-r--r--src/chainparamsbase.cpp5
-rw-r--r--src/coins.cpp3
-rw-r--r--src/coins.h6
-rw-r--r--src/compat.h10
-rw-r--r--src/consensus/merkle.cpp2
-rw-r--r--src/consensus/tx_verify.cpp4
-rw-r--r--src/consensus/validation.h6
-rw-r--r--src/core_io.h18
-rw-r--r--src/core_read.cpp13
-rw-r--r--src/core_write.cpp8
-rw-r--r--src/crypto/siphash.cpp173
-rw-r--r--src/crypto/siphash.h47
-rw-r--r--src/dbwrapper.h4
-rw-r--r--src/dummywallet.cpp55
-rw-r--r--src/fs.cpp126
-rw-r--r--src/fs.h54
-rw-r--r--src/hash.cpp168
-rw-r--r--src/hash.h35
-rw-r--r--src/httprpc.cpp22
-rw-r--r--src/httpserver.cpp43
-rw-r--r--src/httpserver.h4
-rw-r--r--src/index/base.cpp4
-rw-r--r--src/index/txindex.cpp4
-rw-r--r--src/init.cpp179
-rw-r--r--src/init.h20
-rw-r--r--src/interfaces/README.md2
-rw-r--r--src/interfaces/chain.cpp44
-rw-r--r--src/interfaces/chain.h84
-rw-r--r--src/interfaces/handler.cpp2
-rw-r--r--src/interfaces/node.cpp45
-rw-r--r--src/interfaces/node.h6
-rw-r--r--src/interfaces/wallet.cpp147
-rw-r--r--src/interfaces/wallet.h10
-rw-r--r--src/key.cpp4
-rw-r--r--src/key.h6
-rw-r--r--src/key_io.cpp2
-rw-r--r--src/keystore.cpp2
-rw-r--r--src/logging.cpp2
-rw-r--r--src/logging.h55
-rw-r--r--src/merkleblock.cpp2
-rw-r--r--src/miner.cpp9
-rw-r--r--src/net.cpp516
-rw-r--r--src/net.h58
-rw-r--r--src/net_processing.cpp30
-rw-r--r--src/net_processing.h9
-rw-r--r--src/netaddress.cpp13
-rw-r--r--src/netaddress.h3
-rw-r--r--src/netbase.cpp44
-rw-r--r--src/noui.cpp9
-rw-r--r--src/noui.h12
-rw-r--r--src/outputtype.h3
-rw-r--r--src/policy/fees.cpp2
-rw-r--r--src/policy/fees.h6
-rw-r--r--src/policy/policy.cpp6
-rw-r--r--src/policy/rbf.cpp2
-rw-r--r--src/prevector.h22
-rw-r--r--src/primitives/block.cpp2
-rw-r--r--src/primitives/transaction.cpp4
-rw-r--r--src/primitives/transaction.h4
-rw-r--r--src/protocol.cpp4
-rw-r--r--src/qt/README.md4
-rw-r--r--src/qt/askpassphrasedialog.cpp22
-rw-r--r--src/qt/bantablemodel.cpp2
-rw-r--r--src/qt/bitcoin.cpp29
-rw-r--r--src/qt/bitcoinamountfield.cpp67
-rw-r--r--src/qt/bitcoinamountfield.h9
-rw-r--r--src/qt/bitcoingui.cpp106
-rw-r--r--src/qt/bitcoingui.h11
-rw-r--r--src/qt/callback.h30
-rw-r--r--src/qt/clientmodel.cpp7
-rw-r--r--src/qt/clientmodel.h1
-rw-r--r--src/qt/coincontroldialog.cpp5
-rw-r--r--src/qt/forms/debugwindow.ui68
-rw-r--r--src/qt/forms/sendcoinsdialog.ui41
-rw-r--r--src/qt/guiutil.cpp85
-rw-r--r--src/qt/guiutil.h3
-rw-r--r--src/qt/intro.cpp2
-rw-r--r--src/qt/macdockiconhandler.h21
-rw-r--r--src/qt/macdockiconhandler.mm100
-rw-r--r--src/qt/macnotificationhandler.h2
-rw-r--r--src/qt/macos_appnap.h24
-rw-r--r--src/qt/macos_appnap.mm71
-rw-r--r--src/qt/optionsdialog.cpp14
-rw-r--r--src/qt/paymentrequestplus.cpp3
-rw-r--r--src/qt/paymentserver.cpp301
-rw-r--r--src/qt/paymentserver.h44
-rw-r--r--src/qt/rpcconsole.cpp13
-rw-r--r--src/qt/sendcoinsdialog.cpp45
-rw-r--r--src/qt/sendcoinsdialog.h2
-rw-r--r--src/qt/sendcoinsentry.cpp10
-rw-r--r--src/qt/splashscreen.cpp6
-rw-r--r--src/qt/test/addressbooktests.cpp19
-rw-r--r--src/qt/test/compattests.cpp6
-rw-r--r--src/qt/test/paymentservertests.cpp4
-rw-r--r--src/qt/test/rpcnestedtests.cpp2
-rw-r--r--src/qt/test/test_main.cpp8
-rw-r--r--src/qt/test/util.cpp11
-rw-r--r--src/qt/test/util.h2
-rw-r--r--src/qt/test/wallettests.cpp24
-rw-r--r--src/qt/transactiondesc.cpp15
-rw-r--r--src/qt/transactiondesc.h2
-rw-r--r--src/qt/transactionrecord.cpp2
-rw-r--r--src/qt/transactionrecord.h2
-rw-r--r--src/qt/transactiontablemodel.cpp7
-rw-r--r--src/qt/transactionview.cpp6
-rw-r--r--src/qt/utilitydialog.cpp11
-rw-r--r--src/qt/walletmodel.cpp15
-rw-r--r--src/qt/walletmodel.h16
-rw-r--r--src/qt/walletmodeltransaction.cpp6
-rw-r--r--src/qt/walletmodeltransaction.h1
-rw-r--r--src/qt/walletview.cpp8
-rw-r--r--src/qt/winshutdownmonitor.cpp2
-rw-r--r--src/random.cpp4
-rw-r--r--src/rest.cpp47
-rw-r--r--src/rpc/blockchain.cpp358
-rw-r--r--src/rpc/client.cpp7
-rw-r--r--src/rpc/mining.cpp164
-rw-r--r--src/rpc/misc.cpp87
-rw-r--r--src/rpc/net.cpp168
-rw-r--r--src/rpc/protocol.cpp18
-rw-r--r--src/rpc/rawtransaction.cpp284
-rw-r--r--src/rpc/rawtransaction.h6
-rw-r--r--src/rpc/server.cpp41
-rw-r--r--src/rpc/server.h4
-rw-r--r--src/rpc/util.cpp100
-rw-r--r--src/rpc/util.h59
-rw-r--r--src/scheduler.cpp4
-rw-r--r--src/scheduler.h6
-rw-r--r--src/script/bitcoinconsensus.cpp2
-rw-r--r--src/script/descriptor.cpp214
-rw-r--r--src/script/descriptor.h21
-rw-r--r--src/script/ismine.cpp14
-rw-r--r--src/script/script.cpp2
-rw-r--r--src/script/script.h6
-rw-r--r--src/script/sigcache.cpp2
-rw-r--r--src/script/sign.cpp79
-rw-r--r--src/script/sign.h50
-rw-r--r--src/script/standard.cpp4
-rw-r--r--src/serialize.h20
-rw-r--r--src/sync.cpp2
-rw-r--r--src/test/addrman_tests.cpp4
-rw-r--r--src/test/allocator_tests.cpp4
-rw-r--r--src/test/amount_tests.cpp57
-rw-r--r--src/test/base32_tests.cpp2
-rw-r--r--src/test/base58_tests.cpp2
-rw-r--r--src/test/base64_tests.cpp2
-rw-r--r--src/test/bip32_tests.cpp4
-rw-r--r--src/test/blockencodings_tests.cpp45
-rw-r--r--src/test/blockfilter_tests.cpp11
-rw-r--r--src/test/bloom_tests.cpp4
-rw-r--r--src/test/checkqueue_tests.cpp18
-rw-r--r--src/test/coins_tests.cpp11
-rw-r--r--src/test/compress_tests.cpp2
-rw-r--r--src/test/crypto_tests.cpp4
-rw-r--r--src/test/dbwrapper_tests.cpp8
-rw-r--r--src/test/denialofservice_tests.cpp2
-rw-r--r--src/test/descriptor_tests.cpp54
-rw-r--r--src/test/fs_tests.cpp56
-rw-r--r--src/test/getarg_tests.cpp4
-rw-r--r--src/test/hash_tests.cpp3
-rw-r--r--src/test/key_io_tests.cpp2
-rw-r--r--src/test/key_properties.cpp4
-rw-r--r--src/test/key_tests.cpp4
-rw-r--r--src/test/mempool_tests.cpp2
-rw-r--r--src/test/miner_tests.cpp10
-rw-r--r--src/test/net_tests.cpp44
-rw-r--r--src/test/netbase_tests.cpp2
-rw-r--r--src/test/policyestimator_tests.cpp2
-rw-r--r--src/test/pow_tests.cpp4
-rw-r--r--src/test/rpc_tests.cpp7
-rw-r--r--src/test/script_tests.cpp12
-rw-r--r--src/test/serialize_tests.cpp4
-rw-r--r--src/test/sighash_tests.cpp4
-rw-r--r--src/test/skiplist_tests.cpp3
-rw-r--r--src/test/test_bitcoin.cpp9
-rw-r--r--src/test/test_bitcoin.h2
-rw-r--r--src/test/test_bitcoin_fuzzy.cpp2
-rw-r--r--src/test/transaction_tests.cpp2
-rw-r--r--src/test/txindex_tests.cpp4
-rw-r--r--src/test/txvalidationcache_tests.cpp2
-rw-r--r--src/test/uint256_tests.cpp8
-rw-r--r--src/test/util_tests.cpp52
-rw-r--r--src/threadsafety.h11
-rw-r--r--src/timedata.cpp4
-rw-r--r--src/torcontrol.cpp4
-rw-r--r--src/txdb.cpp2
-rw-r--r--src/txmempool.cpp50
-rw-r--r--src/txmempool.h27
-rw-r--r--src/ui_interface.cpp2
-rw-r--r--src/uint256.cpp4
-rw-r--r--src/undo.h3
-rw-r--r--src/univalue/gen/gen.cpp2
-rw-r--r--src/univalue/include/univalue.h68
-rw-r--r--src/univalue/lib/univalue.cpp16
-rw-r--r--src/univalue/lib/univalue_read.cpp12
-rw-r--r--src/univalue/lib/univalue_write.cpp18
-rw-r--r--src/univalue/test/unitester.cpp15
-rw-r--r--src/util/bytevectorhash.cpp18
-rw-r--r--src/util/bytevectorhash.h26
-rw-r--r--src/util/memory.h (renamed from src/utilmemory.h)4
-rw-r--r--src/util/moneystr.cpp (renamed from src/utilmoneystr.cpp)12
-rw-r--r--src/util/moneystr.h (renamed from src/utilmoneystr.h)17
-rw-r--r--src/util/strencodings.cpp (renamed from src/utilstrencodings.cpp)9
-rw-r--r--src/util/strencodings.h (renamed from src/utilstrencodings.h)40
-rw-r--r--src/util/system.cpp (renamed from src/util.cpp)110
-rw-r--r--src/util/system.h (renamed from src/util.h)43
-rw-r--r--src/util/time.cpp (renamed from src/utiltime.cpp)2
-rw-r--r--src/util/time.h (renamed from src/utiltime.h)6
-rw-r--r--src/validation.cpp65
-rw-r--r--src/validation.h10
-rw-r--r--src/validationinterface.cpp2
-rw-r--r--src/versionbits.cpp15
-rw-r--r--src/versionbits.h9
-rw-r--r--src/versionbitsinfo.cpp22
-rw-r--r--src/versionbitsinfo.h17
-rw-r--r--src/wallet/coincontrol.cpp2
-rw-r--r--src/wallet/coinselection.cpp4
-rw-r--r--src/wallet/coinselection.h2
-rw-r--r--src/wallet/crypter.cpp4
-rw-r--r--src/wallet/db.cpp121
-rw-r--r--src/wallet/db.h33
-rw-r--r--src/wallet/feebumper.cpp27
-rw-r--r--src/wallet/fees.cpp2
-rw-r--r--src/wallet/init.cpp86
-rw-r--r--src/wallet/rpcdump.cpp486
-rw-r--r--src/wallet/rpcwallet.cpp1215
-rw-r--r--src/wallet/rpcwallet.h2
-rw-r--r--src/wallet/test/coinselector_tests.cpp66
-rw-r--r--src/wallet/test/init_test_fixture.cpp44
-rw-r--r--src/wallet/test/init_test_fixture.h24
-rw-r--r--src/wallet/test/init_tests.cpp78
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp10
-rw-r--r--src/wallet/test/wallet_crypto_tests.cpp2
-rw-r--r--src/wallet/test/wallet_test_fixture.cpp3
-rw-r--r--src/wallet/test/wallet_test_fixture.h3
-rw-r--r--src/wallet/test/wallet_tests.cpp94
-rw-r--r--src/wallet/wallet.cpp280
-rw-r--r--src/wallet/wallet.h149
-rw-r--r--src/wallet/walletdb.cpp4
-rw-r--r--src/wallet/walletutil.cpp66
-rw-r--r--src/wallet/walletutil.h28
-rw-r--r--src/walletinitinterface.h21
-rw-r--r--src/warnings.cpp2
-rw-r--r--src/zmq/zmqabstractnotifier.cpp3
-rw-r--r--src/zmq/zmqabstractnotifier.h11
-rw-r--r--src/zmq/zmqnotificationinterface.cpp17
-rw-r--r--src/zmq/zmqpublishnotifier.cpp18
-rw-r--r--src/zmq/zmqpublishnotifier.h2
-rw-r--r--src/zmq/zmqrpc.cpp10
282 files changed, 6340 insertions, 3578 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 3701ee8f3c..09daaebd23 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -83,8 +83,11 @@ if BUILD_BITCOIND
bin_PROGRAMS += bitcoind
endif
-if BUILD_BITCOIN_UTILS
- bin_PROGRAMS += bitcoin-cli bitcoin-tx
+if BUILD_BITCOIN_CLI
+ bin_PROGRAMS += bitcoin-cli
+endif
+if BUILD_BITCOIN_TX
+ bin_PROGRAMS += bitcoin-tx
endif
.PHONY: FORCE check-symbols check-security
@@ -92,6 +95,7 @@ endif
BITCOIN_CORE_H = \
addrdb.h \
addrman.h \
+ attributes.h \
base58.h \
bech32.h \
bloom.h \
@@ -122,6 +126,7 @@ BITCOIN_CORE_H = \
index/txindex.h \
indirectmap.h \
init.h \
+ interfaces/chain.h \
interfaces/handler.h \
interfaces/node.h \
interfaces/wallet.h \
@@ -180,13 +185,15 @@ BITCOIN_CORE_H = \
txmempool.h \
ui_interface.h \
undo.h \
- util.h \
- utilmemory.h \
- utilmoneystr.h \
- utiltime.h \
+ util/bytevectorhash.h \
+ util/system.h \
+ util/memory.h \
+ util/moneystr.h \
+ util/time.h \
validation.h \
validationinterface.h \
versionbits.h \
+ versionbitsinfo.h \
walletinitinterface.h \
wallet/coincontrol.h \
wallet/crypter.h \
@@ -228,6 +235,9 @@ libbitcoin_server_a_SOURCES = \
httpserver.cpp \
index/base.cpp \
index/txindex.cpp \
+ interfaces/chain.cpp \
+ interfaces/handler.cpp \
+ interfaces/node.cpp \
init.cpp \
dbwrapper.cpp \
merkleblock.cpp \
@@ -260,6 +270,10 @@ libbitcoin_server_a_SOURCES = \
versionbits.cpp \
$(BITCOIN_CORE_H)
+if !ENABLE_WALLET
+libbitcoin_server_a_SOURCES += dummywallet.cpp
+endif
+
if ENABLE_ZMQ
libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS)
libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
@@ -311,7 +325,9 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/sha256.cpp \
crypto/sha256.h \
crypto/sha512.cpp \
- crypto/sha512.h
+ crypto/sha512.h \
+ crypto/siphash.cpp \
+ crypto/siphash.h
if USE_ASM
crypto_libbitcoin_crypto_base_a_SOURCES += crypto/sha256_sse4.cpp
@@ -367,8 +383,8 @@ libbitcoin_consensus_a_SOURCES = \
tinyformat.h \
uint256.cpp \
uint256.h \
- utilstrencodings.cpp \
- utilstrencodings.h \
+ util/strencodings.cpp \
+ util/strencodings.h \
version.h
# common: shared between bitcoind, and bitcoin-qt and non-server tools
@@ -394,6 +410,7 @@ libbitcoin_common_a_SOURCES = \
script/ismine.cpp \
script/sign.cpp \
script/standard.cpp \
+ versionbitsinfo.cpp \
warnings.cpp \
$(BITCOIN_CORE_H)
@@ -410,18 +427,17 @@ libbitcoin_util_a_SOURCES = \
compat/glibcxx_sanity.cpp \
compat/strnlen.cpp \
fs.cpp \
- interfaces/handler.cpp \
- interfaces/node.cpp \
logging.cpp \
random.cpp \
rpc/protocol.cpp \
support/cleanse.cpp \
sync.cpp \
threadinterrupt.cpp \
- util.cpp \
- utilmoneystr.cpp \
- utilstrencodings.cpp \
- utiltime.cpp \
+ util/bytevectorhash.cpp \
+ util/system.cpp \
+ util/moneystr.cpp \
+ util/strencodings.cpp \
+ util/time.cpp \
$(BITCOIN_CORE_H)
if GLIBC_BACK_COMPAT
@@ -452,6 +468,7 @@ endif
bitcoind_LDADD = \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_WALLET) \
+ $(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
@@ -463,7 +480,7 @@ bitcoind_LDADD = \
$(LIBMEMENV) \
$(LIBSECP256K1)
-bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
+bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
# bitcoin-cli binary #
bitcoin_cli_SOURCES = bitcoin-cli.cpp
@@ -481,7 +498,7 @@ bitcoin_cli_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO)
-bitcoin_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS)
+bitcoin_cli_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS)
#
# bitcoin-tx binary #
@@ -578,9 +595,11 @@ if HARDEN
$(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS)
endif
+if ENABLE_BIP70
%.pb.cc %.pb.h: %.proto
@test -f $(PROTOC)
$(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(<D) $<
+endif
if EMBEDDED_LEVELDB
include Makefile.leveldb.include
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 76177630ab..5e787ca222 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -18,6 +18,7 @@ bench_bench_bitcoin_SOURCES = \
bench/block_assemble.cpp \
bench/checkblock.cpp \
bench/checkqueue.cpp \
+ bench/duplicate_inputs.cpp \
bench/examples.cpp \
bench/rollingbloom.cpp \
bench/crypto_hash.cpp \
@@ -56,7 +57,7 @@ if ENABLE_WALLET
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
endif
-bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
+bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index c7a1963135..445849e3d8 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -122,7 +122,6 @@ QT_MOC_CPP = \
qt/moc_bitcoinamountfield.cpp \
qt/moc_bitcoingui.cpp \
qt/moc_bitcoinunits.cpp \
- qt/moc_callback.cpp \
qt/moc_clientmodel.cpp \
qt/moc_coincontroldialog.cpp \
qt/moc_coincontroltreewidget.cpp \
@@ -163,12 +162,12 @@ QT_MOC_CPP = \
BITCOIN_MM = \
qt/macdockiconhandler.mm \
- qt/macnotificationhandler.mm
+ qt/macnotificationhandler.mm \
+ qt/macos_appnap.mm
QT_MOC = \
qt/bitcoin.moc \
qt/bitcoinamountfield.moc \
- qt/callback.moc \
qt/intro.moc \
qt/overviewpage.moc \
qt/rpcconsole.moc
@@ -178,9 +177,15 @@ QT_QRC = qt/bitcoin.qrc
QT_QRC_LOCALE_CPP = qt/qrc_bitcoin_locale.cpp
QT_QRC_LOCALE = qt/bitcoin_locale.qrc
+if ENABLE_BIP70
PROTOBUF_CC = qt/paymentrequest.pb.cc
PROTOBUF_H = qt/paymentrequest.pb.h
PROTOBUF_PROTO = qt/paymentrequest.proto
+else
+PROTOBUF_CC =
+PROTOBUF_H =
+PROTOBUF_PROTO =
+endif
BITCOIN_QT_H = \
qt/addressbookpage.h \
@@ -191,7 +196,6 @@ BITCOIN_QT_H = \
qt/bitcoinamountfield.h \
qt/bitcoingui.h \
qt/bitcoinunits.h \
- qt/callback.h \
qt/clientmodel.h \
qt/coincontroldialog.h \
qt/coincontroltreewidget.h \
@@ -202,6 +206,7 @@ BITCOIN_QT_H = \
qt/intro.h \
qt/macdockiconhandler.h \
qt/macnotificationhandler.h \
+ qt/macos_appnap.h \
qt/modaloverlay.h \
qt/networkstyle.h \
qt/notificator.h \
@@ -330,7 +335,6 @@ BITCOIN_QT_WALLET_CPP = \
qt/editaddressdialog.cpp \
qt/openuridialog.cpp \
qt/overviewpage.cpp \
- qt/paymentrequestplus.cpp \
qt/paymentserver.cpp \
qt/receivecoinsdialog.cpp \
qt/receiverequestdialog.cpp \
@@ -349,13 +353,19 @@ BITCOIN_QT_WALLET_CPP = \
qt/walletmodeltransaction.cpp \
qt/walletview.cpp
+BITCOIN_QT_WALLET_BIP70_CPP = \
+ qt/paymentrequestplus.cpp
+
BITCOIN_QT_CPP = $(BITCOIN_QT_BASE_CPP)
if TARGET_WINDOWS
BITCOIN_QT_CPP += $(BITCOIN_QT_WINDOWS_CPP)
endif
if ENABLE_WALLET
BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_CPP)
-endif
+if ENABLE_BIP70
+BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_BIP70_CPP)
+endif # ENABLE_BIP70
+endif # ENABLE_WALLET
RES_IMAGES =
@@ -409,8 +419,16 @@ if ENABLE_ZMQ
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
- $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
+ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
+if ENABLE_BIP70
+qt_bitcoin_qt_LDADD += $(SSL_LIBS)
+else
+if TARGET_WINDOWS
+qt_bitcoin_qt_LDADD += $(SSL_LIBS)
+endif
+endif
+qt_bitcoin_qt_LDADD += $(CRYPTO_LIBS)
qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
qt_bitcoin_qt_LIBTOOLFLAGS = $(AM_LIBTOOLFLAGS) --tag CXX
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 4b14212b2e..db7873e8b7 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -13,9 +13,12 @@ TEST_QT_MOC_CPP = \
if ENABLE_WALLET
TEST_QT_MOC_CPP += \
qt/test/moc_addressbooktests.cpp \
- qt/test/moc_paymentservertests.cpp \
qt/test/moc_wallettests.cpp
-endif
+if ENABLE_BIP70
+TEST_QT_MOC_CPP += \
+ qt/test/moc_paymentservertests.cpp
+endif # ENABLE_BIP70
+endif # ENABLE_WALLET
TEST_QT_H = \
qt/test/addressbooktests.h \
@@ -48,10 +51,13 @@ qt_test_test_bitcoin_qt_SOURCES = \
if ENABLE_WALLET
qt_test_test_bitcoin_qt_SOURCES += \
qt/test/addressbooktests.cpp \
- qt/test/paymentservertests.cpp \
qt/test/wallettests.cpp \
wallet/test/wallet_test_fixture.cpp
-endif
+if ENABLE_BIP70
+qt_test_test_bitcoin_qt_SOURCES += \
+ qt/test/paymentservertests.cpp
+endif # ENABLE_BIP70
+endif # ENABLE_WALLET
nodist_qt_test_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index c4ee5f5fcc..a31852c94f 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -51,6 +51,7 @@ BITCOIN_TESTS =\
test/cuckoocache_tests.cpp \
test/denialofservice_tests.cpp \
test/descriptor_tests.cpp \
+ test/fs_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
test/key_io_tests.cpp \
@@ -110,11 +111,14 @@ BITCOIN_TESTS += \
wallet/test/psbt_wallet_tests.cpp \
wallet/test/wallet_tests.cpp \
wallet/test/wallet_crypto_tests.cpp \
- wallet/test/coinselector_tests.cpp
+ wallet/test/coinselector_tests.cpp \
+ wallet/test/init_tests.cpp
BITCOIN_TEST_SUITE += \
wallet/test/wallet_test_fixture.cpp \
- wallet/test/wallet_test_fixture.h
+ wallet/test/wallet_test_fixture.h \
+ wallet/test/init_test_fixture.cpp \
+ wallet/test/init_test_fixture.h
endif
test_test_bitcoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
@@ -128,7 +132,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
+test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
if ENABLE_ZMQ
@@ -161,11 +165,19 @@ nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
$(BITCOIN_TESTS): $(GENERATED_TEST_FILES)
-CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno $(GENERATED_TEST_FILES)
+CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno $(GENERATED_TEST_FILES) $(BITCOIN_TESTS:=.log)
CLEANFILES += $(CLEAN_BITCOIN_TEST)
+if TARGET_WINDOWS
bitcoin_test: $(TEST_BINARY)
+else
+if ENABLE_BENCH
+bitcoin_test: $(TEST_BINARY) $(BENCH_BINARY)
+else
+bitcoin_test: $(TEST_BINARY)
+endif
+endif
bitcoin_test_check: $(TEST_BINARY) FORCE
$(MAKE) check-TESTS TESTS=$^
@@ -174,10 +186,19 @@ bitcoin_test_clean : FORCE
rm -f $(CLEAN_BITCOIN_TEST) $(test_test_bitcoin_OBJECTS) $(TEST_BINARY)
check-local: $(BITCOIN_TESTS:.cpp=.cpp.test)
+if BUILD_BITCOIN_TX
@echo "Running test/util/bitcoin-util-test.py..."
$(PYTHON) $(top_builddir)/test/util/bitcoin-util-test.py
+endif
@echo "Running test/util/rpcauth-test.py..."
$(PYTHON) $(top_builddir)/test/util/rpcauth-test.py
+if TARGET_WINDOWS
+else
+if ENABLE_BENCH
+ @echo "Running bench/bench_bitcoin -evals=1 -scaling=0..."
+ $(BENCH_BINARY) -evals=1 -scaling=0 > /dev/null
+endif
+endif
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check
if EMBEDDED_UNIVALUE
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index 3eae2b5127..1590bce074 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -12,7 +12,7 @@
#include <random.h>
#include <streams.h>
#include <tinyformat.h>
-#include <util.h>
+#include <util/system.h>
namespace {
diff --git a/src/addrman.h b/src/addrman.h
index cf1949c28c..b97feb6f08 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -11,7 +11,7 @@
#include <random.h>
#include <sync.h>
#include <timedata.h>
-#include <util.h>
+#include <util/system.h>
#include <map>
#include <set>
@@ -187,36 +187,37 @@ public:
*/
class CAddrMan
{
-private:
+protected:
//! critical section to protect the inner data structures
mutable CCriticalSection cs;
+private:
//! last used nId
- int nIdCount;
+ int nIdCount GUARDED_BY(cs);
//! table with information about all nIds
- std::map<int, CAddrInfo> mapInfo;
+ std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
//! find an nId based on its network address
- std::map<CNetAddr, int> mapAddr;
+ std::map<CNetAddr, int> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
- std::vector<int> vRandom;
+ std::vector<int> vRandom GUARDED_BY(cs);
// number of "tried" entries
- int nTried;
+ int nTried GUARDED_BY(cs);
//! list of "tried" buckets
- int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE];
+ int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
//! number of (unique) "new" entries
- int nNew;
+ int nNew GUARDED_BY(cs);
//! list of "new" buckets
- int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE];
+ int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs);
//! last time Good was called (memory only)
- int64_t nLastGood;
+ int64_t nLastGood GUARDED_BY(cs);
//! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions.
std::set<int> m_tried_collisions;
@@ -229,58 +230,58 @@ protected:
FastRandomContext insecure_rand;
//! Find an entry.
- CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr);
+ CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! find an entry, creating it if necessary.
//! nTime and nServices of the found node are updated, if necessary.
- CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr);
+ CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Swap two elements in vRandom.
- void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
+ void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Move an entry from the "new" table(s) to the "tried" table
- void MakeTried(CAddrInfo& info, int nId);
+ void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Delete an entry. It must not be in tried, and have refcount 0.
- void Delete(int nId);
+ void Delete(int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Clear a position in a "new" table. This is the only place where entries are actually deleted.
- void ClearNew(int nUBucket, int nUBucketPos);
+ void ClearNew(int nUBucket, int nUBucketPos) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Mark an entry "good", possibly moving it from "new" to "tried".
- void Good_(const CService &addr, bool test_before_evict, int64_t time);
+ void Good_(const CService &addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Add an entry to the "new" table.
- bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty);
+ bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Mark an entry as attempted to connect.
- void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime);
+ void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
- CAddrInfo Select_(bool newOnly);
+ CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
- void ResolveCollisions_();
+ void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Return a random to-be-evicted tried table address.
- CAddrInfo SelectTriedCollision_();
+ CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic.
virtual int RandomInt(int nMax);
#ifdef DEBUG_ADDRMAN
//! Perform consistency check. Returns an error code or zero.
- int Check_();
+ int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
#endif
//! Select several addresses at once.
- void GetAddr_(std::vector<CAddress> &vAddr);
+ void GetAddr_(std::vector<CAddress> &vAddr) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Mark an entry as currently-connected-to.
- void Connected_(const CService &addr, int64_t nTime);
+ void Connected_(const CService &addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Update an entry's service bits.
- void SetServices_(const CService &addr, ServiceFlags nServices);
+ void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
public:
/**
diff --git a/src/amount.h b/src/amount.h
index 2bd367cba2..449fd1b15f 100644
--- a/src/amount.h
+++ b/src/amount.h
@@ -12,7 +12,6 @@
typedef int64_t CAmount;
static const CAmount COIN = 100000000;
-static const CAmount CENT = 1000000;
/** No amount larger than this (in satoshi) is valid.
*
diff --git a/src/arith_uint256.cpp b/src/arith_uint256.cpp
index 13fa176f8b..aa66d13102 100644
--- a/src/arith_uint256.cpp
+++ b/src/arith_uint256.cpp
@@ -6,7 +6,7 @@
#include <arith_uint256.h>
#include <uint256.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <crypto/common.h>
#include <stdio.h>
@@ -176,7 +176,7 @@ unsigned int base_uint<BITS>::bits() const
for (int pos = WIDTH - 1; pos >= 0; pos--) {
if (pn[pos]) {
for (int nbits = 31; nbits > 0; nbits--) {
- if (pn[pos] & 1 << nbits)
+ if (pn[pos] & 1U << nbits)
return 32 * pos + nbits + 1;
}
return 32 * pos + 1;
diff --git a/src/attributes.h b/src/attributes.h
new file mode 100644
index 0000000000..45099bd8b8
--- /dev/null
+++ b/src/attributes.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_ATTRIBUTES_H
+#define BITCOIN_ATTRIBUTES_H
+
+#if defined(__has_cpp_attribute)
+# if __has_cpp_attribute(nodiscard)
+# define NODISCARD [[nodiscard]]
+# endif
+#endif
+#ifndef NODISCARD
+# if defined(_MSC_VER) && _MSC_VER >= 1700
+# define NODISCARD _Check_return_
+# else
+# define NODISCARD __attribute__((warn_unused_result))
+# endif
+#endif
+
+#endif // BITCOIN_ATTRIBUTES_H
diff --git a/src/base58.cpp b/src/base58.cpp
index 7020c24055..e3d2853399 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -6,6 +6,7 @@
#include <hash.h>
#include <uint256.h>
+#include <util/strencodings.h>
#include <assert.h>
#include <string.h>
@@ -34,7 +35,7 @@ static const int8_t mapBase58[256] = {
bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
{
// Skip leading spaces.
- while (*psz && isspace(*psz))
+ while (*psz && IsSpace(*psz))
psz++;
// Skip and count leading '1's.
int zeroes = 0;
@@ -48,7 +49,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
std::vector<unsigned char> b256(size);
// Process the characters.
static_assert(sizeof(mapBase58)/sizeof(mapBase58[0]) == 256, "mapBase58.size() should be 256"); // guarantee not out of range
- while (*psz && !isspace(*psz)) {
+ while (*psz && !IsSpace(*psz)) {
// Decode base58 character
int carry = mapBase58[(uint8_t)*psz];
if (carry == -1) // Invalid b58 character
@@ -64,7 +65,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
psz++;
}
// Skip trailing spaces.
- while (isspace(*psz))
+ while (IsSpace(*psz))
psz++;
if (*psz != 0)
return false;
diff --git a/src/base58.h b/src/base58.h
index 9d3f90652e..d6e0299a1e 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -14,6 +14,8 @@
#ifndef BITCOIN_BASE58_H
#define BITCOIN_BASE58_H
+#include <attributes.h>
+
#include <string>
#include <vector>
@@ -33,13 +35,13 @@ std::string EncodeBase58(const std::vector<unsigned char>& vch);
* return true if decoding is successful.
* psz cannot be nullptr.
*/
-bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet);
/**
* Decode a base58-encoded string (str) into a byte vector (vchRet).
* return true if decoding is successful.
*/
-bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet);
/**
* Encode a byte vector into a base58-encoded string, including checksum
@@ -50,12 +52,12 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn);
* Decode a base58-encoded string (psz) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
-bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
/**
* Decode a base58-encoded string (str) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
-bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
+NODISCARD bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
#endif // BITCOIN_BASE58_H
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index a555376e40..e7702ec461 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -49,7 +49,7 @@ static void Base58Decode(benchmark::State& state)
const char* addr = "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem";
std::vector<unsigned char> vch;
while (state.KeepRunning()) {
- DecodeBase58(addr, vch);
+ (void) DecodeBase58(addr, vch);
}
}
diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp
index 8b80e17391..3c4b453a23 100644
--- a/src/bench/bech32.cpp
+++ b/src/bench/bech32.cpp
@@ -6,7 +6,7 @@
#include <validation.h>
#include <bech32.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <vector>
#include <string>
diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp
index d7b8083e7c..32faba86b4 100644
--- a/src/bench/bench_bitcoin.cpp
+++ b/src/bench/bench_bitcoin.cpp
@@ -7,8 +7,8 @@
#include <crypto/sha256.h>
#include <key.h>
#include <random.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <validation.h>
#include <memory>
@@ -82,7 +82,7 @@ int main(int argc, char** argv)
return EXIT_FAILURE;
}
- std::unique_ptr<benchmark::Printer> printer(new benchmark::ConsolePrinter());
+ std::unique_ptr<benchmark::Printer> printer = MakeUnique<benchmark::ConsolePrinter>();
std::string printer_arg = gArgs.GetArg("-printer", DEFAULT_BENCH_PRINTER);
if ("plot" == printer_arg) {
printer.reset(new benchmark::PlotlyPrinter(
diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp
index bc99b8cdcd..2def0b23e2 100644
--- a/src/bench/block_assemble.cpp
+++ b/src/bench/block_assemble.cpp
@@ -7,13 +7,14 @@
#include <coins.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
+#include <crypto/sha256.h>
#include <miner.h>
#include <policy/policy.h>
#include <pow.h>
#include <scheduler.h>
#include <txdb.h>
#include <txmempool.h>
-#include <utiltime.h>
+#include <util/time.h>
#include <validation.h>
#include <validationinterface.h>
diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp
index db303eeead..b8d82c0a89 100644
--- a/src/bench/ccoins_caching.cpp
+++ b/src/bench/ccoins_caching.cpp
@@ -12,8 +12,8 @@
// FIXME: Dedup with SetupDummyInputs in test/transaction_tests.cpp.
//
// Helper: create two dummy transactions, each with
-// two outputs. The first has 11 and 50 CENT outputs
-// paid to a TX_PUBKEY, the second 21 and 22 CENT outputs
+// two outputs. The first has 11 and 50 COIN outputs
+// paid to a TX_PUBKEY, the second 21 and 22 COIN outputs
// paid to a TX_PUBKEYHASH.
//
static std::vector<CMutableTransaction>
@@ -31,16 +31,16 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
// Create some dummy input transactions
dummyTransactions[0].vout.resize(2);
- dummyTransactions[0].vout[0].nValue = 11 * CENT;
+ dummyTransactions[0].vout[0].nValue = 11 * COIN;
dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
- dummyTransactions[0].vout[1].nValue = 50 * CENT;
+ dummyTransactions[0].vout[1].nValue = 50 * COIN;
dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
AddCoins(coinsRet, dummyTransactions[0], 0);
dummyTransactions[1].vout.resize(2);
- dummyTransactions[1].vout[0].nValue = 21 * CENT;
+ dummyTransactions[1].vout[0].nValue = 21 * COIN;
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
- dummyTransactions[1].vout[1].nValue = 22 * CENT;
+ dummyTransactions[1].vout[1].nValue = 22 * COIN;
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
AddCoins(coinsRet, dummyTransactions[1], 0);
@@ -72,7 +72,7 @@ static void CCoinsCaching(benchmark::State& state)
t1.vin[2].prevout.n = 1;
t1.vin[2].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
t1.vout.resize(2);
- t1.vout[0].nValue = 90 * CENT;
+ t1.vout[0].nValue = 90 * COIN;
t1.vout[0].scriptPubKey << OP_1;
// Benchmark.
@@ -80,7 +80,7 @@ static void CCoinsCaching(benchmark::State& state)
bool success = AreInputsStandard(t1, coins);
assert(success);
CAmount value = coins.GetValueIn(t1);
- assert(value == (50 + 21 + 22) * CENT);
+ assert(value == (50 + 21 + 22) * COIN);
}
}
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index 6f03581c4b..e325333c01 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -20,7 +20,7 @@ namespace block_bench {
static void DeserializeBlockTest(benchmark::State& state)
{
CDataStream stream((const char*)block_bench::block413567,
- (const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
+ (const char*)block_bench::block413567 + sizeof(block_bench::block413567),
SER_NETWORK, PROTOCOL_VERSION);
char a = '\0';
stream.write(&a, 1); // Prevent compaction
@@ -36,7 +36,7 @@ static void DeserializeBlockTest(benchmark::State& state)
static void DeserializeAndCheckBlockTest(benchmark::State& state)
{
CDataStream stream((const char*)block_bench::block413567,
- (const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
+ (const char*)block_bench::block413567 + sizeof(block_bench::block413567),
SER_NETWORK, PROTOCOL_VERSION);
char a = '\0';
stream.write(&a, 1); // Prevent compaction
diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp
index 79689f6e0b..6ab542067a 100644
--- a/src/bench/checkqueue.cpp
+++ b/src/bench/checkqueue.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <util.h>
+#include <util/system.h>
#include <validation.h>
#include <checkqueue.h>
#include <prevector.h>
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 0a6f5d85ea..74641191a1 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -3,25 +3,20 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
-#include <wallet/wallet.h>
+#include <interfaces/chain.h>
#include <wallet/coinselection.h>
+#include <wallet/wallet.h>
#include <set>
-static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<OutputGroup>& groups)
+static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<std::unique_ptr<CWalletTx>>& wtxs)
{
- int nInput = 0;
-
static int nextLockTime = 0;
CMutableTransaction tx;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
- tx.vout.resize(nInput + 1);
- tx.vout[nInput].nValue = nValue;
- CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx)));
-
- int nAge = 6 * 24;
- COutput output(wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
- groups.emplace_back(output.GetInputCoin(), 6, false, 0, 0);
+ tx.vout.resize(1);
+ tx.vout[0].nValue = nValue;
+ wtxs.push_back(MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx))));
}
// Simple benchmark for wallet coin selection. Note that it maybe be necessary
@@ -33,15 +28,23 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<Ou
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
static void CoinSelection(benchmark::State& state)
{
- const CWallet wallet("dummy", WalletDatabase::CreateDummy());
+ auto chain = interfaces::MakeChain();
+ const CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
+ std::vector<std::unique_ptr<CWalletTx>> wtxs;
LOCK(wallet.cs_wallet);
// Add coins.
- std::vector<OutputGroup> groups;
for (int i = 0; i < 1000; ++i) {
- addCoin(1000 * COIN, wallet, groups);
+ addCoin(1000 * COIN, wallet, wtxs);
+ }
+ addCoin(3 * COIN, wallet, wtxs);
+
+ // Create groups
+ std::vector<OutputGroup> groups;
+ for (const auto& wtx : wtxs) {
+ COutput output(wtx.get(), 0 /* iIn */, 6 * 24 /* nDepthIn */, true /* spendable */, true /* solvable */, true /* safe */);
+ groups.emplace_back(output.GetInputCoin(), 6, false, 0, 0);
}
- addCoin(3 * COIN, wallet, groups);
const CoinEligibilityFilter filter_standard(1, 6, 0);
const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0);
@@ -57,7 +60,8 @@ static void CoinSelection(benchmark::State& state)
}
typedef std::set<CInputCoin> CoinSet;
-static const CWallet testWallet("dummy", WalletDatabase::CreateDummy());
+static auto testChain = interfaces::MakeChain();
+static const CWallet testWallet(*testChain, WalletLocation(), WalletDatabase::CreateDummy());
std::vector<std::unique_ptr<CWalletTx>> wtxn;
// Copied from src/wallet/test/coinselector_tests.cpp
@@ -66,7 +70,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup>
CMutableTransaction tx;
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
- std::unique_ptr<CWalletTx> wtx(new CWalletTx(&testWallet, MakeTransactionRef(std::move(tx))));
+ std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
set.emplace_back(COutput(wtx.get(), nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0);
wtxn.emplace_back(std::move(wtx));
}
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 5b0cf27e04..dc0b054420 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -9,11 +9,12 @@
#include <hash.h>
#include <random.h>
#include <uint256.h>
-#include <utiltime.h>
+#include <util/time.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
#include <crypto/sha512.h>
+#include <crypto/siphash.h>
/* Number of bytes to hash per iteration */
static const uint64_t BUFFER_SIZE = 1000*1000;
diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp
new file mode 100644
index 0000000000..e0854e2c62
--- /dev/null
+++ b/src/bench/duplicate_inputs.cpp
@@ -0,0 +1,100 @@
+// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <bench/bench.h>
+#include <chainparams.h>
+#include <coins.h>
+#include <consensus/merkle.h>
+#include <consensus/validation.h>
+#include <miner.h>
+#include <policy/policy.h>
+#include <pow.h>
+#include <scheduler.h>
+#include <txdb.h>
+#include <txmempool.h>
+#include <util/time.h>
+#include <validation.h>
+#include <validationinterface.h>
+
+#include <boost/thread.hpp>
+
+#include <list>
+#include <vector>
+
+
+static void DuplicateInputs(benchmark::State& state)
+{
+ const CScript SCRIPT_PUB{CScript(OP_TRUE)};
+
+ // Switch to regtest so we can mine faster
+ // Also segwit is active, so we can include witness transactions
+ SelectParams(CBaseChainParams::REGTEST);
+
+ InitScriptExecutionCache();
+
+ boost::thread_group thread_group;
+ CScheduler scheduler;
+ const CChainParams& chainparams = Params();
+ {
+ ::pblocktree.reset(new CBlockTreeDB(1 << 20, true));
+ ::pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
+ ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
+
+ thread_group.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
+ GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+ LoadGenesisBlock(chainparams);
+ CValidationState cvstate;
+ ActivateBestChain(cvstate, chainparams);
+ assert(::chainActive.Tip() != nullptr);
+ const bool witness_enabled{IsWitnessEnabled(::chainActive.Tip(), chainparams.GetConsensus())};
+ assert(witness_enabled);
+ }
+
+ CBlock block{};
+ CMutableTransaction coinbaseTx{};
+ CMutableTransaction naughtyTx{};
+
+ CBlockIndex* pindexPrev = ::chainActive.Tip();
+ assert(pindexPrev != nullptr);
+ block.nBits = GetNextWorkRequired(pindexPrev, &block, chainparams.GetConsensus());
+ block.nNonce = 0;
+ auto nHeight = pindexPrev->nHeight + 1;
+
+ // Make a coinbase TX
+ coinbaseTx.vin.resize(1);
+ coinbaseTx.vin[0].prevout.SetNull();
+ coinbaseTx.vout.resize(1);
+ coinbaseTx.vout[0].scriptPubKey = SCRIPT_PUB;
+ coinbaseTx.vout[0].nValue = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
+ coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
+
+
+ naughtyTx.vout.resize(1);
+ naughtyTx.vout[0].nValue = 0;
+ naughtyTx.vout[0].scriptPubKey = SCRIPT_PUB;
+
+ uint64_t n_inputs = (((MAX_BLOCK_SERIALIZED_SIZE / WITNESS_SCALE_FACTOR) - (CTransaction(coinbaseTx).GetTotalSize() + CTransaction(naughtyTx).GetTotalSize())) / 41) - 100;
+ for (uint64_t x = 0; x < (n_inputs - 1); ++x) {
+ naughtyTx.vin.emplace_back(GetRandHash(), 0, CScript(), 0);
+ }
+ naughtyTx.vin.emplace_back(naughtyTx.vin.back());
+
+ block.vtx.push_back(MakeTransactionRef(std::move(coinbaseTx)));
+ block.vtx.push_back(MakeTransactionRef(std::move(naughtyTx)));
+
+ block.hashMerkleRoot = BlockMerkleRoot(block);
+
+ while (state.KeepRunning()) {
+ CValidationState cvstate{};
+ assert(!CheckBlock(block, cvstate, chainparams.GetConsensus(), false, false));
+ assert(cvstate.GetRejectReason() == "bad-txns-inputs-duplicate");
+ }
+
+ thread_group.interrupt_all();
+ thread_group.join_all();
+ GetMainSignals().FlushBackgroundCallbacks();
+ GetMainSignals().UnregisterBackgroundSignalScheduler();
+}
+
+BENCHMARK(DuplicateInputs, 10);
diff --git a/src/bench/examples.cpp b/src/bench/examples.cpp
index 6d95e05ef6..e7ddd5a938 100644
--- a/src/bench/examples.cpp
+++ b/src/bench/examples.cpp
@@ -4,7 +4,7 @@
#include <bench/bench.h>
#include <validation.h>
-#include <utiltime.h>
+#include <util/time.h>
// Sanity test: this should loop ten times, and
// min/max/average should be close to 100ms.
diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp
index 8cc404b9e2..00e5d7e7a0 100644
--- a/src/bench/prevector.cpp
+++ b/src/bench/prevector.cpp
@@ -2,13 +2,21 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <compat.h>
#include <prevector.h>
#include <serialize.h>
#include <streams.h>
+#include <type_traits>
#include <bench/bench.h>
+// GCC 4.8 is missing some C++11 type_traits,
+// https://www.gnu.org/software/gcc/gcc-5/changes.html
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
+#define IS_TRIVIALLY_CONSTRUCTIBLE std::has_trivial_default_constructor
+#else
+#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_default_constructible
+#endif
+
struct nontrivial_t {
int x;
nontrivial_t() :x(-1) {}
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 89149de4bb..262458a684 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -12,11 +12,12 @@
#include <fs.h>
#include <rpc/client.h>
#include <rpc/protocol.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <memory>
#include <stdio.h>
+#include <tuple>
#include <event2/buffer.h>
#include <event2/keyvalq_struct.h>
@@ -46,12 +47,12 @@ static void SetupCliArgs()
gArgs.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpccookiefile=<loc>", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)"), false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-rpcwait", "Wait for RPC server to start", false, OptionsCategory::OPTIONS);
- gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)", false, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-stdinrpcpass", "Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS);
@@ -139,11 +140,6 @@ static int AppInitRPC(int argc, char* argv[])
fprintf(stderr, "Error: %s\n", e.what());
return EXIT_FAILURE;
}
- if (gArgs.GetBoolArg("-rpcssl", false))
- {
- fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
- return EXIT_FAILURE;
- }
return CONTINUE_EXECUTION;
}
@@ -516,6 +512,10 @@ static int CommandLineRPC(int argc, char *argv[])
int main(int argc, char* argv[])
{
+#ifdef WIN32
+ util::WinCmdLineArgs winArgs;
+ std::tie(argc, argv) = winArgs.get();
+#endif
SetupEnvironment();
if (!SetupNetworking()) {
fprintf(stderr, "Error: Initializing networking failed\n");
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 9ee3e8eee7..bc91ca3641 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -18,9 +18,9 @@
#include <script/script.h>
#include <script/sign.h>
#include <univalue.h>
-#include <util.h>
-#include <utilmoneystr.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/moneystr.h>
+#include <util/strencodings.h>
#include <memory>
#include <stdio.h>
@@ -240,10 +240,10 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
throw std::runtime_error("TX input missing separator");
// extract and validate TXID
- std::string strTxid = vStrInputParts[0];
- if ((strTxid.size() != 64) || !IsHex(strTxid))
+ uint256 txid;
+ if (!ParseHashStr(vStrInputParts[0], txid)) {
throw std::runtime_error("invalid TX input txid");
- uint256 txid(uint256S(strTxid));
+ }
static const unsigned int minTxOutSz = 9;
static const unsigned int maxVout = MAX_BLOCK_WEIGHT / (WITNESS_SCALE_FACTOR * minTxOutSz);
@@ -255,7 +255,7 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
throw std::runtime_error("invalid TX input vout '" + strVout + "'");
// extract the optional sequence number
- uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max();
+ uint32_t nSequenceIn = CTxIn::SEQUENCE_FINAL;
if (vStrInputParts.size() > 2)
nSequenceIn = std::stoul(vStrInputParts[2]);
@@ -356,7 +356,7 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
if (vStrInputParts.size() < numkeys + 3)
throw std::runtime_error("incorrect number of multisig pubkeys");
- if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 || numkeys < required)
+ if (required < 1 || required > MAX_PUBKEYS_PER_MULTISIG || numkeys < 1 || numkeys > MAX_PUBKEYS_PER_MULTISIG || numkeys < required)
throw std::runtime_error("multisig parameter mismatch. Required " \
+ std::to_string(required) + " of " + std::to_string(numkeys) + "signatures.");
@@ -590,7 +590,10 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
if (!prevOut.checkObject(types))
throw std::runtime_error("prevtxs internal object typecheck fail");
- uint256 txid = ParseHashStr(prevOut["txid"].get_str(), "txid");
+ uint256 txid;
+ if (!ParseHashStr(prevOut["txid"].get_str(), txid)) {
+ throw std::runtime_error("txid must be hexadecimal string (not '" + prevOut["txid"].get_str() + "')");
+ }
const int nOut = prevOut["vout"].get_int();
if (nOut < 0)
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index bf04d95b50..dde75c1b12 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -11,14 +11,15 @@
#include <clientversion.h>
#include <compat.h>
#include <fs.h>
+#include <interfaces/chain.h>
#include <rpc/server.h>
#include <init.h>
#include <noui.h>
#include <shutdown.h>
-#include <util.h>
+#include <util/system.h>
#include <httpserver.h>
#include <httprpc.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <walletinitinterface.h>
#include <stdio.h>
@@ -58,6 +59,9 @@ static void WaitForShutdown()
//
static bool AppInit(int argc, char* argv[])
{
+ InitInterfaces interfaces;
+ interfaces.chain = interfaces::MakeChain();
+
bool fRet = false;
//
@@ -77,7 +81,7 @@ static bool AppInit(int argc, char* argv[])
if (gArgs.IsArgSet("-version"))
{
- strUsage += FormatParagraph(LicenseInfo());
+ strUsage += FormatParagraph(LicenseInfo()) + "\n";
}
else
{
@@ -164,7 +168,7 @@ static bool AppInit(int argc, char* argv[])
// If locking the data directory failed, exit immediately
return false;
}
- fRet = AppInitMain();
+ fRet = AppInitMain(interfaces);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()");
@@ -178,13 +182,17 @@ static bool AppInit(int argc, char* argv[])
} else {
WaitForShutdown();
}
- Shutdown();
+ Shutdown(interfaces);
return fRet;
}
int main(int argc, char* argv[])
{
+#ifdef WIN32
+ util::WinCmdLineArgs winArgs;
+ std::tie(argc, argv) = winArgs.get();
+#endif
SetupEnvironment();
// Connect bitcoind signal handlers
diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
index b17ff3507d..10f51931f0 100644
--- a/src/blockencodings.cpp
+++ b/src/blockencodings.cpp
@@ -6,12 +6,13 @@
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <chainparams.h>
-#include <hash.h>
+#include <crypto/sha256.h>
+#include <crypto/siphash.h>
#include <random.h>
#include <streams.h>
#include <txmempool.h>
#include <validation.h>
-#include <util.h>
+#include <util/system.h>
#include <unordered_map>
@@ -162,7 +163,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
break;
}
- LogPrint(BCLog::CMPCTBLOCK, "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of size %lu\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock, SER_NETWORK, PROTOCOL_VERSION));
+ LogPrint(BCLog::CMPCTBLOCK, "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of size %lu\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock, PROTOCOL_VERSION));
return READ_STATUS_OK;
}
diff --git a/src/blockencodings.h b/src/blockencodings.h
index fad1f56f54..0c2b83ebcf 100644
--- a/src/blockencodings.h
+++ b/src/blockencodings.h
@@ -52,12 +52,12 @@ public:
}
}
- uint16_t offset = 0;
+ int32_t offset = 0;
for (size_t j = 0; j < indexes.size(); j++) {
- if (uint64_t(indexes[j]) + uint64_t(offset) > std::numeric_limits<uint16_t>::max())
+ if (int32_t(indexes[j]) + offset > std::numeric_limits<uint16_t>::max())
throw std::ios_base::failure("indexes overflowed 16 bits");
indexes[j] = indexes[j] + offset;
- offset = indexes[j] + 1;
+ offset = int32_t(indexes[j]) + 1;
}
} else {
for (size_t i = 0; i < indexes.size(); i++) {
@@ -186,6 +186,9 @@ public:
READWRITE(prefilledtxn);
+ if (BlockTxCount() > std::numeric_limits<uint16_t>::max())
+ throw std::ios_base::failure("indexes overflowed 16 bits");
+
if (ser_action.ForRead())
FillShortTxIDSelector();
}
diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp
index 91623fe70a..163e2a52ef 100644
--- a/src/blockfilter.cpp
+++ b/src/blockfilter.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <blockfilter.h>
+#include <crypto/siphash.h>
#include <hash.h>
#include <primitives/transaction.h>
#include <script/script.h>
diff --git a/src/blockfilter.h b/src/blockfilter.h
index 46833ac0be..871be11769 100644
--- a/src/blockfilter.h
+++ b/src/blockfilter.h
@@ -5,14 +5,15 @@
#ifndef BITCOIN_BLOCKFILTER_H
#define BITCOIN_BLOCKFILTER_H
-#include <set>
#include <stdint.h>
+#include <unordered_set>
#include <vector>
#include <primitives/block.h>
#include <serialize.h>
#include <uint256.h>
#include <undo.h>
+#include <util/bytevectorhash.h>
/**
* This implements a Golomb-coded set as defined in BIP 158. It is a
@@ -22,7 +23,7 @@ class GCSFilter
{
public:
typedef std::vector<unsigned char> Element;
- typedef std::set<Element> ElementSet;
+ typedef std::unordered_set<Element, ByteVectorHash> ElementSet;
private:
uint64_t m_siphash_k0;
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index b80cc2c259..4ce1b53880 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -8,11 +8,15 @@
#include <chainparamsseeds.h>
#include <consensus/merkle.h>
#include <tinyformat.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
+#include <versionbitsinfo.h>
#include <assert.h>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/split.hpp>
+
static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
{
CMutableTransaction txNew;
@@ -52,12 +56,6 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits
return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
}
-void CChainParams::UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
-{
- consensus.vDeployments[d].nStartTime = nStartTime;
- consensus.vDeployments[d].nTimeout = nTimeout;
-}
-
/**
* Main network
*/
@@ -270,7 +268,7 @@ public:
*/
class CRegTestParams : public CChainParams {
public:
- CRegTestParams() {
+ explicit CRegTestParams(const ArgsManager& args) {
strNetworkID = "regtest";
consensus.nSubsidyHalvingInterval = 150;
consensus.BIP16Exception = uint256();
@@ -308,6 +306,8 @@ public:
nDefaultPort = 18444;
nPruneAfterHeight = 1000;
+ UpdateVersionBitsParametersFromArgs(args);
+
genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"));
@@ -343,23 +343,65 @@ public:
/* enable fallback fee on regtest */
m_fallback_fee_enabled = true;
}
+
+ /**
+ * Allows modifying the Version Bits regtest parameters.
+ */
+ void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
+ {
+ consensus.vDeployments[d].nStartTime = nStartTime;
+ consensus.vDeployments[d].nTimeout = nTimeout;
+ }
+ void UpdateVersionBitsParametersFromArgs(const ArgsManager& args);
};
-static std::unique_ptr<CChainParams> globalChainParams;
+void CRegTestParams::UpdateVersionBitsParametersFromArgs(const ArgsManager& args)
+{
+ if (!args.IsArgSet("-vbparams")) return;
+
+ for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
+ std::vector<std::string> vDeploymentParams;
+ boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
+ if (vDeploymentParams.size() != 3) {
+ throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end");
+ }
+ int64_t nStartTime, nTimeout;
+ if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
+ throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
+ }
+ if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
+ throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
+ }
+ bool found = false;
+ for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
+ if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) {
+ UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout);
+ found = true;
+ LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout);
+ break;
+ }
+ }
+ if (!found) {
+ throw std::runtime_error(strprintf("Invalid deployment (%s)", vDeploymentParams[0]));
+ }
+ }
+}
+
+static std::unique_ptr<const CChainParams> globalChainParams;
const CChainParams &Params() {
assert(globalChainParams);
return *globalChainParams;
}
-std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain)
+std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain)
{
if (chain == CBaseChainParams::MAIN)
return std::unique_ptr<CChainParams>(new CMainParams());
else if (chain == CBaseChainParams::TESTNET)
return std::unique_ptr<CChainParams>(new CTestNetParams());
else if (chain == CBaseChainParams::REGTEST)
- return std::unique_ptr<CChainParams>(new CRegTestParams());
+ return std::unique_ptr<CChainParams>(new CRegTestParams(gArgs));
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
@@ -368,8 +410,3 @@ void SelectParams(const std::string& network)
SelectBaseParams(network);
globalChainParams = CreateChainParams(network);
}
-
-void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout)
-{
- globalChainParams->UpdateVersionBitsParameters(d, nStartTime, nTimeout);
-}
diff --git a/src/chainparams.h b/src/chainparams.h
index 722e52ff40..19818b40af 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -80,7 +80,6 @@ public:
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
- void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
protected:
CChainParams() {}
@@ -107,7 +106,7 @@ protected:
* @returns a CChainParams* of the chosen chain.
* @throws a std::runtime_error if the chain is not supported.
*/
-std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain);
+std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain);
/**
* Return the currently selected parameters. This won't change after app
@@ -121,9 +120,4 @@ const CChainParams &Params();
*/
void SelectParams(const std::string& chain);
-/**
- * Allows modifying the Version Bits regtest parameters.
- */
-void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
-
#endif // BITCOIN_CHAINPARAMS_H
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index e9e8ce03b4..f0559a319a 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -6,8 +6,8 @@
#include <chainparamsbase.h>
#include <tinyformat.h>
-#include <util.h>
-#include <utilmemory.h>
+#include <util/system.h>
+#include <util/memory.h>
#include <assert.h>
@@ -20,6 +20,7 @@ void SetupChainParamsBaseOptions()
gArgs.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development.", true, OptionsCategory::CHAINPARAMS);
gArgs.AddArg("-testnet", "Use the test chain", false, OptionsCategory::CHAINPARAMS);
+ gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", true, OptionsCategory::CHAINPARAMS);
}
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
diff --git a/src/coins.cpp b/src/coins.cpp
index da1036acfb..3ef9e0463c 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -6,6 +6,7 @@
#include <consensus/consensus.h>
#include <random.h>
+#include <version.h>
bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
@@ -244,7 +245,7 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
return true;
}
-static const size_t MIN_TRANSACTION_OUTPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut(), SER_NETWORK, PROTOCOL_VERSION);
+static const size_t MIN_TRANSACTION_OUTPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut(), PROTOCOL_VERSION);
static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_OUTPUT_WEIGHT;
const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
diff --git a/src/coins.h b/src/coins.h
index 41a422f485..94493453f0 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -9,7 +9,7 @@
#include <primitives/transaction.h>
#include <compressor.h>
#include <core_memusage.h>
-#include <hash.h>
+#include <crypto/siphash.h>
#include <memusage.h>
#include <serialize.h>
#include <uint256.h>
@@ -285,8 +285,8 @@ public:
* Note that lightweight clients may not know anything besides the hash of previous transactions,
* so may not be able to calculate this.
*
- * @param[in] tx transaction for which we are checking input total
- * @return Sum of value of all inputs (scriptSigs)
+ * @param[in] tx transaction for which we are checking input total
+ * @return Sum of value of all inputs (scriptSigs)
*/
CAmount GetValueIn(const CTransaction& tx) const;
diff --git a/src/compat.h b/src/compat.h
index d228611160..049579c365 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -10,16 +10,6 @@
#include <config/bitcoin-config.h>
#endif
-#include <type_traits>
-
-// GCC 4.8 is missing some C++11 type_traits,
-// https://www.gnu.org/software/gcc/gcc-5/changes.html
-#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
-#define IS_TRIVIALLY_CONSTRUCTIBLE std::has_trivial_default_constructor
-#else
-#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_default_constructible
-#endif
-
#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp
index 2a87a936b1..b47d9774ca 100644
--- a/src/consensus/merkle.cpp
+++ b/src/consensus/merkle.cpp
@@ -4,7 +4,7 @@
#include <consensus/merkle.h>
#include <hash.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
/* WARNING! If you're reading this because you're learning about crypto
and/or designing a new system that will use merkle trees, keep in mind
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index be73d0a2f9..b17a8bb31d 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -12,7 +12,7 @@
// TODO remove the following dependencies
#include <chain.h>
#include <coins.h>
-#include <utilmoneystr.h>
+#include <util/moneystr.h>
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
@@ -164,7 +164,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
if (tx.vout.empty())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
- if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
+ if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
// Check for negative or overflow output values
diff --git a/src/consensus/validation.h b/src/consensus/validation.h
index 008eda69b2..f2e2c3585a 100644
--- a/src/consensus/validation.h
+++ b/src/consensus/validation.h
@@ -95,16 +95,16 @@ public:
// weight = (stripped_size * 3) + total_size.
static inline int64_t GetTransactionWeight(const CTransaction& tx)
{
- return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
+ return ::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, PROTOCOL_VERSION);
}
static inline int64_t GetBlockWeight(const CBlock& block)
{
- return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION);
+ return ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, PROTOCOL_VERSION);
}
static inline int64_t GetTransactionInputWeight(const CTxIn& txin)
{
// scriptWitness size is added here because witnesses and txins are split up in segwit serialization.
- return ::GetSerializeSize(txin, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txin, SER_NETWORK, PROTOCOL_VERSION) + ::GetSerializeSize(txin.scriptWitness.stack, SER_NETWORK, PROTOCOL_VERSION);
+ return ::GetSerializeSize(txin, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txin, PROTOCOL_VERSION) + ::GetSerializeSize(txin.scriptWitness.stack, PROTOCOL_VERSION);
}
#endif // BITCOIN_CONSENSUS_VALIDATION_H
diff --git a/src/core_io.h b/src/core_io.h
index d53a45c0cb..6f87161f46 100644
--- a/src/core_io.h
+++ b/src/core_io.h
@@ -6,6 +6,7 @@
#define BITCOIN_CORE_IO_H
#include <amount.h>
+#include <attributes.h>
#include <string>
#include <vector>
@@ -22,12 +23,21 @@ class UniValue;
// core_read.cpp
CScript ParseScript(const std::string& s);
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
-bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness = false, bool try_witness = true);
-bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
+NODISCARD bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness = false, bool try_witness = true);
+NODISCARD bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);
-uint256 ParseHashStr(const std::string&, const std::string& strName);
+
+/**
+ * Parse a hex string into 256 bits
+ * @param[in] strHex a hex-formatted, 64-character string
+ * @param[out] result the result of the parasing
+ * @returns true if successful, false if not
+ *
+ * @see ParseHashV for an RPC-oriented version of this
+ */
+bool ParseHashStr(const std::string& strHex, uint256& result);
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
-bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error);
+NODISCARD bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error);
int ParseSighashString(const UniValue& sighash);
// core_write.cpp
diff --git a/src/core_read.cpp b/src/core_read.cpp
index b02016c014..3b82b2853c 100644
--- a/src/core_read.cpp
+++ b/src/core_read.cpp
@@ -11,8 +11,8 @@
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <version.h>
#include <boost/algorithm/string/classification.hpp>
@@ -193,14 +193,13 @@ bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx,
return true;
}
-uint256 ParseHashStr(const std::string& strHex, const std::string& strName)
+bool ParseHashStr(const std::string& strHex, uint256& result)
{
- if (!IsHex(strHex)) // Note: IsHex("") is false
- throw std::runtime_error(strName + " must be hexadecimal string (not '" + strHex + "')");
+ if ((strHex.size() != 64) || !IsHex(strHex))
+ return false;
- uint256 result;
result.SetHex(strHex);
- return result;
+ return true;
}
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName)
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 55dcb1661d..765a170307 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -12,9 +12,9 @@
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
-#include <util.h>
-#include <utilmoneystr.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/moneystr.h>
+#include <util/strencodings.h>
UniValue ValueFromAmount(const CAmount& amount)
{
@@ -181,7 +181,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
entry.pushKV("txid", tx.GetHash().GetHex());
entry.pushKV("hash", tx.GetWitnessHash().GetHex());
entry.pushKV("version", tx.nVersion);
- entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION));
+ entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION));
entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
entry.pushKV("weight", GetTransactionWeight(tx));
entry.pushKV("locktime", (int64_t)tx.nLockTime);
diff --git a/src/crypto/siphash.cpp b/src/crypto/siphash.cpp
new file mode 100644
index 0000000000..e81957111a
--- /dev/null
+++ b/src/crypto/siphash.cpp
@@ -0,0 +1,173 @@
+// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <crypto/siphash.h>
+
+#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
+
+#define SIPROUND do { \
+ v0 += v1; v1 = ROTL(v1, 13); v1 ^= v0; \
+ v0 = ROTL(v0, 32); \
+ v2 += v3; v3 = ROTL(v3, 16); v3 ^= v2; \
+ v0 += v3; v3 = ROTL(v3, 21); v3 ^= v0; \
+ v2 += v1; v1 = ROTL(v1, 17); v1 ^= v2; \
+ v2 = ROTL(v2, 32); \
+} while (0)
+
+CSipHasher::CSipHasher(uint64_t k0, uint64_t k1)
+{
+ v[0] = 0x736f6d6570736575ULL ^ k0;
+ v[1] = 0x646f72616e646f6dULL ^ k1;
+ v[2] = 0x6c7967656e657261ULL ^ k0;
+ v[3] = 0x7465646279746573ULL ^ k1;
+ count = 0;
+ tmp = 0;
+}
+
+CSipHasher& CSipHasher::Write(uint64_t data)
+{
+ uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
+
+ assert(count % 8 == 0);
+
+ v3 ^= data;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= data;
+
+ v[0] = v0;
+ v[1] = v1;
+ v[2] = v2;
+ v[3] = v3;
+
+ count += 8;
+ return *this;
+}
+
+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;
+
+ while (size--) {
+ t |= ((uint64_t)(*(data++))) << (8 * (c % 8));
+ c++;
+ if ((c & 7) == 0) {
+ v3 ^= t;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= t;
+ t = 0;
+ }
+ }
+
+ v[0] = v0;
+ v[1] = v1;
+ v[2] = v2;
+ v[3] = v3;
+ count = c;
+ tmp = t;
+
+ return *this;
+}
+
+uint64_t CSipHasher::Finalize() const
+{
+ uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
+
+ uint64_t t = tmp | (((uint64_t)count) << 56);
+
+ v3 ^= t;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= t;
+ v2 ^= 0xFF;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ return v0 ^ v1 ^ v2 ^ v3;
+}
+
+uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val)
+{
+ /* Specialized implementation for efficiency */
+ uint64_t d = val.GetUint64(0);
+
+ uint64_t v0 = 0x736f6d6570736575ULL ^ k0;
+ uint64_t v1 = 0x646f72616e646f6dULL ^ k1;
+ uint64_t v2 = 0x6c7967656e657261ULL ^ k0;
+ uint64_t v3 = 0x7465646279746573ULL ^ k1 ^ d;
+
+ SIPROUND;
+ SIPROUND;
+ v0 ^= d;
+ d = val.GetUint64(1);
+ v3 ^= d;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= d;
+ d = val.GetUint64(2);
+ v3 ^= d;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= d;
+ d = val.GetUint64(3);
+ v3 ^= d;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= d;
+ v3 ^= ((uint64_t)4) << 59;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= ((uint64_t)4) << 59;
+ v2 ^= 0xFF;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ return v0 ^ v1 ^ v2 ^ v3;
+}
+
+uint64_t SipHashUint256Extra(uint64_t k0, uint64_t k1, const uint256& val, uint32_t extra)
+{
+ /* Specialized implementation for efficiency */
+ uint64_t d = val.GetUint64(0);
+
+ uint64_t v0 = 0x736f6d6570736575ULL ^ k0;
+ uint64_t v1 = 0x646f72616e646f6dULL ^ k1;
+ uint64_t v2 = 0x6c7967656e657261ULL ^ k0;
+ uint64_t v3 = 0x7465646279746573ULL ^ k1 ^ d;
+
+ SIPROUND;
+ SIPROUND;
+ v0 ^= d;
+ d = val.GetUint64(1);
+ v3 ^= d;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= d;
+ d = val.GetUint64(2);
+ v3 ^= d;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= d;
+ d = val.GetUint64(3);
+ v3 ^= d;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= d;
+ d = (((uint64_t)36) << 56) | extra;
+ v3 ^= d;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= d;
+ v2 ^= 0xFF;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ return v0 ^ v1 ^ v2 ^ v3;
+}
diff --git a/src/crypto/siphash.h b/src/crypto/siphash.h
new file mode 100644
index 0000000000..b312f913f9
--- /dev/null
+++ b/src/crypto/siphash.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_CRYPTO_SIPHASH_H
+#define BITCOIN_CRYPTO_SIPHASH_H
+
+#include <stdint.h>
+
+#include <uint256.h>
+
+/** SipHash-2-4 */
+class CSipHasher
+{
+private:
+ uint64_t v[4];
+ uint64_t tmp;
+ int count;
+
+public:
+ /** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */
+ CSipHasher(uint64_t k0, uint64_t k1);
+ /** Hash a 64-bit integer worth of data
+ * It is treated as if this was the little-endian interpretation of 8 bytes.
+ * This function can only be used when a multiple of 8 bytes have been written so far.
+ */
+ CSipHasher& Write(uint64_t data);
+ /** Hash arbitrary bytes. */
+ CSipHasher& Write(const unsigned char* data, size_t size);
+ /** Compute the 64-bit SipHash-2-4 of the data written so far. The object remains untouched. */
+ uint64_t Finalize() const;
+};
+
+/** Optimized SipHash-2-4 implementation for uint256.
+ *
+ * It is identical to:
+ * SipHasher(k0, k1)
+ * .Write(val.GetUint64(0))
+ * .Write(val.GetUint64(1))
+ * .Write(val.GetUint64(2))
+ * .Write(val.GetUint64(3))
+ * .Finalize()
+ */
+uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val);
+uint64_t SipHashUint256Extra(uint64_t k0, uint64_t k1, const uint256& val, uint32_t extra);
+
+#endif // BITCOIN_CRYPTO_SIPHASH_H
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 52f9efe17c..416f5e8399 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -9,8 +9,8 @@
#include <fs.h>
#include <serialize.h>
#include <streams.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <version.h>
#include <leveldb/db.h>
diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp
new file mode 100644
index 0000000000..9211a7596b
--- /dev/null
+++ b/src/dummywallet.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <stdio.h>
+#include <util/system.h>
+#include <walletinitinterface.h>
+
+class CWallet;
+
+class DummyWalletInit : public WalletInitInterface {
+public:
+
+ bool HasWalletSupport() const override {return false;}
+ void AddWalletOptions() const override;
+ bool ParameterInteraction() const override {return true;}
+ void Construct(InitInterfaces& interfaces) const override {LogPrintf("No wallet support compiled in!\n");}
+};
+
+void DummyWalletInit::AddWalletOptions() const
+{
+ std::vector<std::string> opts = {"-addresstype", "-changetype", "-disablewallet", "-discardfee=<amt>", "-fallbackfee=<amt>",
+ "-keypool=<n>", "-mintxfee=<amt>", "-paytxfee=<amt>", "-rescan", "-salvagewallet", "-spendzeroconfchange", "-txconfirmtarget=<n>",
+ "-upgradewallet", "-wallet=<path>", "-walletbroadcast", "-walletdir=<dir>", "-walletnotify=<cmd>", "-walletrbf", "-zapwallettxes=<mode>",
+ "-dblogsize=<n>", "-flushwallet", "-privdb", "-walletrejectlongchains"};
+ gArgs.AddHiddenArgs(opts);
+}
+
+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.");
+}
+
+namespace interfaces {
+
+class Wallet;
+
+std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet)
+{
+ throw std::logic_error("Wallet function called in non-wallet build.");
+}
+
+} // namespace interfaces
diff --git a/src/fs.cpp b/src/fs.cpp
index 2dbc13643b..3c8f4c0247 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -3,6 +3,7 @@
#ifndef WIN32
#include <fcntl.h>
#else
+#define NOMINMAX
#include <codecvt>
#include <windows.h>
#endif
@@ -11,7 +12,12 @@ namespace fsbridge {
FILE *fopen(const fs::path& p, const char *mode)
{
+#ifndef WIN32
return ::fopen(p.string().c_str(), mode);
+#else
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t> utf8_cvt;
+ return ::_wfopen(p.wstring().c_str(), utf8_cvt.from_bytes(mode).c_str());
+#endif
}
#ifndef WIN32
@@ -84,7 +90,7 @@ bool FileLock::TryLock()
return false;
}
_OVERLAPPED overlapped = {0};
- if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 0, 0, &overlapped)) {
+ if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, std::numeric_limits<DWORD>::max(), std::numeric_limits<DWORD>::max(), &overlapped)) {
reason = GetErrorReason();
return false;
}
@@ -92,4 +98,122 @@ bool FileLock::TryLock()
}
#endif
+std::string get_filesystem_error_message(const fs::filesystem_error& e)
+{
+#ifndef WIN32
+ return e.what();
+#else
+ // Convert from Multi Byte to utf-16
+ std::string mb_string(e.what());
+ int size = MultiByteToWideChar(CP_ACP, 0, mb_string.c_str(), mb_string.size(), nullptr, 0);
+
+ std::wstring utf16_string(size, L'\0');
+ MultiByteToWideChar(CP_ACP, 0, mb_string.c_str(), mb_string.size(), &*utf16_string.begin(), size);
+ // Convert from utf-16 to utf-8
+ return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>().to_bytes(utf16_string);
+#endif
+}
+
+#ifdef WIN32
+#ifdef __GLIBCXX__
+
+// reference: https://github.com/gcc-mirror/gcc/blob/gcc-7_3_0-release/libstdc%2B%2B-v3/include/std/fstream#L270
+
+static std::string openmodeToStr(std::ios_base::openmode mode)
+{
+ switch (mode & ~std::ios_base::ate) {
+ case std::ios_base::out:
+ case std::ios_base::out | std::ios_base::trunc:
+ return "w";
+ case std::ios_base::out | std::ios_base::app:
+ case std::ios_base::app:
+ return "a";
+ case std::ios_base::in:
+ return "r";
+ case std::ios_base::in | std::ios_base::out:
+ return "r+";
+ case std::ios_base::in | std::ios_base::out | std::ios_base::trunc:
+ return "w+";
+ case std::ios_base::in | std::ios_base::out | std::ios_base::app:
+ case std::ios_base::in | std::ios_base::app:
+ return "a+";
+ case std::ios_base::out | std::ios_base::binary:
+ case std::ios_base::out | std::ios_base::trunc | std::ios_base::binary:
+ return "wb";
+ case std::ios_base::out | std::ios_base::app | std::ios_base::binary:
+ case std::ios_base::app | std::ios_base::binary:
+ return "ab";
+ case std::ios_base::in | std::ios_base::binary:
+ return "rb";
+ case std::ios_base::in | std::ios_base::out | std::ios_base::binary:
+ return "r+b";
+ case std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::binary:
+ return "w+b";
+ case std::ios_base::in | std::ios_base::out | std::ios_base::app | std::ios_base::binary:
+ case std::ios_base::in | std::ios_base::app | std::ios_base::binary:
+ return "a+b";
+ default:
+ return std::string();
+ }
+}
+
+void ifstream::open(const fs::path& p, std::ios_base::openmode mode)
+{
+ close();
+ m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
+ if (m_file == nullptr) {
+ return;
+ }
+ m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode);
+ rdbuf(&m_filebuf);
+ if (mode & std::ios_base::ate) {
+ seekg(0, std::ios_base::end);
+ }
+}
+
+void ifstream::close()
+{
+ if (m_file != nullptr) {
+ m_filebuf.close();
+ fclose(m_file);
+ }
+ m_file = nullptr;
+}
+
+void ofstream::open(const fs::path& p, std::ios_base::openmode mode)
+{
+ close();
+ m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
+ if (m_file == nullptr) {
+ return;
+ }
+ m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode);
+ rdbuf(&m_filebuf);
+ if (mode & std::ios_base::ate) {
+ seekp(0, std::ios_base::end);
+ }
+}
+
+void ofstream::close()
+{
+ if (m_file != nullptr) {
+ m_filebuf.close();
+ fclose(m_file);
+ }
+ m_file = nullptr;
+}
+#else // __GLIBCXX__
+
+static_assert(sizeof(*fs::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t),
+ "Warning: This build is using boost::filesystem ofstream and ifstream "
+ "implementations which will fail to open paths containing multibyte "
+ "characters. You should delete this static_assert to ignore this warning, "
+ "or switch to a different C++ standard library like the Microsoft C++ "
+ "Standard Library (where boost uses non-standard extensions to construct "
+ "stream objects with wide filenames), or the GNU libstdc++ library (where "
+ "a more complicated workaround has been implemented above).");
+
+#endif // __GLIBCXX__
+#endif // WIN32
+
} // fsbridge
diff --git a/src/fs.h b/src/fs.h
index 5a28d9a81c..bdccb15232 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -7,10 +7,12 @@
#include <stdio.h>
#include <string>
+#if defined WIN32 && defined __GLIBCXX__
+#include <ext/stdio_filebuf.h>
+#endif
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
-#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
/** Filesystem operations and types */
namespace fs = boost::filesystem;
@@ -38,6 +40,56 @@ namespace fsbridge {
void* hFile = (void*)-1; // INVALID_HANDLE_VALUE
#endif
};
+
+ std::string get_filesystem_error_message(const fs::filesystem_error& e);
+
+ // GNU libstdc++ specific workaround for opening UTF-8 paths on Windows.
+ //
+ // On Windows, it is only possible to reliably access multibyte file paths through
+ // `wchar_t` APIs, not `char` APIs. But because the C++ standard doesn't
+ // require ifstream/ofstream `wchar_t` constructors, and the GNU library doesn't
+ // provide them (in contrast to the Microsoft C++ library, see
+ // https://stackoverflow.com/questions/821873/how-to-open-an-stdfstream-ofstream-or-ifstream-with-a-unicode-filename/822032#822032),
+ // Boost is forced to fall back to `char` constructors which may not work properly.
+ //
+ // Work around this issue by creating stream objects with `_wfopen` in
+ // combination with `__gnu_cxx::stdio_filebuf`. This workaround can be removed
+ // with an upgrade to C++17, where streams can be constructed directly from
+ // `std::filesystem::path` objects.
+
+#if defined WIN32 && defined __GLIBCXX__
+ class ifstream : public std::istream
+ {
+ public:
+ ifstream() = default;
+ explicit ifstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in) { open(p, mode); }
+ ~ifstream() { close(); }
+ void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in);
+ bool is_open() { return m_filebuf.is_open(); }
+ void close();
+
+ private:
+ __gnu_cxx::stdio_filebuf<char> m_filebuf;
+ FILE* m_file = nullptr;
+ };
+ class ofstream : public std::ostream
+ {
+ public:
+ ofstream() = default;
+ explicit ofstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out) { open(p, mode); }
+ ~ofstream() { close(); }
+ void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out);
+ bool is_open() { return m_filebuf.is_open(); }
+ void close();
+
+ private:
+ __gnu_cxx::stdio_filebuf<char> m_filebuf;
+ FILE* m_file = nullptr;
+ };
+#else // !(WIN32 && __GLIBCXX__)
+ typedef fs::ifstream ifstream;
+ typedef fs::ofstream ofstream;
+#endif // WIN32 && __GLIBCXX__
};
#endif // BITCOIN_FS_H
diff --git a/src/hash.cpp b/src/hash.cpp
index c049eea716..26150e5ca8 100644
--- a/src/hash.cpp
+++ b/src/hash.cpp
@@ -77,171 +77,3 @@ void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char he
num[3] = (nChild >> 0) & 0xFF;
CHMAC_SHA512(chainCode.begin(), chainCode.size()).Write(&header, 1).Write(data, 32).Write(num, 4).Finalize(output);
}
-
-#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
-
-#define SIPROUND do { \
- v0 += v1; v1 = ROTL(v1, 13); v1 ^= v0; \
- v0 = ROTL(v0, 32); \
- v2 += v3; v3 = ROTL(v3, 16); v3 ^= v2; \
- v0 += v3; v3 = ROTL(v3, 21); v3 ^= v0; \
- v2 += v1; v1 = ROTL(v1, 17); v1 ^= v2; \
- v2 = ROTL(v2, 32); \
-} while (0)
-
-CSipHasher::CSipHasher(uint64_t k0, uint64_t k1)
-{
- v[0] = 0x736f6d6570736575ULL ^ k0;
- v[1] = 0x646f72616e646f6dULL ^ k1;
- v[2] = 0x6c7967656e657261ULL ^ k0;
- v[3] = 0x7465646279746573ULL ^ k1;
- count = 0;
- tmp = 0;
-}
-
-CSipHasher& CSipHasher::Write(uint64_t data)
-{
- uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
-
- assert(count % 8 == 0);
-
- v3 ^= data;
- SIPROUND;
- SIPROUND;
- v0 ^= data;
-
- v[0] = v0;
- v[1] = v1;
- v[2] = v2;
- v[3] = v3;
-
- count += 8;
- return *this;
-}
-
-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;
-
- while (size--) {
- t |= ((uint64_t)(*(data++))) << (8 * (c % 8));
- c++;
- if ((c & 7) == 0) {
- v3 ^= t;
- SIPROUND;
- SIPROUND;
- v0 ^= t;
- t = 0;
- }
- }
-
- v[0] = v0;
- v[1] = v1;
- v[2] = v2;
- v[3] = v3;
- count = c;
- tmp = t;
-
- return *this;
-}
-
-uint64_t CSipHasher::Finalize() const
-{
- uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
-
- uint64_t t = tmp | (((uint64_t)count) << 56);
-
- v3 ^= t;
- SIPROUND;
- SIPROUND;
- v0 ^= t;
- v2 ^= 0xFF;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- return v0 ^ v1 ^ v2 ^ v3;
-}
-
-uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val)
-{
- /* Specialized implementation for efficiency */
- uint64_t d = val.GetUint64(0);
-
- uint64_t v0 = 0x736f6d6570736575ULL ^ k0;
- uint64_t v1 = 0x646f72616e646f6dULL ^ k1;
- uint64_t v2 = 0x6c7967656e657261ULL ^ k0;
- uint64_t v3 = 0x7465646279746573ULL ^ k1 ^ d;
-
- SIPROUND;
- SIPROUND;
- v0 ^= d;
- d = val.GetUint64(1);
- v3 ^= d;
- SIPROUND;
- SIPROUND;
- v0 ^= d;
- d = val.GetUint64(2);
- v3 ^= d;
- SIPROUND;
- SIPROUND;
- v0 ^= d;
- d = val.GetUint64(3);
- v3 ^= d;
- SIPROUND;
- SIPROUND;
- v0 ^= d;
- v3 ^= ((uint64_t)4) << 59;
- SIPROUND;
- SIPROUND;
- v0 ^= ((uint64_t)4) << 59;
- v2 ^= 0xFF;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- return v0 ^ v1 ^ v2 ^ v3;
-}
-
-uint64_t SipHashUint256Extra(uint64_t k0, uint64_t k1, const uint256& val, uint32_t extra)
-{
- /* Specialized implementation for efficiency */
- uint64_t d = val.GetUint64(0);
-
- uint64_t v0 = 0x736f6d6570736575ULL ^ k0;
- uint64_t v1 = 0x646f72616e646f6dULL ^ k1;
- uint64_t v2 = 0x6c7967656e657261ULL ^ k0;
- uint64_t v3 = 0x7465646279746573ULL ^ k1 ^ d;
-
- SIPROUND;
- SIPROUND;
- v0 ^= d;
- d = val.GetUint64(1);
- v3 ^= d;
- SIPROUND;
- SIPROUND;
- v0 ^= d;
- d = val.GetUint64(2);
- v3 ^= d;
- SIPROUND;
- SIPROUND;
- v0 ^= d;
- d = val.GetUint64(3);
- v3 ^= d;
- SIPROUND;
- SIPROUND;
- v0 ^= d;
- d = (((uint64_t)36) << 56) | extra;
- v3 ^= d;
- SIPROUND;
- SIPROUND;
- v0 ^= d;
- v2 ^= 0xFF;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- SIPROUND;
- return v0 ^ v1 ^ v2 ^ v3;
-}
diff --git a/src/hash.h b/src/hash.h
index 1645eceb03..c295568a3e 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -204,39 +204,4 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char
void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]);
-/** SipHash-2-4 */
-class CSipHasher
-{
-private:
- uint64_t v[4];
- uint64_t tmp;
- int count;
-
-public:
- /** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */
- CSipHasher(uint64_t k0, uint64_t k1);
- /** Hash a 64-bit integer worth of data
- * It is treated as if this was the little-endian interpretation of 8 bytes.
- * This function can only be used when a multiple of 8 bytes have been written so far.
- */
- CSipHasher& Write(uint64_t data);
- /** Hash arbitrary bytes. */
- CSipHasher& Write(const unsigned char* data, size_t size);
- /** Compute the 64-bit SipHash-2-4 of the data written so far. The object remains untouched. */
- uint64_t Finalize() const;
-};
-
-/** Optimized SipHash-2-4 implementation for uint256.
- *
- * It is identical to:
- * SipHasher(k0, k1)
- * .Write(val.GetUint64(0))
- * .Write(val.GetUint64(1))
- * .Write(val.GetUint64(2))
- * .Write(val.GetUint64(3))
- * .Finalize()
- */
-uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val);
-uint64_t SipHashUint256Extra(uint64_t k0, uint64_t k1, const uint256& val, uint32_t extra);
-
#endif // BITCOIN_HASH_H
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 38f6e79643..fcf760a4c6 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -11,9 +11,10 @@
#include <rpc/server.h>
#include <random.h>
#include <sync.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <ui_interface.h>
+#include <walletinitinterface.h>
#include <crypto/hmac_sha256.h>
#include <stdio.h>
@@ -30,7 +31,7 @@ static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
class HTTPRPCTimer : public RPCTimerBase
{
public:
- HTTPRPCTimer(struct event_base* eventBase, std::function<void(void)>& func, int64_t millis) :
+ HTTPRPCTimer(struct event_base* eventBase, std::function<void()>& func, int64_t millis) :
ev(eventBase, false, func)
{
struct timeval tv;
@@ -52,7 +53,7 @@ public:
{
return "HTTP";
}
- RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) override
+ RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override
{
return new HTTPRPCTimer(base, func, millis);
}
@@ -240,10 +241,9 @@ bool StartHTTPRPC()
return false;
RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
-#ifdef ENABLE_WALLET
- // ifdef can be removed once we switch to better endpoint support and API versioning
- RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
-#endif
+ if (g_wallet_init_interface.HasWalletSupport()) {
+ RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
+ }
struct event_base* eventBase = EventBase();
assert(eventBase);
httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(eventBase);
@@ -260,9 +260,9 @@ void StopHTTPRPC()
{
LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
UnregisterHTTPHandler("/", true);
-#ifdef ENABLE_WALLET
- UnregisterHTTPHandler("/wallet/", false);
-#endif
+ if (g_wallet_init_interface.HasWalletSupport()) {
+ UnregisterHTTPHandler("/wallet/", false);
+ }
if (httpRPCTimerInterface) {
RPCUnsetTimerInterface(httpRPCTimerInterface.get());
httpRPCTimerInterface.reset();
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 326f7f6b64..00434169cd 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -6,8 +6,8 @@
#include <chainparamsbase.h>
#include <compat.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <netbase.h>
#include <rpc/protocol.h> // For HTTP status codes
#include <sync.h>
@@ -224,21 +224,25 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
}
std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
- LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
- RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString());
-
// Early address-based allow check
if (!ClientAllowed(hreq->GetPeer())) {
+ LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Client network is not allowed RPC access\n",
+ hreq->GetPeer().ToString());
hreq->WriteReply(HTTP_FORBIDDEN);
return;
}
// Early reject unknown HTTP methods
if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) {
+ LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n",
+ hreq->GetPeer().ToString());
hreq->WriteReply(HTTP_BADMETHOD);
return;
}
+ LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
+ RequestMethodString(hreq->GetRequestMethod()), SanitizeString(hreq->GetURI(), SAFE_CHARS_URI).substr(0, 100), hreq->GetPeer().ToString());
+
// Find registered handler for prefix
std::string strURI = hreq->GetURI();
std::string path;
@@ -292,26 +296,26 @@ static bool ThreadHTTP(struct event_base* base)
/** Bind HTTP server to specified addresses */
static bool HTTPBindAddresses(struct evhttp* http)
{
- int defaultPort = gArgs.GetArg("-rpcport", BaseParams().RPCPort());
+ int http_port = gArgs.GetArg("-rpcport", BaseParams().RPCPort());
std::vector<std::pair<std::string, uint16_t> > endpoints;
// Determine what addresses to bind to
- if (!gArgs.IsArgSet("-rpcallowip")) { // Default to loopback if not allowing external IPs
- endpoints.push_back(std::make_pair("::1", defaultPort));
- endpoints.push_back(std::make_pair("127.0.0.1", defaultPort));
+ if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs
+ endpoints.push_back(std::make_pair("::1", http_port));
+ endpoints.push_back(std::make_pair("127.0.0.1", http_port));
+ if (gArgs.IsArgSet("-rpcallowip")) {
+ LogPrintf("WARNING: option -rpcallowip was specified without -rpcbind; this doesn't usually make sense\n");
+ }
if (gArgs.IsArgSet("-rpcbind")) {
LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
}
} else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address
for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) {
- int port = defaultPort;
+ int port = http_port;
std::string host;
SplitHostPort(strRPCBind, port, host);
endpoints.push_back(std::make_pair(host, port));
}
- } else { // No specific bind address specified, bind to any
- endpoints.push_back(std::make_pair("::", defaultPort));
- endpoints.push_back(std::make_pair("0.0.0.0", defaultPort));
}
// Bind addresses
@@ -319,6 +323,10 @@ static bool HTTPBindAddresses(struct evhttp* http)
LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second);
evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second);
if (bind_handle) {
+ CNetAddr addr;
+ if (i->first.empty() || (LookupHost(i->first.c_str(), addr, false) && addr.IsBindAny())) {
+ LogPrintf("WARNING: the RPC server is not safe to expose to untrusted networks such as the public internet\n");
+ }
boundSockets.push_back(bind_handle);
} else {
LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second);
@@ -352,13 +360,6 @@ bool InitHTTPServer()
if (!InitHTTPAllowList())
return false;
- if (gArgs.GetBoolArg("-rpcssl", false)) {
- uiInterface.ThreadSafeMessageBox(
- "SSL mode for RPC (-rpcssl) is no longer supported.",
- "", CClientUIInterface::MSG_ERROR);
- return false;
- }
-
// Redirect libevent's logging to our own log
event_set_log_callback(&libevent_log_cb);
// Update libevent's log handling. Returns false if our version of
@@ -505,7 +506,7 @@ static void httpevent_callback_fn(evutil_socket_t, short, void* data)
delete self;
}
-HTTPEvent::HTTPEvent(struct event_base* base, bool _deleteWhenTriggered, const std::function<void(void)>& _handler):
+HTTPEvent::HTTPEvent(struct event_base* base, bool _deleteWhenTriggered, const std::function<void()>& _handler):
deleteWhenTriggered(_deleteWhenTriggered), handler(_handler)
{
ev = event_new(base, -1, 0, httpevent_callback_fn, this);
diff --git a/src/httpserver.h b/src/httpserver.h
index 67c6a88314..63f96734f8 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -134,7 +134,7 @@ public:
* deleteWhenTriggered deletes this event object after the event is triggered (and the handler called)
* handler is the handler to call when the event is triggered.
*/
- HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void(void)>& handler);
+ HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void()>& handler);
~HTTPEvent();
/** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after
@@ -143,7 +143,7 @@ public:
void trigger(struct timeval* tv);
bool deleteWhenTriggered;
- std::function<void(void)> handler;
+ std::function<void()> handler;
private:
struct event* ev;
};
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 788f7adccd..4d4a7e1502 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -7,7 +7,7 @@
#include <shutdown.h>
#include <tinyformat.h>
#include <ui_interface.h>
-#include <util.h>
+#include <util/system.h>
#include <validation.h>
#include <warnings.h>
@@ -65,7 +65,7 @@ bool BaseIndex::Init()
return true;
}
-static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev)
+static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index c85030e18e..ba1c44765f 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -5,7 +5,7 @@
#include <index/txindex.h>
#include <shutdown.h>
#include <ui_interface.h>
-#include <util.h>
+#include <util/system.h>
#include <validation.h>
#include <boost/thread.hpp>
@@ -250,7 +250,7 @@ bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
vPos.reserve(block.vtx.size());
for (const auto& tx : block.vtx) {
vPos.emplace_back(tx->GetHash(), pos);
- pos.nTxOffset += ::GetSerializeSize(*tx, SER_DISK, CLIENT_VERSION);
+ pos.nTxOffset += ::GetSerializeSize(*tx, CLIENT_VERSION);
}
return m_db->WriteTxs(vPos);
}
diff --git a/src/init.cpp b/src/init.cpp
index a3b04c6c44..31212a355b 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -19,6 +19,7 @@
#include <fs.h>
#include <httpserver.h>
#include <httprpc.h>
+#include <interfaces/chain.h>
#include <index/txindex.h>
#include <key.h>
#include <validation.h>
@@ -32,6 +33,7 @@
#include <rpc/server.h>
#include <rpc/register.h>
#include <rpc/blockchain.h>
+#include <rpc/util.h>
#include <script/standard.h>
#include <script/sigcache.h>
#include <scheduler.h>
@@ -41,8 +43,8 @@
#include <txmempool.h>
#include <torcontrol.h>
#include <ui_interface.h>
-#include <util.h>
-#include <utilmoneystr.h>
+#include <util/system.h>
+#include <util/moneystr.h>
#include <validationinterface.h>
#include <warnings.h>
#include <walletinitinterface.h>
@@ -62,6 +64,7 @@
#include <openssl/crypto.h>
#if ENABLE_ZMQ
+#include <zmq/zmqabstractnotifier.h>
#include <zmq/zmqnotificationinterface.h>
#include <zmq/zmqrpc.h>
#endif
@@ -74,33 +77,6 @@ static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
std::unique_ptr<CConnman> g_connman;
std::unique_ptr<PeerLogicValidation> peerLogic;
-#if !(ENABLE_WALLET)
-class DummyWalletInit : public WalletInitInterface {
-public:
-
- void AddWalletOptions() const override;
- bool ParameterInteraction() const override {return true;}
- void RegisterRPC(CRPCTable &) const override {}
- bool Verify() const override {return true;}
- bool Open() const override {LogPrintf("No wallet support compiled in!\n"); return true;}
- void Start(CScheduler& scheduler) const override {}
- void Flush() const override {}
- void Stop() const override {}
- void Close() const override {}
-};
-
-void DummyWalletInit::AddWalletOptions() const
-{
- std::vector<std::string> opts = {"-addresstype", "-changetype", "-disablewallet", "-discardfee=<amt>", "-fallbackfee=<amt>",
- "-keypool=<n>", "-mintxfee=<amt>", "-paytxfee=<amt>", "-rescan", "-salvagewallet", "-spendzeroconfchange", "-txconfirmtarget=<n>",
- "-upgradewallet", "-wallet=<path>", "-walletbroadcast", "-walletdir=<dir>", "-walletnotify=<cmd>", "-walletrbf", "-zapwallettxes=<mode>",
- "-dblogsize=<n>", "-flushwallet", "-privdb", "-walletrejectlongchains"};
- gArgs.AddHiddenArgs(opts);
-}
-
-const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
-#endif
-
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for
// accessing block files don't count towards the fd_set size limit
@@ -183,7 +159,7 @@ void Interrupt()
}
}
-void Shutdown()
+void Shutdown(InitInterfaces& interfaces)
{
LogPrintf("%s: In progress...\n", __func__);
static CCriticalSection cs_Shutdown;
@@ -202,7 +178,9 @@ void Shutdown()
StopREST();
StopRPC();
StopHTTPServer();
- g_wallet_init_interface.Flush();
+ for (const auto& client : interfaces.chain_clients) {
+ client->flush();
+ }
StopMapPort();
// Because these depend on each-other, we make sure that neither can be
@@ -265,7 +243,9 @@ void Shutdown()
pcoinsdbview.reset();
pblocktree.reset();
}
- g_wallet_init_interface.Stop();
+ for (const auto& client : interfaces.chain_clients) {
+ client->stop();
+ }
#if ENABLE_ZMQ
if (g_zmq_notification_interface) {
@@ -285,7 +265,7 @@ void Shutdown()
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
GetMainSignals().UnregisterWithMempoolSignals(mempool);
- g_wallet_init_interface.Close();
+ interfaces.chain_clients.clear();
globalVerifyHandle.reset();
ECC_Stop();
LogPrintf("%s: done\n", __func__);
@@ -349,8 +329,8 @@ void SetupServerArgs()
const auto regtestChainParams = CreateChainParams(CBaseChainParams::REGTEST);
// Hidden Options
- std::vector<std::string> hidden_args = {"-rpcssl", "-benchmark", "-h", "-help", "-socks", "-tor", "-debugnet", "-whitelistalwaysrelay",
- "-prematurewitness", "-walletprematurewitness", "-promiscuousmempoolflags", "-blockminsize", "-dbcrashratio", "-forcecompactdb", "-usehd",
+ std::vector<std::string> hidden_args = {"-h", "-help",
+ "-dbcrashratio", "-forcecompactdb",
// GUI args. These will be overwritten by SetupUIArgs for the GUI
"-allowselfsignedrootcertificates", "-choosedatadir", "-lang=<lang>", "-min", "-resetguisettings", "-rootcertificates=<file>", "-splash", "-uiplatform"};
@@ -445,15 +425,30 @@ void SetupServerArgs()
gArgs.AddArg("-zmqpubhashtx=<address>", "Enable publish hash transaction in <address>", false, OptionsCategory::ZMQ);
gArgs.AddArg("-zmqpubrawblock=<address>", "Enable publish raw block in <address>", false, OptionsCategory::ZMQ);
gArgs.AddArg("-zmqpubrawtx=<address>", "Enable publish raw transaction in <address>", false, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashblockhwm=<n>", strprintf("Set publish hash block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubhashtxhwm=<n>", strprintf("Set publish hash transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawblockhwm=<n>", strprintf("Set publish raw block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
+ gArgs.AddArg("-zmqpubrawtxhwm=<n>", strprintf("Set publish raw transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
#else
hidden_args.emplace_back("-zmqpubhashblock=<address>");
hidden_args.emplace_back("-zmqpubhashtx=<address>");
hidden_args.emplace_back("-zmqpubrawblock=<address>");
hidden_args.emplace_back("-zmqpubrawtx=<address>");
+ hidden_args.emplace_back("-zmqpubhashblockhwm=<n>");
+ hidden_args.emplace_back("-zmqpubhashtxhwm=<n>");
+ hidden_args.emplace_back("-zmqpubrawblockhwm=<n>");
+ hidden_args.emplace_back("-zmqpubrawtxhwm=<n>");
#endif
gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is (0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: "
+ "level 0 reads the blocks from disk, "
+ "level 1 verifies block validity, "
+ "level 2 verifies undo data, "
+ "level 3 checks disconnection of tip blocks, "
+ "and level 4 tries to reconnect the blocks, "
+ "each level includes the checks of the previous levels "
+ "(0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), true, OptionsCategory::DEBUG_TEST);
@@ -465,12 +460,11 @@ void SetupServerArgs()
gArgs.AddArg("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-addrmantest", "Allows to test address relay on localhost", true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-debug=<category>", "Output debugging information (default: -nodebug, supplying <category> is optional). "
"If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + ListLogCategories() + ".", false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."), false, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-help-debug", "Show all debugging options (usage: --help -help-debug)", false, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-help-debug", "Print help message with debugging options and exit", false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST);
@@ -506,7 +500,7 @@ void SetupServerArgs()
gArgs.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), false, OptionsCategory::RPC);
gArgs.AddArg("-rpcallowip=<ip>", "Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcauth=<userpw>", "Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcauth. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", false, OptionsCategory::RPC);
- gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)", false, OptionsCategory::RPC);
+ gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost)", false, OptionsCategory::RPC);
gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::RPC);
gArgs.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), false, OptionsCategory::RPC);
@@ -711,7 +705,7 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
* Ensure that Bitcoin is running in a usable environment with all
* necessary library support.
*/
-static bool InitSanityCheck(void)
+static bool InitSanityCheck()
{
if(!ECC_InitSanityCheck()) {
InitError("Elliptic curve cryptography sanity check failure. Aborting.");
@@ -809,7 +803,15 @@ void InitParameterInteraction()
// Warn if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden
// on the command line or in this network's section of the config file.
- gArgs.WarnForSectionOnlyArgs();
+ std::string network = gArgs.GetChainName();
+ for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
+ InitWarning(strprintf(_("Config setting for %s only applied on %s network when in [%s] section."), arg, network, network));
+ }
+
+ // Warn if unrecognized section name are present in the config file.
+ for (const auto& section : gArgs.GetUnrecognizedSections()) {
+ InitWarning(strprintf(_("Section [%s] is not recognized."), section));
+ }
}
static std::string ResolveErrMsg(const char * const optname, const std::string& strBind)
@@ -981,25 +983,6 @@ bool AppInitParameterInteraction()
}
}
- // Check for -debugnet
- if (gArgs.GetBoolArg("-debugnet", false))
- InitWarning(_("Unsupported argument -debugnet ignored, use -debug=net."));
- // Check for -socks - as this is a privacy risk to continue, exit here
- if (gArgs.IsArgSet("-socks"))
- return InitError(_("Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported."));
- // Check for -tor - as this is a privacy risk to continue, exit here
- if (gArgs.GetBoolArg("-tor", false))
- return InitError(_("Unsupported argument -tor found, use -onion."));
-
- if (gArgs.GetBoolArg("-benchmark", false))
- InitWarning(_("Unsupported argument -benchmark ignored, use -debug=bench."));
-
- if (gArgs.GetBoolArg("-whitelistalwaysrelay", false))
- InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay."));
-
- if (gArgs.IsArgSet("-blockminsize"))
- InitWarning("Unsupported argument -blockminsize ignored.");
-
// Checkmempool and checkblockindex default to true in regtest mode
int ratio = std::min<int>(std::max<int>(gArgs.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
if (ratio != 0) {
@@ -1140,39 +1123,6 @@ bool AppInitParameterInteraction()
fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end());
}
- if (gArgs.IsArgSet("-vbparams")) {
- // Allow overriding version bits parameters for testing
- if (!chainparams.MineBlocksOnDemand()) {
- return InitError("Version bits parameters may only be overridden on regtest.");
- }
- for (const std::string& strDeployment : gArgs.GetArgs("-vbparams")) {
- std::vector<std::string> vDeploymentParams;
- boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
- if (vDeploymentParams.size() != 3) {
- return InitError("Version bits parameters malformed, expecting deployment:start:end");
- }
- int64_t nStartTime, nTimeout;
- if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
- return InitError(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
- }
- if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
- return InitError(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
- }
- bool found = false;
- for (int j=0; j<(int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j)
- {
- if (vDeploymentParams[0].compare(VersionBitsDeploymentInfo[j].name) == 0) {
- UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout);
- found = true;
- LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout);
- break;
- }
- }
- if (!found) {
- return InitError(strprintf("Invalid deployment (%s)", vDeploymentParams[0]));
- }
- }
- }
return true;
}
@@ -1222,7 +1172,7 @@ bool AppInitLockDataDirectory()
return true;
}
-bool AppInitMain()
+bool AppInitMain(InitInterfaces& interfaces)
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization
@@ -1245,7 +1195,19 @@ bool AppInitMain()
LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime()));
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
LogPrintf("Using data directory %s\n", GetDataDir().string());
- LogPrintf("Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
+
+ // Only log conf file usage message if conf file actually exists.
+ fs::path config_file_path = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
+ if (fs::exists(config_file_path)) {
+ LogPrintf("Config file: %s\n", config_file_path.string());
+ } else if (gArgs.IsArgSet("-conf")) {
+ // Warn if no conf file exists at path provided by user
+ InitWarning(strprintf(_("The specified config file %s does not exist\n"), config_file_path.string()));
+ } else {
+ // Not categorizing as "Warning" because it's the default behavior
+ LogPrintf("Config file: %s (not found, skipping)\n", config_file_path.string());
+ }
+
LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);
// Warn about relative -datadir path.
@@ -1273,11 +1235,20 @@ bool AppInitMain()
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
GetMainSignals().RegisterWithMempoolSignals(mempool);
+ // Create client interfaces for wallets that are supposed to be loaded
+ // according to -wallet and -disablewallet options. This only constructs
+ // 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(interfaces);
+
/* Register RPC commands regardless of -server setting so they will be
* available in the GUI RPC console even if external calls are disabled.
*/
RegisterAllCoreRPCCommands(tableRPC);
- g_wallet_init_interface.RegisterRPC(tableRPC);
+ for (const auto& client : interfaces.chain_clients) {
+ client->registerRpcs();
+ }
+ g_rpc_interfaces = &interfaces;
#if ENABLE_ZMQ
RegisterZMQRPCCommands(tableRPC);
#endif
@@ -1295,7 +1266,11 @@ bool AppInitMain()
}
// ********************************************************* Step 5: verify wallet database integrity
- if (!g_wallet_init_interface.Verify()) return false;
+ for (const auto& client : interfaces.chain_clients) {
+ if (!client->verify()) {
+ return false;
+ }
+ }
// ********************************************************* Step 6: network initialization
// Note that we absolutely cannot open any actual connections
@@ -1614,7 +1589,11 @@ bool AppInitMain()
}
// ********************************************************* Step 9: load wallet
- if (!g_wallet_init_interface.Open()) return false;
+ for (const auto& client : interfaces.chain_clients) {
+ if (!client->load()) {
+ return false;
+ }
+ }
// ********************************************************* Step 10: data directory maintenance
@@ -1760,7 +1739,9 @@ bool AppInitMain()
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading"));
- g_wallet_init_interface.Start(scheduler);
+ for (const auto& client : interfaces.chain_clients) {
+ client->start(scheduler);
+ }
return true;
}
diff --git a/src/init.h b/src/init.h
index 0c85d3c9dc..1c59ca069e 100644
--- a/src/init.h
+++ b/src/init.h
@@ -8,13 +8,19 @@
#include <memory>
#include <string>
-#include <util.h>
+#include <util/system.h>
-class CScheduler;
-class CWallet;
+namespace interfaces {
+class Chain;
+class ChainClient;
+} // namespace interfaces
-class WalletInitInterface;
-extern const WalletInitInterface& g_wallet_init_interface;
+//! Pointers to interfaces used during init and destroyed on shutdown.
+struct InitInterfaces
+{
+ std::unique_ptr<interfaces::Chain> chain;
+ std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
+};
namespace boost
{
@@ -23,7 +29,7 @@ class thread_group;
/** Interrupt threads */
void Interrupt();
-void Shutdown();
+void Shutdown(InitInterfaces& interfaces);
//!Initialize the logging infrastructure
void InitLogging();
//!Parameter interaction: change current parameters depending on various rules
@@ -57,7 +63,7 @@ bool AppInitLockDataDirectory();
* @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.
*/
-bool AppInitMain();
+bool AppInitMain(InitInterfaces& interfaces);
/**
* Setup the arguments for gArgs
diff --git a/src/interfaces/README.md b/src/interfaces/README.md
index e93b91d23c..57d41df746 100644
--- a/src/interfaces/README.md
+++ b/src/interfaces/README.md
@@ -4,7 +4,7 @@ The following interfaces are defined here:
* [`Chain`](chain.h) — used by wallet to access blockchain and mempool state. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
-* [`Chain::Client`](chain.h) — used by node to start & stop `Chain` clients. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
+* [`ChainClient`](chain.h) — used by node to start & stop `Chain` clients. Added in [#10973](https://github.com/bitcoin/bitcoin/pull/10973).
* [`Node`](node.h) — used by GUI to start & stop bitcoin node. Added in [#10244](https://github.com/bitcoin/bitcoin/pull/10244).
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
new file mode 100644
index 0000000000..2571a91031
--- /dev/null
+++ b/src/interfaces/chain.cpp
@@ -0,0 +1,44 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <interfaces/chain.h>
+
+#include <sync.h>
+#include <util/system.h>
+#include <validation.h>
+
+#include <memory>
+#include <utility>
+
+namespace interfaces {
+namespace {
+
+class LockImpl : public Chain::Lock
+{
+};
+
+class LockingStateImpl : public LockImpl, public UniqueLock<CCriticalSection>
+{
+ using UniqueLock::UniqueLock;
+};
+
+class ChainImpl : public Chain
+{
+public:
+ std::unique_ptr<Chain::Lock> lock(bool try_lock) override
+ {
+ auto result = MakeUnique<LockingStateImpl>(::cs_main, "cs_main", __FILE__, __LINE__, try_lock);
+ if (try_lock && result && !*result) return {};
+ // std::move necessary on some compilers due to conversion from
+ // LockingStateImpl to Lock pointer
+ return std::move(result);
+ }
+ std::unique_ptr<Chain::Lock> assumeLocked() override { return MakeUnique<LockImpl>(); }
+};
+
+} // namespace
+
+std::unique_ptr<Chain> MakeChain() { return MakeUnique<ChainImpl>(); }
+
+} // namespace interfaces
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
new file mode 100644
index 0000000000..fe5658de4b
--- /dev/null
+++ b/src/interfaces/chain.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_INTERFACES_CHAIN_H
+#define BITCOIN_INTERFACES_CHAIN_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+class CScheduler;
+
+namespace interfaces {
+
+//! Interface for giving wallet processes access to blockchain state.
+class Chain
+{
+public:
+ virtual ~Chain() {}
+
+ //! Interface for querying locked chain state, used by legacy code that
+ //! assumes state won't change between calls. New code should avoid using
+ //! the Lock interface and instead call higher-level Chain methods
+ //! that return more information so the chain doesn't need to stay locked
+ //! between calls.
+ class Lock
+ {
+ public:
+ virtual ~Lock() {}
+ };
+
+ //! Return Lock interface. Chain is locked when this is called, and
+ //! unlocked when the returned interface is freed.
+ virtual std::unique_ptr<Lock> lock(bool try_lock = false) = 0;
+
+ //! Return Lock interface assuming chain is already locked. This
+ //! method is temporary and is only used in a few places to avoid changing
+ //! behavior while code is transitioned to use the Chain::Lock interface.
+ virtual std::unique_ptr<Lock> assumeLocked() = 0;
+};
+
+//! Interface to let node manage chain clients (wallets, or maybe tools for
+//! monitoring and analysis in the future).
+class ChainClient
+{
+public:
+ virtual ~ChainClient() {}
+
+ //! Register rpcs.
+ virtual void registerRpcs() = 0;
+
+ //! Check for errors before loading.
+ virtual bool verify() = 0;
+
+ //! Load saved state.
+ virtual bool load() = 0;
+
+ //! Start client execution and provide a scheduler.
+ virtual void start(CScheduler& scheduler) = 0;
+
+ //! Save state to disk.
+ virtual void flush() = 0;
+
+ //! Shut down client.
+ virtual void stop() = 0;
+};
+
+//! Return implementation of Chain interface.
+std::unique_ptr<Chain> MakeChain();
+
+//! 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, std::vector<std::string> wallet_filenames);
+
+} // namespace interfaces
+
+#endif // BITCOIN_INTERFACES_CHAIN_H
diff --git a/src/interfaces/handler.cpp b/src/interfaces/handler.cpp
index ddf9b342d8..92601fc4e9 100644
--- a/src/interfaces/handler.cpp
+++ b/src/interfaces/handler.cpp
@@ -4,7 +4,7 @@
#include <interfaces/handler.h>
-#include <utilmemory.h>
+#include <util/memory.h>
#include <boost/signals2/connection.hpp>
#include <utility>
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index 1da58fe487..bd7e414ff3 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -9,6 +9,7 @@
#include <chain.h>
#include <chainparams.h>
#include <init.h>
+#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <interfaces/wallet.h>
#include <net.h>
@@ -25,30 +26,32 @@
#include <sync.h>
#include <txmempool.h>
#include <ui_interface.h>
-#include <util.h>
+#include <util/system.h>
#include <validation.h>
#include <warnings.h>
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
-#ifdef ENABLE_WALLET
-#include <wallet/fees.h>
-#include <wallet/wallet.h>
-#define CHECK_WALLET(x) x
-#else
-#define CHECK_WALLET(x) throw std::logic_error("Wallet function called in non-wallet build.")
-#endif
#include <atomic>
-#include <boost/thread/thread.hpp>
#include <univalue.h>
+class CWallet;
+fs::path GetWalletDir();
+std::vector<fs::path> ListWalletDir();
+std::vector<std::shared_ptr<CWallet>> GetWallets();
+
namespace interfaces {
+
+class Wallet;
+
namespace {
class NodeImpl : public Node
{
+public:
+ NodeImpl() { m_interfaces.chain = MakeChain(); }
bool parseParameters(int argc, const char* const argv[], std::string& error) override
{
return gArgs.ParseParameters(argc, argv, error);
@@ -67,11 +70,11 @@ class NodeImpl : public Node
return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
AppInitLockDataDirectory();
}
- bool appInitMain() override { return AppInitMain(); }
+ bool appInitMain() override { return AppInitMain(m_interfaces); }
void appShutdown() override
{
Interrupt();
- Shutdown();
+ Shutdown(m_interfaces);
}
void startShutdown() override { StartShutdown(); }
bool shutdownRequested() override { return ShutdownRequested(); }
@@ -219,17 +222,25 @@ class NodeImpl : public Node
LOCK(::cs_main);
return ::pcoinsTip->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
{
-#ifdef ENABLE_WALLET
std::vector<std::unique_ptr<Wallet>> wallets;
for (const std::shared_ptr<CWallet>& wallet : GetWallets()) {
wallets.emplace_back(MakeWallet(wallet));
}
return wallets;
-#else
- throw std::logic_error("Node::getWallets() called in non-wallet build.");
-#endif
}
std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override
{
@@ -249,8 +260,7 @@ class NodeImpl : public Node
}
std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
{
- CHECK_WALLET(
- return MakeHandler(::uiInterface.LoadWallet_connect([fn](std::shared_ptr<CWallet> wallet) { fn(MakeWallet(wallet)); })));
+ return MakeHandler(::uiInterface.LoadWallet_connect([fn](std::shared_ptr<CWallet> wallet) { fn(MakeWallet(wallet)); }));
}
std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override
{
@@ -283,6 +293,7 @@ class NodeImpl : public Node
GuessVerificationProgress(Params().TxData(), block));
}));
}
+ InitInterfaces m_interfaces;
};
} // namespace
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 8185c015a9..1f8bbbff7a 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -173,6 +173,12 @@ 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;
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 566ae37509..672a557d41 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -7,12 +7,16 @@
#include <amount.h>
#include <chain.h>
#include <consensus/validation.h>
+#include <init.h>
+#include <interfaces/chain.h>
#include <interfaces/handler.h>
#include <net.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
+#include <rpc/server.h>
+#include <scheduler.h>
#include <script/ismine.h>
#include <script/standard.h>
#include <support/allocators/secure.h>
@@ -20,10 +24,18 @@
#include <timedata.h>
#include <ui_interface.h>
#include <uint256.h>
+#include <util/system.h>
#include <validation.h>
#include <wallet/feebumper.h>
#include <wallet/fees.h>
+#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
+#include <wallet/walletutil.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
namespace interfaces {
namespace {
@@ -41,7 +53,8 @@ public:
WalletOrderForm order_form,
std::string& reject_reason) override
{
- LOCK2(cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
CValidationState state;
if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, g_connman.get(), state)) {
reject_reason = state.GetRejectReason();
@@ -56,7 +69,7 @@ public:
};
//! Construct wallet tx struct.
-static WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, const CWalletTx& wtx)
{
WalletTx result;
result.tx = wtx.tx;
@@ -74,7 +87,7 @@ static WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) EXCLUSIVE_LO
IsMine(wallet, result.txout_address.back()) :
ISMINE_NO);
}
- result.credit = wtx.GetCredit(ISMINE_ALL);
+ result.credit = wtx.GetCredit(locked_chain, ISMINE_ALL);
result.debit = wtx.GetDebit(ISMINE_ALL);
result.change = wtx.GetChange();
result.time = wtx.GetTxTime();
@@ -84,32 +97,38 @@ static WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) EXCLUSIVE_LO
}
//! Construct wallet tx status struct.
-static WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx)
{
+ LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
+
WalletTxStatus result;
auto mi = ::mapBlockIndex.find(wtx.hashBlock);
CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr;
result.block_height = (block ? block->nHeight : std::numeric_limits<int>::max());
- result.blocks_to_maturity = wtx.GetBlocksToMaturity();
- result.depth_in_main_chain = wtx.GetDepthInMainChain();
+ result.blocks_to_maturity = wtx.GetBlocksToMaturity(locked_chain);
+ result.depth_in_main_chain = wtx.GetDepthInMainChain(locked_chain);
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
result.is_final = CheckFinalTx(*wtx.tx);
- result.is_trusted = wtx.IsTrusted();
+ result.is_trusted = wtx.IsTrusted(locked_chain);
result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase();
- result.is_in_main_chain = wtx.IsInMainChain();
+ result.is_in_main_chain = wtx.IsInMainChain(locked_chain);
return result;
}
//! Construct wallet TxOut struct.
-static WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+WalletTxOut MakeWalletTxOut(interfaces::Chain::Lock& locked_chain,
+ CWallet& wallet,
+ const CWalletTx& wtx,
+ int n,
+ int depth) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
WalletTxOut result;
result.txout = wtx.tx->vout[n];
result.time = wtx.GetTxTime();
result.depth_in_main_chain = depth;
- result.is_spent = wallet.IsSpent(wtx.GetHash(), n);
+ result.is_spent = wallet.IsSpent(locked_chain, wtx.GetHash(), n);
return result;
}
@@ -197,22 +216,26 @@ public:
}
void lockCoin(const COutPoint& output) override
{
- LOCK2(cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
return m_wallet.LockCoin(output);
}
void unlockCoin(const COutPoint& output) override
{
- LOCK2(cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
return m_wallet.UnlockCoin(output);
}
bool isLockedCoin(const COutPoint& output) override
{
- LOCK2(cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
return m_wallet.IsLockedCoin(output.hash, output.n);
}
void listLockedCoins(std::vector<COutPoint>& outputs) override
{
- LOCK2(cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
return m_wallet.ListLockedCoins(outputs);
}
std::unique_ptr<PendingWalletTx> createTransaction(const std::vector<CRecipient>& recipients,
@@ -222,9 +245,10 @@ public:
CAmount& fee,
std::string& fail_reason) override
{
- LOCK2(cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
auto pending = MakeUnique<PendingWalletTxImpl>(m_wallet);
- if (!m_wallet.CreateTransaction(recipients, pending->m_tx, pending->m_key, fee, change_pos,
+ if (!m_wallet.CreateTransaction(*locked_chain, recipients, pending->m_tx, pending->m_key, fee, change_pos,
fail_reason, coin_control, sign)) {
return {};
}
@@ -233,8 +257,9 @@ public:
bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet.TransactionCanBeAbandoned(txid); }
bool abandonTransaction(const uint256& txid) override
{
- LOCK2(cs_main, m_wallet.cs_wallet);
- return m_wallet.AbandonTransaction(txid);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
+ return m_wallet.AbandonTransaction(*locked_chain, txid);
}
bool transactionCanBeBumped(const uint256& txid) override
{
@@ -262,7 +287,8 @@ public:
}
CTransactionRef getTx(const uint256& txid) override
{
- LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
auto mi = m_wallet.mapWallet.find(txid);
if (mi != m_wallet.mapWallet.end()) {
return mi->second.tx;
@@ -271,29 +297,30 @@ public:
}
WalletTx getWalletTx(const uint256& txid) override
{
- LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
auto mi = m_wallet.mapWallet.find(txid);
if (mi != m_wallet.mapWallet.end()) {
- return MakeWalletTx(m_wallet, mi->second);
+ return MakeWalletTx(*locked_chain, m_wallet, mi->second);
}
return {};
}
std::vector<WalletTx> getWalletTxs() override
{
- LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
std::vector<WalletTx> result;
result.reserve(m_wallet.mapWallet.size());
for (const auto& entry : m_wallet.mapWallet) {
- result.emplace_back(MakeWalletTx(m_wallet, entry.second));
+ result.emplace_back(MakeWalletTx(*locked_chain, m_wallet, entry.second));
}
return result;
}
bool tryGetTxStatus(const uint256& txid,
interfaces::WalletTxStatus& tx_status,
- int& num_blocks,
- int64_t& adjusted_time) override
+ int& num_blocks) override
{
- TRY_LOCK(::cs_main, locked_chain);
+ auto locked_chain = m_wallet.chain().lock(true /* try_lock */);
if (!locked_chain) {
return false;
}
@@ -306,26 +333,24 @@ public:
return false;
}
num_blocks = ::chainActive.Height();
- adjusted_time = GetAdjustedTime();
- tx_status = MakeWalletTxStatus(mi->second);
+ tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
return true;
}
WalletTx getWalletTxDetails(const uint256& txid,
WalletTxStatus& tx_status,
WalletOrderForm& order_form,
bool& in_mempool,
- int& num_blocks,
- int64_t& adjusted_time) override
+ int& num_blocks) override
{
- LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
auto mi = m_wallet.mapWallet.find(txid);
if (mi != m_wallet.mapWallet.end()) {
num_blocks = ::chainActive.Height();
- adjusted_time = GetAdjustedTime();
in_mempool = mi->second.InMempool();
order_form = mi->second.vOrderForm;
- tx_status = MakeWalletTxStatus(mi->second);
- return MakeWalletTx(m_wallet, mi->second);
+ tx_status = MakeWalletTxStatus(*locked_chain, mi->second);
+ return MakeWalletTx(*locked_chain, m_wallet, mi->second);
}
return {};
}
@@ -345,7 +370,7 @@ public:
}
bool tryGetBalances(WalletBalances& balances, int& num_blocks) override
{
- TRY_LOCK(cs_main, locked_chain);
+ auto locked_chain = m_wallet.chain().lock(true /* try_lock */);
if (!locked_chain) return false;
TRY_LOCK(m_wallet.cs_wallet, locked_wallet);
if (!locked_wallet) {
@@ -362,49 +387,55 @@ public:
}
isminetype txinIsMine(const CTxIn& txin) override
{
- LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
return m_wallet.IsMine(txin);
}
isminetype txoutIsMine(const CTxOut& txout) override
{
- LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
return m_wallet.IsMine(txout);
}
CAmount getDebit(const CTxIn& txin, isminefilter filter) override
{
- LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
return m_wallet.GetDebit(txin, filter);
}
CAmount getCredit(const CTxOut& txout, isminefilter filter) override
{
- LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
return m_wallet.GetCredit(txout, filter);
}
CoinsList listCoins() override
{
- LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
CoinsList result;
- for (const auto& entry : m_wallet.ListCoins()) {
+ for (const auto& entry : m_wallet.ListCoins(*locked_chain)) {
auto& group = result[entry.first];
for (const auto& coin : entry.second) {
- group.emplace_back(
- COutPoint(coin.tx->GetHash(), coin.i), MakeWalletTxOut(m_wallet, *coin.tx, coin.i, coin.nDepth));
+ group.emplace_back(COutPoint(coin.tx->GetHash(), coin.i),
+ MakeWalletTxOut(*locked_chain, m_wallet, *coin.tx, coin.i, coin.nDepth));
}
}
return result;
}
std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) override
{
- LOCK2(::cs_main, m_wallet.cs_wallet);
+ auto locked_chain = m_wallet.chain().lock();
+ LOCK(m_wallet.cs_wallet);
std::vector<WalletTxOut> result;
result.reserve(outputs.size());
for (const auto& output : outputs) {
result.emplace_back();
auto it = m_wallet.mapWallet.find(output.hash);
if (it != m_wallet.mapWallet.end()) {
- int depth = it->second.GetDepthInMainChain();
+ int depth = it->second.GetDepthInMainChain(*locked_chain);
if (depth >= 0) {
- result.back() = MakeWalletTxOut(m_wallet, it->second, output.n, depth);
+ result.back() = MakeWalletTxOut(*locked_chain, m_wallet, it->second, output.n, depth);
}
}
}
@@ -460,8 +491,32 @@ public:
CWallet& m_wallet;
};
+class WalletClientImpl : public ChainClient
+{
+public:
+ WalletClientImpl(Chain& chain, std::vector<std::string> wallet_filenames)
+ : m_chain(chain), m_wallet_filenames(std::move(wallet_filenames))
+ {
+ }
+ void registerRpcs() override { return RegisterWalletRPCCommands(::tableRPC); }
+ bool verify() override { return VerifyWallets(m_chain, m_wallet_filenames); }
+ bool load() override { return LoadWallets(m_chain, m_wallet_filenames); }
+ void start(CScheduler& scheduler) override { return StartWallets(scheduler); }
+ void flush() override { return FlushWallets(); }
+ void stop() override { return StopWallets(); }
+ ~WalletClientImpl() override { UnloadWallets(); }
+
+ Chain& m_chain;
+ std::vector<std::string> m_wallet_filenames;
+};
+
} // namespace
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return MakeUnique<WalletImpl>(wallet); }
+std::unique_ptr<ChainClient> MakeWalletClient(Chain& chain, std::vector<std::string> wallet_filenames)
+{
+ return MakeUnique<WalletClientImpl>(chain, std::move(wallet_filenames));
+}
+
} // namespace interfaces
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 44240a5726..c79b9afce3 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -178,16 +178,14 @@ public:
//! Try to get updated status for a particular transaction, if possible without blocking.
virtual bool tryGetTxStatus(const uint256& txid,
WalletTxStatus& tx_status,
- int& num_blocks,
- int64_t& adjusted_time) = 0;
+ int& num_blocks) = 0;
//! Get transaction details.
virtual WalletTx getWalletTxDetails(const uint256& txid,
WalletTxStatus& tx_status,
WalletOrderForm& order_form,
bool& in_mempool,
- int& num_blocks,
- int64_t& adjusted_time) = 0;
+ int& num_blocks) = 0;
//! Get balances.
virtual WalletBalances getBalances() = 0;
@@ -366,8 +364,8 @@ struct WalletTxOut
bool is_spent = false;
};
-//! Return implementation of Wallet interface. This function will be undefined
-//! in builds where ENABLE_WALLET is false.
+//! Return implementation of Wallet interface. This function is defined in
+//! dummywallet.cpp and throws if the wallet component is not compiled.
std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet);
} // namespace interfaces
diff --git a/src/key.cpp b/src/key.cpp
index df452cd330..80d6589a3c 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -89,7 +89,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou
* will be set to the number of bytes used in the buffer.
* key32 must point to a 32-byte raw private key.
*/
-static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) {
+static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, bool compressed) {
assert(*privkeylen >= CKey::PRIVATE_KEY_SIZE);
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
@@ -170,7 +170,7 @@ CPrivKey CKey::GetPrivKey() const {
size_t privkeylen;
privkey.resize(PRIVATE_KEY_SIZE);
privkeylen = PRIVATE_KEY_SIZE;
- ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
+ ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed);
assert(ret);
privkey.resize(privkeylen);
return privkey;
diff --git a/src/key.h b/src/key.h
index a3baa421e6..0f695c07b7 100644
--- a/src/key.h
+++ b/src/key.h
@@ -181,12 +181,12 @@ struct CExtKey {
};
/** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */
-void ECC_Start(void);
+void ECC_Start();
/** Deinitialize the elliptic curve support. No-op if ECC_Start wasn't called first. */
-void ECC_Stop(void);
+void ECC_Stop();
/** Check that required EC support is available at runtime. */
-bool ECC_InitSanityCheck(void);
+bool ECC_InitSanityCheck();
#endif // BITCOIN_KEY_H
diff --git a/src/key_io.cpp b/src/key_io.cpp
index c6a541cdb1..282385f50d 100644
--- a/src/key_io.cpp
+++ b/src/key_io.cpp
@@ -7,7 +7,7 @@
#include <base58.h>
#include <bech32.h>
#include <script/script.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
diff --git a/src/keystore.cpp b/src/keystore.cpp
index b2012a04bb..148979cf35 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -5,7 +5,7 @@
#include <keystore.h>
-#include <util.h>
+#include <util/system.h>
void CBasicKeyStore::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
{
diff --git a/src/logging.cpp b/src/logging.cpp
index 0ae4f8121e..77dc2d0939 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -4,7 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <logging.h>
-#include <utiltime.h>
+#include <util/time.h>
const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
diff --git a/src/logging.h b/src/logging.h
index 6400b131c2..0c8e7f5291 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -125,42 +125,31 @@ std::vector<CLogCategoryActive> ListActiveLogCategories();
/** Return true if str parses as a log category and set the flag */
bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str);
-/** Get format string from VA_ARGS for error reporting */
-template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; }
-
-static inline void MarkUsed() {}
-template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args)
-{
- (void)t;
- MarkUsed(args...);
-}
-
// Be conservative when using LogPrintf/error or other things which
// unconditionally log to debug.log! It should not be the case that an inbound
// peer can fill up a user's disk with debug.log entries.
-#ifdef USE_COVERAGE
-#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0)
-#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0)
-#else
-#define LogPrintf(...) do { \
- if (g_logger->Enabled()) { \
- std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \
- try { \
- _log_msg_ = tfm::format(__VA_ARGS__); \
- } catch (tinyformat::format_error &fmterr) { \
- /* Original format string will have newline so don't add one here */ \
- _log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \
- } \
- g_logger->LogPrintStr(_log_msg_); \
- } \
-} while(0)
-
-#define LogPrint(category, ...) do { \
- if (LogAcceptCategory((category))) { \
- LogPrintf(__VA_ARGS__); \
- } \
-} while(0)
-#endif
+template <typename... Args>
+static inline void LogPrintf(const char* fmt, const Args&... args)
+{
+ if (g_logger->Enabled()) {
+ std::string log_msg;
+ try {
+ log_msg = tfm::format(fmt, args...);
+ } catch (tinyformat::format_error& fmterr) {
+ /* Original format string will have newline so don't add one here */
+ log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt;
+ }
+ g_logger->LogPrintStr(log_msg);
+ }
+}
+
+template <typename... Args>
+static inline void LogPrint(const BCLog::LogFlags& category, const Args&... args)
+{
+ if (LogAcceptCategory((category))) {
+ LogPrintf(args...);
+ }
+}
#endif // BITCOIN_LOGGING_H
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index 7bbc45d38a..0c37bab1f8 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -7,7 +7,7 @@
#include <hash.h>
#include <consensus/consensus.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids)
diff --git a/src/miner.cpp b/src/miner.cpp
index 6d35f9ac37..96c9cd6d2a 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -21,8 +21,8 @@
#include <primitives/transaction.h>
#include <script/standard.h>
#include <timedata.h>
-#include <util.h>
-#include <utilmoneystr.h>
+#include <util/system.h>
+#include <util/moneystr.h>
#include <validationinterface.h>
#include <algorithm>
@@ -70,9 +70,8 @@ static BlockAssembler::Options DefaultOptions()
// If -blockmaxweight is not given, limit to DEFAULT_BLOCK_MAX_WEIGHT
BlockAssembler::Options options;
options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
- if (gArgs.IsArgSet("-blockmintxfee")) {
- CAmount n = 0;
- ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n);
+ CAmount n = 0;
+ if (gArgs.IsArgSet("-blockmintxfee") && ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) {
options.blockMinFeeRate = CFeeRate(n);
} else {
options.blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
diff --git a/src/net.cpp b/src/net.cpp
index c51a1b4a74..b85a8c2c1d 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -18,7 +18,7 @@
#include <netbase.h>
#include <scheduler.h>
#include <ui_interface.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#ifdef WIN32
#include <string.h>
@@ -82,8 +82,8 @@ bool fDiscover = true;
bool fListen = true;
bool fRelayTxes = true;
CCriticalSection cs_mapLocalHost;
-std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
-static bool vfLimited[NET_MAX] = {};
+std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
+static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {};
std::string strSubVersion;
limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
@@ -715,7 +715,10 @@ void CNode::copyStats(CNodeStats &stats)
X(nRecvBytes);
}
X(fWhitelisted);
- X(minFeeFilter);
+ {
+ LOCK(cs_feeFilter);
+ X(minFeeFilter);
+ }
// It is common for nodes with good ping times to suddenly become lagged,
// due to a new block arriving or other large transfer.
@@ -874,16 +877,7 @@ const uint256& CNetMessage::GetMessageHash() const
return data_hash;
}
-
-
-
-
-
-
-
-
-// requires LOCK(cs_vSend)
-size_t CConnman::SocketSendData(CNode *pnode) const
+size_t CConnman::SocketSendData(CNode *pnode) const EXCLUSIVE_LOCKS_REQUIRED(pnode->cs_vSend)
{
auto it = pnode->vSendMsg.begin();
size_t nSentSize = 0;
@@ -1011,6 +1005,7 @@ bool CConnman::AttemptToEvictConnection()
continue;
if (node->fDisconnect)
continue;
+ LOCK(node->cs_filter);
NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
node->nLastBlockTime, node->nLastTXTime,
HasAllDesirableServiceFlags(node->nServices),
@@ -1153,310 +1148,322 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}
-void CConnman::ThreadSocketHandler()
+void CConnman::DisconnectNodes()
{
- unsigned int nPrevNodeCount = 0;
- while (!interruptNet)
{
- //
- // Disconnect nodes
- //
- {
- LOCK(cs_vNodes);
+ LOCK(cs_vNodes);
- if (!fNetworkActive) {
- // Disconnect any connected nodes
- for (CNode* pnode : vNodes) {
- if (!pnode->fDisconnect) {
- LogPrint(BCLog::NET, "Network not active, dropping peer=%d\n", pnode->GetId());
- pnode->fDisconnect = true;
- }
+ if (!fNetworkActive) {
+ // Disconnect any connected nodes
+ for (CNode* pnode : vNodes) {
+ if (!pnode->fDisconnect) {
+ LogPrint(BCLog::NET, "Network not active, dropping peer=%d\n", pnode->GetId());
+ pnode->fDisconnect = true;
}
}
+ }
- // Disconnect unused nodes
- std::vector<CNode*> vNodesCopy = vNodes;
- for (CNode* pnode : vNodesCopy)
+ // Disconnect unused nodes
+ std::vector<CNode*> vNodesCopy = vNodes;
+ for (CNode* pnode : vNodesCopy)
+ {
+ if (pnode->fDisconnect)
{
- if (pnode->fDisconnect)
- {
- // remove from vNodes
- vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
+ // remove from vNodes
+ vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
- // release outbound grant (if any)
- pnode->grantOutbound.Release();
+ // release outbound grant (if any)
+ pnode->grantOutbound.Release();
- // close socket and cleanup
- pnode->CloseSocketDisconnect();
+ // close socket and cleanup
+ pnode->CloseSocketDisconnect();
- // hold in disconnected pool until all refs are released
- pnode->Release();
- vNodesDisconnected.push_back(pnode);
- }
+ // hold in disconnected pool until all refs are released
+ pnode->Release();
+ vNodesDisconnected.push_back(pnode);
}
}
+ }
+ {
+ // Delete disconnected nodes
+ std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
+ for (CNode* pnode : vNodesDisconnectedCopy)
{
- // Delete disconnected nodes
- std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
- for (CNode* pnode : vNodesDisconnectedCopy)
- {
- // wait until threads are done using it
- if (pnode->GetRefCount() <= 0) {
- bool fDelete = false;
- {
- TRY_LOCK(pnode->cs_inventory, lockInv);
- if (lockInv) {
- TRY_LOCK(pnode->cs_vSend, lockSend);
- if (lockSend) {
- fDelete = true;
- }
+ // wait until threads are done using it
+ if (pnode->GetRefCount() <= 0) {
+ bool fDelete = false;
+ {
+ TRY_LOCK(pnode->cs_inventory, lockInv);
+ if (lockInv) {
+ TRY_LOCK(pnode->cs_vSend, lockSend);
+ if (lockSend) {
+ fDelete = true;
}
}
- if (fDelete) {
- vNodesDisconnected.remove(pnode);
- DeleteNode(pnode);
- }
+ }
+ if (fDelete) {
+ vNodesDisconnected.remove(pnode);
+ DeleteNode(pnode);
}
}
}
- size_t vNodesSize;
+ }
+}
+
+void CConnman::NotifyNumConnectionsChanged()
+{
+ size_t vNodesSize;
+ {
+ LOCK(cs_vNodes);
+ vNodesSize = vNodes.size();
+ }
+ if(vNodesSize != nPrevNodeCount) {
+ nPrevNodeCount = vNodesSize;
+ if(clientInterface)
+ clientInterface->NotifyNumConnectionsChanged(vNodesSize);
+ }
+}
+
+void CConnman::InactivityCheck(CNode *pnode)
+{
+ int64_t nTime = GetSystemTimeInSeconds();
+ if (nTime - pnode->nTimeConnected > 60)
+ {
+ if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
{
- LOCK(cs_vNodes);
- vNodesSize = vNodes.size();
+ LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId());
+ pnode->fDisconnect = true;
+ }
+ else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
+ {
+ LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend);
+ pnode->fDisconnect = true;
+ }
+ else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60))
+ {
+ LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv);
+ pnode->fDisconnect = true;
}
- if(vNodesSize != nPrevNodeCount) {
- nPrevNodeCount = vNodesSize;
- if(clientInterface)
- clientInterface->NotifyNumConnectionsChanged(vNodesSize);
+ else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros())
+ {
+ LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart));
+ pnode->fDisconnect = true;
}
+ else if (!pnode->fSuccessfullyConnected)
+ {
+ LogPrint(BCLog::NET, "version handshake timeout from %d\n", pnode->GetId());
+ pnode->fDisconnect = true;
+ }
+ }
+}
- //
- // Find which sockets have data to receive
- //
- struct timeval timeout;
- timeout.tv_sec = 0;
- timeout.tv_usec = 50000; // frequency to poll pnode->vSend
-
- fd_set fdsetRecv;
- fd_set fdsetSend;
- fd_set fdsetError;
- FD_ZERO(&fdsetRecv);
- FD_ZERO(&fdsetSend);
- FD_ZERO(&fdsetError);
- SOCKET hSocketMax = 0;
- bool have_fds = false;
+void CConnman::SocketHandler()
+{
+ //
+ // Find which sockets have data to receive
+ //
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 50000; // frequency to poll pnode->vSend
- for (const ListenSocket& hListenSocket : vhListenSocket) {
- FD_SET(hListenSocket.socket, &fdsetRecv);
- hSocketMax = std::max(hSocketMax, hListenSocket.socket);
- have_fds = true;
- }
+ fd_set fdsetRecv;
+ fd_set fdsetSend;
+ fd_set fdsetError;
+ FD_ZERO(&fdsetRecv);
+ FD_ZERO(&fdsetSend);
+ FD_ZERO(&fdsetError);
+ SOCKET hSocketMax = 0;
+ bool have_fds = false;
+
+ for (const ListenSocket& hListenSocket : vhListenSocket) {
+ FD_SET(hListenSocket.socket, &fdsetRecv);
+ hSocketMax = std::max(hSocketMax, hListenSocket.socket);
+ have_fds = true;
+ }
+ {
+ LOCK(cs_vNodes);
+ for (CNode* pnode : vNodes)
{
- LOCK(cs_vNodes);
- for (CNode* pnode : vNodes)
+ // Implement the following logic:
+ // * If there is data to send, select() for sending data. As this only
+ // happens when optimistic write failed, we choose to first drain the
+ // write buffer in this case before receiving more. This avoids
+ // needlessly queueing received data, if the remote peer is not themselves
+ // receiving data. This means properly utilizing TCP flow control signalling.
+ // * Otherwise, if there is space left in the receive buffer, select() for
+ // receiving data.
+ // * Hand off all complete messages to the processor, to be handled without
+ // blocking here.
+
+ bool select_recv = !pnode->fPauseRecv;
+ bool select_send;
{
- // Implement the following logic:
- // * If there is data to send, select() for sending data. As this only
- // happens when optimistic write failed, we choose to first drain the
- // write buffer in this case before receiving more. This avoids
- // needlessly queueing received data, if the remote peer is not themselves
- // receiving data. This means properly utilizing TCP flow control signalling.
- // * Otherwise, if there is space left in the receive buffer, select() for
- // receiving data.
- // * Hand off all complete messages to the processor, to be handled without
- // blocking here.
-
- bool select_recv = !pnode->fPauseRecv;
- bool select_send;
- {
- LOCK(pnode->cs_vSend);
- select_send = !pnode->vSendMsg.empty();
- }
+ LOCK(pnode->cs_vSend);
+ select_send = !pnode->vSendMsg.empty();
+ }
- LOCK(pnode->cs_hSocket);
- if (pnode->hSocket == INVALID_SOCKET)
- continue;
+ LOCK(pnode->cs_hSocket);
+ if (pnode->hSocket == INVALID_SOCKET)
+ continue;
- FD_SET(pnode->hSocket, &fdsetError);
- hSocketMax = std::max(hSocketMax, pnode->hSocket);
- have_fds = true;
+ FD_SET(pnode->hSocket, &fdsetError);
+ hSocketMax = std::max(hSocketMax, pnode->hSocket);
+ have_fds = true;
- if (select_send) {
- FD_SET(pnode->hSocket, &fdsetSend);
- continue;
- }
- if (select_recv) {
- FD_SET(pnode->hSocket, &fdsetRecv);
- }
+ if (select_send) {
+ FD_SET(pnode->hSocket, &fdsetSend);
+ continue;
+ }
+ if (select_recv) {
+ FD_SET(pnode->hSocket, &fdsetRecv);
}
}
+ }
- int nSelect = select(have_fds ? hSocketMax + 1 : 0,
- &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
- if (interruptNet)
- return;
+ int nSelect = select(have_fds ? hSocketMax + 1 : 0,
+ &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
+ if (interruptNet)
+ return;
- if (nSelect == SOCKET_ERROR)
+ if (nSelect == SOCKET_ERROR)
+ {
+ if (have_fds)
{
- if (have_fds)
- {
- int nErr = WSAGetLastError();
- LogPrintf("socket select error %s\n", NetworkErrorString(nErr));
- for (unsigned int i = 0; i <= hSocketMax; i++)
- FD_SET(i, &fdsetRecv);
- }
- FD_ZERO(&fdsetSend);
- FD_ZERO(&fdsetError);
- if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000)))
- return;
+ int nErr = WSAGetLastError();
+ LogPrintf("socket select error %s\n", NetworkErrorString(nErr));
+ for (unsigned int i = 0; i <= hSocketMax; i++)
+ FD_SET(i, &fdsetRecv);
}
+ FD_ZERO(&fdsetSend);
+ FD_ZERO(&fdsetError);
+ if (!interruptNet.sleep_for(std::chrono::milliseconds(timeout.tv_usec/1000)))
+ return;
+ }
- //
- // Accept new connections
- //
- for (const ListenSocket& hListenSocket : vhListenSocket)
+ //
+ // Accept new connections
+ //
+ for (const ListenSocket& hListenSocket : vhListenSocket)
+ {
+ if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
{
- if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
- {
- AcceptConnection(hListenSocket);
- }
+ AcceptConnection(hListenSocket);
}
+ }
+
+ //
+ // Service each socket
+ //
+ std::vector<CNode*> vNodesCopy;
+ {
+ LOCK(cs_vNodes);
+ vNodesCopy = vNodes;
+ for (CNode* pnode : vNodesCopy)
+ pnode->AddRef();
+ }
+ for (CNode* pnode : vNodesCopy)
+ {
+ if (interruptNet)
+ return;
//
- // Service each socket
+ // Receive
//
- std::vector<CNode*> vNodesCopy;
+ bool recvSet = false;
+ bool sendSet = false;
+ bool errorSet = false;
{
- LOCK(cs_vNodes);
- vNodesCopy = vNodes;
- for (CNode* pnode : vNodesCopy)
- pnode->AddRef();
+ LOCK(pnode->cs_hSocket);
+ if (pnode->hSocket == INVALID_SOCKET)
+ continue;
+ recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv);
+ sendSet = FD_ISSET(pnode->hSocket, &fdsetSend);
+ errorSet = FD_ISSET(pnode->hSocket, &fdsetError);
}
- for (CNode* pnode : vNodesCopy)
+ if (recvSet || errorSet)
{
- if (interruptNet)
- return;
-
- //
- // Receive
- //
- bool recvSet = false;
- bool sendSet = false;
- bool errorSet = false;
+ // typical socket buffer is 8K-64K
+ char pchBuf[0x10000];
+ int nBytes = 0;
{
LOCK(pnode->cs_hSocket);
if (pnode->hSocket == INVALID_SOCKET)
continue;
- recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv);
- sendSet = FD_ISSET(pnode->hSocket, &fdsetSend);
- errorSet = FD_ISSET(pnode->hSocket, &fdsetError);
+ nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
}
- if (recvSet || errorSet)
+ if (nBytes > 0)
{
- // typical socket buffer is 8K-64K
- char pchBuf[0x10000];
- int nBytes = 0;
- {
- LOCK(pnode->cs_hSocket);
- if (pnode->hSocket == INVALID_SOCKET)
- continue;
- nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
- }
- if (nBytes > 0)
- {
- bool notify = false;
- if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
- pnode->CloseSocketDisconnect();
- RecordBytesRecv(nBytes);
- if (notify) {
- size_t nSizeAdded = 0;
- auto it(pnode->vRecvMsg.begin());
- for (; it != pnode->vRecvMsg.end(); ++it) {
- if (!it->complete())
- break;
- nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE;
- }
- {
- LOCK(pnode->cs_vProcessMsg);
- pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);
- pnode->nProcessQueueSize += nSizeAdded;
- pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;
- }
- WakeMessageHandler();
- }
- }
- else if (nBytes == 0)
- {
- // socket closed gracefully
- if (!pnode->fDisconnect) {
- LogPrint(BCLog::NET, "socket closed\n");
- }
+ bool notify = false;
+ if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
pnode->CloseSocketDisconnect();
- }
- else if (nBytes < 0)
- {
- // error
- int nErr = WSAGetLastError();
- if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
+ RecordBytesRecv(nBytes);
+ if (notify) {
+ size_t nSizeAdded = 0;
+ auto it(pnode->vRecvMsg.begin());
+ for (; it != pnode->vRecvMsg.end(); ++it) {
+ if (!it->complete())
+ break;
+ nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE;
+ }
{
- if (!pnode->fDisconnect)
- LogPrintf("socket recv error %s\n", NetworkErrorString(nErr));
- pnode->CloseSocketDisconnect();
+ LOCK(pnode->cs_vProcessMsg);
+ pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);
+ pnode->nProcessQueueSize += nSizeAdded;
+ pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;
}
+ WakeMessageHandler();
}
}
-
- //
- // Send
- //
- if (sendSet)
+ else if (nBytes == 0)
{
- LOCK(pnode->cs_vSend);
- size_t nBytes = SocketSendData(pnode);
- if (nBytes) {
- RecordBytesSent(nBytes);
+ // socket closed gracefully
+ if (!pnode->fDisconnect) {
+ LogPrint(BCLog::NET, "socket closed\n");
}
+ pnode->CloseSocketDisconnect();
}
-
- //
- // Inactivity checking
- //
- int64_t nTime = GetSystemTimeInSeconds();
- if (nTime - pnode->nTimeConnected > 60)
+ else if (nBytes < 0)
{
- if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
- {
- LogPrint(BCLog::NET, "socket no message in first 60 seconds, %d %d from %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0, pnode->GetId());
- pnode->fDisconnect = true;
- }
- else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
- {
- LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend);
- pnode->fDisconnect = true;
- }
- else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60))
- {
- LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv);
- pnode->fDisconnect = true;
- }
- else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros())
- {
- LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart));
- pnode->fDisconnect = true;
- }
- else if (!pnode->fSuccessfullyConnected)
+ // error
+ int nErr = WSAGetLastError();
+ if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS)
{
- LogPrint(BCLog::NET, "version handshake timeout from %d\n", pnode->GetId());
- pnode->fDisconnect = true;
+ if (!pnode->fDisconnect)
+ LogPrintf("socket recv error %s\n", NetworkErrorString(nErr));
+ pnode->CloseSocketDisconnect();
}
}
}
+
+ //
+ // Send
+ //
+ if (sendSet)
{
- LOCK(cs_vNodes);
- for (CNode* pnode : vNodesCopy)
- pnode->Release();
+ LOCK(pnode->cs_vSend);
+ size_t nBytes = SocketSendData(pnode);
+ if (nBytes) {
+ RecordBytesSent(nBytes);
+ }
}
+
+ InactivityCheck(pnode);
+ }
+ {
+ LOCK(cs_vNodes);
+ for (CNode* pnode : vNodesCopy)
+ pnode->Release();
+ }
+}
+
+void CConnman::ThreadSocketHandler()
+{
+ while (!interruptNet)
+ {
+ DisconnectNodes();
+ NotifyNumConnectionsChanged();
+ SocketHandler();
}
}
@@ -2167,7 +2174,7 @@ void Discover()
}
}
}
-#else
+#elif (HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS)
// Get local host ip
struct ifaddrs* myaddrs;
if (getifaddrs(&myaddrs) == 0)
@@ -2217,6 +2224,7 @@ CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSe
setBannedIsDirty = false;
fAddressesInitialized = false;
nLastNodeId = 0;
+ nPrevNodeCount = 0;
nSendBufferMaxSize = 0;
nReceiveFloodSize = 0;
flagInterruptMsgProc = false;
diff --git a/src/net.h b/src/net.h
index 9f6c426ab7..a059d32c89 100644
--- a/src/net.h
+++ b/src/net.h
@@ -11,6 +11,7 @@
#include <amount.h>
#include <bloom.h>
#include <compat.h>
+#include <crypto/siphash.h>
#include <hash.h>
#include <limitedmap.h>
#include <netaddress.h>
@@ -149,6 +150,7 @@ public:
nLocalServices = connOptions.nLocalServices;
nMaxConnections = connOptions.nMaxConnections;
nMaxOutbound = std::min(connOptions.nMaxOutbound, connOptions.nMaxConnections);
+ m_use_addrman_outgoing = connOptions.m_use_addrman_outgoing;
nMaxAddnode = connOptions.nMaxAddnode;
nMaxFeeler = connOptions.nMaxFeeler;
nBestHeight = connOptions.nBestHeight;
@@ -174,6 +176,7 @@ public:
void Stop();
void Interrupt();
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, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false);
bool CheckIncomingNonce(uint64_t nonce);
@@ -336,6 +339,10 @@ private:
void ThreadOpenConnections(std::vector<std::string> connect);
void ThreadMessageHandler();
void AcceptConnection(const ListenSocket& hListenSocket);
+ void DisconnectNodes();
+ void NotifyNumConnectionsChanged();
+ void InactivityCheck(CNode *pnode);
+ void SocketHandler();
void ThreadSocketHandler();
void ThreadDNSAddressSeed();
@@ -393,12 +400,12 @@ private:
std::vector<ListenSocket> vhListenSocket;
std::atomic<bool> fNetworkActive;
- banmap_t setBanned;
+ banmap_t setBanned GUARDED_BY(cs_setBanned);
CCriticalSection cs_setBanned;
- bool setBannedIsDirty;
+ bool setBannedIsDirty GUARDED_BY(cs_setBanned);
bool fAddressesInitialized;
CAddrMan addrman;
- std::deque<std::string> vOneShots;
+ std::deque<std::string> vOneShots GUARDED_BY(cs_vOneShots);
CCriticalSection cs_vOneShots;
std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes);
CCriticalSection cs_vAddedNodes;
@@ -406,6 +413,7 @@ private:
std::list<CNode*> vNodesDisconnected;
mutable CCriticalSection cs_vNodes;
std::atomic<NodeId> nLastNodeId;
+ unsigned int nPrevNodeCount;
/** Services this instance offers */
ServiceFlags nLocalServices;
@@ -416,6 +424,7 @@ private:
int nMaxOutbound;
int nMaxAddnode;
int nMaxFeeler;
+ bool m_use_addrman_outgoing;
std::atomic<int> nBestHeight;
CClientUIInterface* clientInterface;
NetEventsInterface* m_msgproc;
@@ -531,7 +540,7 @@ struct LocalServiceInfo {
};
extern CCriticalSection cs_mapLocalHost;
-extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
+extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes
class CNodeStats
@@ -621,23 +630,23 @@ class CNode
public:
// socket
std::atomic<ServiceFlags> nServices;
- SOCKET hSocket;
+ SOCKET hSocket GUARDED_BY(cs_hSocket);
size_t nSendSize; // total size of all vSendMsg entries
size_t nSendOffset; // offset inside the first vSendMsg already sent
- uint64_t nSendBytes;
- std::deque<std::vector<unsigned char>> vSendMsg;
+ uint64_t nSendBytes GUARDED_BY(cs_vSend);
+ std::deque<std::vector<unsigned char>> vSendMsg GUARDED_BY(cs_vSend);
CCriticalSection cs_vSend;
CCriticalSection cs_hSocket;
CCriticalSection cs_vRecv;
CCriticalSection cs_vProcessMsg;
- std::list<CNetMessage> vProcessMsg;
+ std::list<CNetMessage> vProcessMsg GUARDED_BY(cs_vProcessMsg);
size_t nProcessQueueSize;
CCriticalSection cs_sendProcessing;
std::deque<CInv> vRecvGetData;
- uint64_t nRecvBytes;
+ uint64_t nRecvBytes GUARDED_BY(cs_vRecv);
std::atomic<int> nRecvVersion;
std::atomic<int64_t> nLastSend;
@@ -653,7 +662,7 @@ public:
// to be printed out, displayed to humans in various forms and so on. So we sanitize it and
// store the sanitized version in cleanSubVer. The original should be used when dealing with
// the network or wire types and the cleaned string used when displayed or logged.
- std::string strSubVer, cleanSubVer;
+ std::string strSubVer GUARDED_BY(cs_SubVer), cleanSubVer GUARDED_BY(cs_SubVer);
CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer
bool fWhitelisted; // This peer can bypass DoS banning.
bool fFeeler; // If true this node is being used as a short lived feeler.
@@ -668,11 +677,11 @@ public:
// a) it allows us to not relay tx invs before receiving the peer's version message
// b) the peer may tell us in its version message that we should not relay tx invs
// unless it loads a bloom filter.
- bool fRelayTxes; //protected by cs_filter
+ bool fRelayTxes GUARDED_BY(cs_filter);
bool fSentAddr;
CSemaphoreGrant grantOutbound;
- CCriticalSection cs_filter;
- std::unique_ptr<CBloomFilter> pfilter;
+ mutable CCriticalSection cs_filter;
+ std::unique_ptr<CBloomFilter> pfilter PT_GUARDED_BY(cs_filter);
std::atomic<int> nRefCount;
const uint64_t nKeyedNetGroup;
@@ -681,7 +690,7 @@ public:
protected:
mapMsgCmdSize mapSendBytesPerMsgCmd;
- mapMsgCmdSize mapRecvBytesPerMsgCmd;
+ mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv);
public:
uint256 hashContinue;
@@ -692,27 +701,26 @@ public:
CRollingBloomFilter addrKnown;
bool fGetAddr;
std::set<uint256> setKnown;
- int64_t nNextAddrSend;
- int64_t nNextLocalAddrSend;
+ int64_t nNextAddrSend GUARDED_BY(cs_sendProcessing);
+ int64_t nNextLocalAddrSend GUARDED_BY(cs_sendProcessing);
// inventory based relay
- CRollingBloomFilter filterInventoryKnown;
+ CRollingBloomFilter filterInventoryKnown GUARDED_BY(cs_inventory);
// Set of transaction ids we still have to announce.
// They are sorted by the mempool before relay, so the order is not important.
std::set<uint256> setInventoryTxToSend;
// 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.
- std::vector<uint256> vInventoryBlockToSend;
+ std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory);
CCriticalSection cs_inventory;
std::set<uint256> setAskFor;
std::multimap<int64_t, CInv> mapAskFor;
int64_t nNextInvSend;
// Used for headers announcements - unfiltered blocks to relay
- // Also protected by cs_inventory
- std::vector<uint256> vBlockHashesToAnnounce;
- // Used for BIP35 mempool sending, also protected by cs_inventory
- bool fSendMempool;
+ std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
+ // Used for BIP35 mempool sending
+ bool fSendMempool GUARDED_BY(cs_inventory);
// Last time a "MEMPOOL" request was serviced.
std::atomic<int64_t> timeLastMempoolReq;
@@ -733,7 +741,7 @@ public:
// Whether a ping is requested.
std::atomic<bool> fPingQueued;
// Minimum fee rate with which to filter inv's to this node
- CAmount minFeeFilter;
+ CAmount minFeeFilter GUARDED_BY(cs_feeFilter);
CCriticalSection cs_feeFilter;
CAmount lastSentFeeFilter;
int64_t nextSendTimeFeeFilter;
@@ -753,10 +761,10 @@ private:
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
mutable CCriticalSection cs_addrName;
- std::string addrName;
+ std::string addrName GUARDED_BY(cs_addrName);
// Our address, as reported by the peer
- CService addrLocal;
+ CService addrLocal GUARDED_BY(cs_addrLocal);
mutable CCriticalSection cs_addrLocal;
public:
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index b48a3bd221..f4ab3aa153 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -25,9 +25,9 @@
#include <tinyformat.h>
#include <txmempool.h>
#include <ui_interface.h>
-#include <util.h>
-#include <utilmoneystr.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/moneystr.h>
+#include <util/strencodings.h>
#include <memory>
@@ -2357,6 +2357,23 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
for (const CTransactionRef& removedTx : lRemovedTxn)
AddToCompactExtraTransactions(removedTx);
+ // If a tx has been detected by recentRejects, we will have reached
+ // this point and the tx will have been ignored. Because we haven't run
+ // the tx through AcceptToMemoryPool, we won't have computed a DoS
+ // score for it or determined exactly why we consider it invalid.
+ //
+ // This means we won't penalize any peer subsequently relaying a DoSy
+ // tx (even if we penalized the first peer who gave it to us) because
+ // we have to account for recentRejects showing false positives. In
+ // other words, we shouldn't penalize a peer if we aren't *sure* they
+ // submitted a DoSy tx.
+ //
+ // Note that recentRejects doesn't just record DoSy or invalid
+ // transactions, but any tx not accepted by the mempool, which may be
+ // due to node policy (vs. consensus). So we can't blanket penalize a
+ // peer simply for relaying a tx that our recentRejects has caught,
+ // regardless of false positives.
+
int nDoS = 0;
if (state.IsInvalid(nDoS))
{
@@ -3144,8 +3161,6 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
NodeId worst_peer = -1;
int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
- LOCK(cs_main);
-
connman->ForEachNode([&](CNode* pnode) {
AssertLockHeld(cs_main);
@@ -3193,6 +3208,8 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams)
{
+ LOCK(cs_main);
+
if (connman == nullptr) return;
int64_t time_in_seconds = GetTime();
@@ -3200,10 +3217,9 @@ void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params
EvictExtraOutboundPeers(time_in_seconds);
if (time_in_seconds > m_stale_tip_check_time) {
- LOCK(cs_main);
// Check whether our tip is stale, and if so, allow using an extra
// outbound peer
- if (TipMayBeStale(consensusParams)) {
+ if (!fImporting && !fReindex && connman->GetNetworkActive() && connman->GetUseAddrmanOutgoing() && TipMayBeStale(consensusParams)) {
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);
connman->SetTryNewOutboundPeer(true);
} else if (connman->GetTryNewOutboundPeer()) {
diff --git a/src/net_processing.h b/src/net_processing.h
index f16d00032e..0113e25f7e 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -9,13 +9,16 @@
#include <net.h>
#include <validationinterface.h>
#include <consensus/params.h>
+#include <sync.h>
+
+extern CCriticalSection cs_main;
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
/** Default number of orphan+recently-replaced txn to keep around for block reconstruction */
static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100;
/** Default for BIP61 (sending reject messages) */
-static constexpr bool DEFAULT_ENABLE_BIP61 = true;
+static constexpr bool DEFAULT_ENABLE_BIP61{false};
class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface {
private:
@@ -65,10 +68,10 @@ public:
/** Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound */
void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams);
/** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */
- void EvictExtraOutboundPeers(int64_t time_in_seconds);
+ void EvictExtraOutboundPeers(int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
private:
- int64_t m_stale_tip_check_time; //! Next time to check for stale tip
+ int64_t m_stale_tip_check_time; //!< Next time to check for stale tip
/** Enable BIP61 (sending reject messages) */
const bool m_enable_bip61;
diff --git a/src/netaddress.cpp b/src/netaddress.cpp
index 778c2700f9..a0c7f8e3c2 100644
--- a/src/netaddress.cpp
+++ b/src/netaddress.cpp
@@ -5,7 +5,7 @@
#include <netaddress.h>
#include <hash.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <tinyformat.h>
static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
@@ -17,7 +17,6 @@ static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87,
CNetAddr::CNetAddr()
{
memset(ip, 0, sizeof(ip));
- scopeId = 0;
}
void CNetAddr::SetIP(const CNetAddr& ipIn)
@@ -83,6 +82,16 @@ unsigned int CNetAddr::GetByte(int n) const
return ip[15-n];
}
+bool CNetAddr::IsBindAny() const
+{
+ const int cmplen = IsIPv4() ? 4 : 16;
+ for (int i = 0; i < cmplen; ++i) {
+ if (GetByte(i)) return false;
+ }
+
+ return true;
+}
+
bool CNetAddr::IsIPv4() const
{
return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0);
diff --git a/src/netaddress.h b/src/netaddress.h
index cc0e4d4f12..ca435d17dc 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -33,7 +33,7 @@ class CNetAddr
{
protected:
unsigned char ip[16]; // in network byte order
- uint32_t scopeId; // for scoped/link-local ipv6 addresses
+ uint32_t scopeId{0}; // for scoped/link-local ipv6 addresses
public:
CNetAddr();
@@ -55,6 +55,7 @@ class CNetAddr
bool SetInternal(const std::string& name);
bool SetSpecial(const std::string &strName); // for Tor addresses
+ bool IsBindAny() const; // INADDR_ANY equivalent
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 4b63757f3d..1c043fc981 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -10,13 +10,15 @@
#include <uint256.h>
#include <random.h>
#include <tinyformat.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <atomic>
#ifndef WIN32
#include <fcntl.h>
+#else
+#include <codecvt>
#endif
#if !defined(MSG_NOSIGNAL)
@@ -24,9 +26,9 @@
#endif
// Settings
-static proxyType proxyInfo[NET_MAX];
-static proxyType nameProxy;
static CCriticalSection cs_proxyInfos;
+static proxyType proxyInfo[NET_MAX] GUARDED_BY(cs_proxyInfos);
+static proxyType nameProxy GUARDED_BY(cs_proxyInfos);
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;
@@ -189,10 +191,10 @@ enum SOCKSVersion: uint8_t {
/** Values defined for METHOD in RFC1928 */
enum SOCKS5Method: uint8_t {
- NOAUTH = 0x00, //! No authentication required
- GSSAPI = 0x01, //! GSSAPI
- USER_PASS = 0x02, //! Username/password
- NO_ACCEPTABLE = 0xff, //! No acceptable methods
+ NOAUTH = 0x00, //!< No authentication required
+ GSSAPI = 0x01, //!< GSSAPI
+ USER_PASS = 0x02, //!< Username/password
+ NO_ACCEPTABLE = 0xff, //!< No acceptable methods
};
/** Values defined for CMD in RFC1928 */
@@ -204,15 +206,15 @@ enum SOCKS5Command: uint8_t {
/** Values defined for REP in RFC1928 */
enum SOCKS5Reply: uint8_t {
- SUCCEEDED = 0x00, //! Succeeded
- GENFAILURE = 0x01, //! General failure
- NOTALLOWED = 0x02, //! Connection not allowed by ruleset
- NETUNREACHABLE = 0x03, //! Network unreachable
- HOSTUNREACHABLE = 0x04, //! Network unreachable
- CONNREFUSED = 0x05, //! Connection refused
- TTLEXPIRED = 0x06, //! TTL expired
- CMDUNSUPPORTED = 0x07, //! Command not supported
- ATYPEUNSUPPORTED = 0x08, //! Address type not supported
+ SUCCEEDED = 0x00, //!< Succeeded
+ GENFAILURE = 0x01, //!< General failure
+ NOTALLOWED = 0x02, //!< Connection not allowed by ruleset
+ NETUNREACHABLE = 0x03, //!< Network unreachable
+ HOSTUNREACHABLE = 0x04, //!< Network unreachable
+ CONNREFUSED = 0x05, //!< Connection refused
+ TTLEXPIRED = 0x06, //!< TTL expired
+ CMDUNSUPPORTED = 0x07, //!< Command not supported
+ ATYPEUNSUPPORTED = 0x08, //!< Address type not supported
};
/** Values defined for ATYPE in RFC1928 */
@@ -649,13 +651,13 @@ bool LookupSubNet(const char* pszName, CSubNet& ret)
#ifdef WIN32
std::string NetworkErrorString(int err)
{
- char buf[256];
+ wchar_t buf[256];
buf[0] = 0;
- if(FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- buf, sizeof(buf), nullptr))
+ buf, ARRAYSIZE(buf), nullptr))
{
- return strprintf("%s (%d)", buf, err);
+ return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);
}
else
{
diff --git a/src/noui.cpp b/src/noui.cpp
index 3a1ec2d050..c7d8fee0ba 100644
--- a/src/noui.cpp
+++ b/src/noui.cpp
@@ -6,7 +6,7 @@
#include <noui.h>
#include <ui_interface.h>
-#include <util.h>
+#include <util/system.h>
#include <cstdio>
#include <stdint.h>
@@ -14,7 +14,7 @@
#include <boost/signals2/connection.hpp>
-static bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style)
+bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style)
{
bool fSecure = style & CClientUIInterface::SECURE;
style &= ~CClientUIInterface::SECURE;
@@ -41,19 +41,18 @@ static bool noui_ThreadSafeMessageBox(const std::string& message, const std::str
return false;
}
-static bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
+bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
{
return noui_ThreadSafeMessageBox(message, caption, style);
}
-static void noui_InitMessage(const std::string& message)
+void noui_InitMessage(const std::string& message)
{
LogPrintf("init message: %s\n", message);
}
void noui_connect()
{
- // Connect bitcoind signal handlers
uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
uiInterface.InitMessage_connect(noui_InitMessage);
diff --git a/src/noui.h b/src/noui.h
index ff16cc9aa8..169c2bbd7f 100644
--- a/src/noui.h
+++ b/src/noui.h
@@ -5,6 +5,16 @@
#ifndef BITCOIN_NOUI_H
#define BITCOIN_NOUI_H
-extern void noui_connect();
+#include <string>
+
+/** Non-GUI handler, which logs and prints messages. */
+bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style);
+/** Non-GUI handler, which logs and prints questions. */
+bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style);
+/** Non-GUI handler, which only logs a message. */
+void noui_InitMessage(const std::string& message);
+
+/** Connect all bitcoind signal handlers */
+void noui_connect();
#endif // BITCOIN_NOUI_H
diff --git a/src/outputtype.h b/src/outputtype.h
index 4c4d93bc8b..6c30fd1950 100644
--- a/src/outputtype.h
+++ b/src/outputtype.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_OUTPUTTYPE_H
#define BITCOIN_OUTPUTTYPE_H
+#include <attributes.h>
#include <keystore.h>
#include <script/standard.h>
@@ -26,7 +27,7 @@ enum class OutputType {
CHANGE_AUTO,
};
-bool ParseOutputType(const std::string& str, OutputType& output_type);
+NODISCARD bool ParseOutputType(const std::string& str, OutputType& output_type);
const std::string& FormatOutputType(OutputType type);
/**
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index aee6fbee1a..3afe6fe1b7 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -10,7 +10,7 @@
#include <primitives/transaction.h>
#include <streams.h>
#include <txmempool.h>
-#include <util.h>
+#include <util/system.h>
static constexpr double INF_FEERATE = 1e99;
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 2733c5a7de..90f159b48c 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -50,9 +50,9 @@ std::string StringForFeeReason(FeeReason reason);
/* Used to determine type of fee estimation requested */
enum class FeeEstimateMode {
- UNSET, //! Use default settings based on other criteria
- ECONOMICAL, //! Force estimateSmartFee to use non-conservative estimates
- CONSERVATIVE, //! Force estimateSmartFee to use conservative estimates
+ UNSET, //!< Use default settings based on other criteria
+ ECONOMICAL, //!< Force estimateSmartFee to use non-conservative estimates
+ CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
};
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 47d6644551..d4cc538492 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -11,8 +11,8 @@
#include <validation.h>
#include <coins.h>
#include <tinyformat.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
@@ -34,7 +34,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
if (txout.scriptPubKey.IsUnspendable())
return 0;
- size_t nSize = GetSerializeSize(txout, SER_DISK, 0);
+ size_t nSize = GetSerializeSize(txout);
int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 18f9c0c2a8..0dc130d104 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -7,7 +7,7 @@
bool SignalsOptInRBF(const CTransaction &tx)
{
for (const CTxIn &txin : tx.vin) {
- if (txin.nSequence < std::numeric_limits<unsigned int>::max()-1) {
+ if (txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) {
return true;
}
}
diff --git a/src/prevector.h b/src/prevector.h
index 6ddb6f321f..99e5751634 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -10,12 +10,11 @@
#include <stdint.h>
#include <string.h>
+#include <algorithm>
#include <cstddef>
#include <iterator>
#include <type_traits>
-#include <compat.h>
-
#pragma pack(push, 1)
/** Implements a drop-in replacement for std::vector<T> which stores up to N
* elements directly (without heap allocation). The types Size and Diff are
@@ -197,23 +196,8 @@ private:
T* item_ptr(difference_type pos) { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
- void fill(T* dst, ptrdiff_t count) {
- if (IS_TRIVIALLY_CONSTRUCTIBLE<T>::value) {
- // The most common use of prevector is where T=unsigned char. For
- // trivially constructible types, we can use memset() to avoid
- // looping.
- ::memset(dst, 0, count * sizeof(T));
- } else {
- for (auto i = 0; i < count; ++i) {
- new(static_cast<void*>(dst + i)) T();
- }
- }
- }
-
- void fill(T* dst, ptrdiff_t count, const T& value) {
- for (auto i = 0; i < count; ++i) {
- new(static_cast<void*>(dst + i)) T(value);
- }
+ void fill(T* dst, ptrdiff_t count, const T& value = T{}) {
+ std::fill_n(dst, count, value);
}
template<typename InputIterator>
diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp
index fb95a66bde..a0c2e3f125 100644
--- a/src/primitives/block.cpp
+++ b/src/primitives/block.cpp
@@ -7,7 +7,7 @@
#include <hash.h>
#include <tinyformat.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <crypto/common.h>
uint256 CBlockHeader::GetHash() const
diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp
index 59865a5eab..28c145f71d 100644
--- a/src/primitives/transaction.cpp
+++ b/src/primitives/transaction.cpp
@@ -7,7 +7,7 @@
#include <hash.h>
#include <tinyformat.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
std::string COutPoint::ToString() const
{
@@ -93,7 +93,7 @@ CAmount CTransaction::GetValueOut() const
unsigned int CTransaction::GetTotalSize() const
{
- return ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION);
+ return ::GetSerializeSize(*this, PROTOCOL_VERSION);
}
std::string CTransaction::ToString() const
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index fb9db508d2..0f834eb8c1 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -64,7 +64,7 @@ public:
COutPoint prevout;
CScript scriptSig;
uint32_t nSequence;
- CScriptWitness scriptWitness; //! Only serialized through CTransaction
+ CScriptWitness scriptWitness; //!< Only serialized through CTransaction
/* Setting nSequence to this value for every input in a transaction
* disables nLockTime. */
@@ -73,7 +73,7 @@ public:
/* Below flags apply in the context of BIP 68*/
/* If this flag set, CTxIn::nSequence is NOT interpreted as a
* relative lock-time. */
- static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);
+ static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1U << 31);
/* If CTxIn::nSequence encodes a relative lock-time and this flag
* is set, the relative lock-time has units of 512 seconds,
diff --git a/src/protocol.cpp b/src/protocol.cpp
index b4fc9def1f..bdf236c2c7 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -5,8 +5,8 @@
#include <protocol.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#ifndef WIN32
# include <arpa/inet.h>
diff --git a/src/qt/README.md b/src/qt/README.md
index 3ec538b4f4..0eb18f7cd5 100644
--- a/src/qt/README.md
+++ b/src/qt/README.md
@@ -64,8 +64,8 @@ Represents the view to a single wallet.
* `callback.h`
* `guiconstants.h`: UI colors, app name, etc
* `guiutil.h`: several helper functions
-* `macdockiconhandler.(h/cpp)`
-* `macdockiconhandler.(h/cpp)`: display notifications in macOS
+* `macdockiconhandler.(h/mm)`: macOS dock icon handler
+* `macnotificationhandler.(h/mm)`: display notifications in macOS
## Contribute
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index 673f63a19b..821c23c467 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -123,16 +123,15 @@ void AskPassphraseDialog::accept()
{
QMessageBox::warning(this, tr("Wallet encrypted"),
"<qt>" +
- tr("%1 will close now to finish the encryption process. "
+ tr("Your wallet is now encrypted. "
"Remember that encrypting your wallet cannot fully protect "
- "your bitcoins from being stolen by malware infecting your computer.").arg(tr(PACKAGE_NAME)) +
+ "your bitcoins from being stolen by malware infecting your computer.") +
"<br><br><b>" +
tr("IMPORTANT: Any previous backups you have made of your wallet file "
"should be replaced with the newly generated, encrypted wallet file. "
"For security reasons, previous backups of the unencrypted wallet file "
"will become useless as soon as you start using the new, encrypted wallet.") +
"</b></qt>");
- QApplication::quit();
}
else
{
@@ -153,14 +152,15 @@ void AskPassphraseDialog::accept()
}
} break;
case Unlock:
- if(!model->setWalletLocked(false, oldpass))
- {
- QMessageBox::critical(this, tr("Wallet unlock failed"),
- tr("The passphrase entered for the wallet decryption was incorrect."));
- }
- else
- {
- QDialog::accept(); // Success
+ try {
+ if (!model->setWalletLocked(false, oldpass)) {
+ QMessageBox::critical(this, tr("Wallet unlock failed"),
+ tr("The passphrase entered for the wallet decryption was incorrect."));
+ } else {
+ QDialog::accept(); // Success
+ }
+ } catch (const std::runtime_error& e) {
+ QMessageBox::critical(this, tr("Wallet unlock failed"), e.what());
}
break;
case Decrypt:
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index 97348aad2b..dcfe3dcc57 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -10,7 +10,7 @@
#include <interfaces/node.h>
#include <sync.h>
-#include <utiltime.h>
+#include <util/time.h>
#include <QDebug>
#include <QList>
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index f1d3074c2f..de236a016f 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -28,10 +28,11 @@
#include <interfaces/handler.h>
#include <interfaces/node.h>
+#include <noui.h>
#include <rpc/server.h>
#include <ui_interface.h>
#include <uint256.h>
-#include <util.h>
+#include <util/system.h>
#include <warnings.h>
#include <walletinitinterface.h>
@@ -50,7 +51,6 @@
#include <QThread>
#include <QTimer>
#include <QTranslator>
-#include <QSslConfiguration>
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
@@ -71,9 +71,9 @@ Q_DECLARE_METATYPE(bool*)
Q_DECLARE_METATYPE(CAmount)
Q_DECLARE_METATYPE(uint256)
-static void InitMessage(const std::string &message)
+static void InitMessage(const std::string& message)
{
- LogPrintf("init message: %s\n", message);
+ noui_InitMessage(message);
}
/** Translate string to current locale using Qt. */
@@ -439,8 +439,10 @@ void BitcoinApplication::addWallet(WalletModel* walletModel)
window->setCurrentWallet(walletModel->getWalletName());
}
+#ifdef ENABLE_BIP70
connect(walletModel, &WalletModel::coinsSent,
paymentServer, &PaymentServer::fetchPaymentACK);
+#endif
connect(walletModel, &WalletModel::unload, this, &BitcoinApplication::removeWallet);
m_wallet_models.push_back(walletModel);
@@ -467,7 +469,9 @@ void BitcoinApplication::initializeResult(bool success)
// Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
qWarning() << "Platform customization:" << platformStyle->getName();
#ifdef ENABLE_WALLET
+#ifdef ENABLE_BIP70
PaymentServer::LoadRootCAs();
+#endif
paymentServer->setOptionsModel(optionsModel);
#endif
@@ -536,7 +540,7 @@ WId BitcoinApplication::getMainWinId() const
static void SetupUIArgs()
{
-#ifdef ENABLE_WALLET
+#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70)
gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), true, OptionsCategory::GUI);
#endif
gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), false, OptionsCategory::GUI);
@@ -551,6 +555,10 @@ static void SetupUIArgs()
#ifndef BITCOIN_QT_TEST
int main(int argc, char *argv[])
{
+#ifdef WIN32
+ util::WinCmdLineArgs winArgs;
+ std::tie(argc, argv) = winArgs.get();
+#endif
SetupEnvironment();
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
@@ -562,30 +570,21 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(bitcoin_locale);
BitcoinApplication app(*node, argc, argv);
-#if QT_VERSION > 0x050100
// Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
-#endif
#if QT_VERSION >= 0x050600
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
#ifdef Q_OS_MAC
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
-#if QT_VERSION >= 0x050500
- // Because of the POODLE attack it is recommended to disable SSLv3 (https://disablessl3.com/),
- // so set SSL protocols to TLS1.0+.
- QSslConfiguration sslconf = QSslConfiguration::defaultConfiguration();
- sslconf.setProtocol(QSsl::TlsV1_0OrLater);
- QSslConfiguration::setDefaultConfiguration(sslconf);
-#endif
// Register meta types used for QMetaObject::invokeMethod
qRegisterMetaType< bool* >();
// Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
// IMPORTANT if it is no longer a typedef use the normal variant above
qRegisterMetaType< CAmount >("CAmount");
- qRegisterMetaType< std::function<void(void)> >("std::function<void(void)>");
+ qRegisterMetaType< std::function<void()> >("std::function<void()>");
#ifdef ENABLE_WALLET
qRegisterMetaType<WalletModel*>("WalletModel*");
#endif
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index b68f3a439b..558fcf50ba 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -23,9 +23,7 @@ class AmountSpinBox: public QAbstractSpinBox
public:
explicit AmountSpinBox(QWidget *parent):
- QAbstractSpinBox(parent),
- currentUnit(BitcoinUnits::BTC),
- singleStep(100000) // satoshis
+ QAbstractSpinBox(parent)
{
setAlignment(Qt::AlignRight);
@@ -44,10 +42,19 @@ public:
void fixup(QString &input) const
{
- bool valid = false;
- CAmount val = parse(input, &valid);
- if(valid)
- {
+ bool valid;
+ CAmount val;
+
+ if (input.isEmpty() && !m_allow_empty) {
+ valid = true;
+ val = m_min_amount;
+ } else {
+ valid = false;
+ val = parse(input, &valid);
+ }
+
+ if (valid) {
+ val = qBound(m_min_amount, val, m_max_amount);
input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways);
lineEdit()->setText(input);
}
@@ -64,12 +71,27 @@ public:
Q_EMIT valueChanged();
}
+ void SetAllowEmpty(bool allow)
+ {
+ m_allow_empty = allow;
+ }
+
+ void SetMinValue(const CAmount& value)
+ {
+ m_min_amount = value;
+ }
+
+ void SetMaxValue(const CAmount& value)
+ {
+ m_max_amount = value;
+ }
+
void stepBy(int steps)
{
bool valid = false;
CAmount val = value(&valid);
val = val + steps * singleStep;
- val = qMin(qMax(val, CAmount(0)), BitcoinUnits::maxMoney());
+ val = qBound(m_min_amount, val, m_max_amount);
setValue(val);
}
@@ -125,9 +147,12 @@ public:
}
private:
- int currentUnit;
- CAmount singleStep;
+ int currentUnit{BitcoinUnits::BTC};
+ CAmount singleStep{CAmount(100000)}; // satoshis
mutable QSize cachedMinimumSizeHint;
+ bool m_allow_empty{true};
+ CAmount m_min_amount{CAmount(0)};
+ CAmount m_max_amount{BitcoinUnits::maxMoney()};
/**
* Parse a string into a number of base monetary units and
@@ -174,11 +199,10 @@ protected:
StepEnabled rv = 0;
bool valid = false;
CAmount val = value(&valid);
- if(valid)
- {
- if(val > 0)
+ if (valid) {
+ if (val > m_min_amount)
rv |= StepDownEnabled;
- if(val < BitcoinUnits::maxMoney())
+ if (val < m_max_amount)
rv |= StepUpEnabled;
}
return rv;
@@ -275,6 +299,21 @@ void BitcoinAmountField::setValue(const CAmount& value)
amount->setValue(value);
}
+void BitcoinAmountField::SetAllowEmpty(bool allow)
+{
+ amount->SetAllowEmpty(allow);
+}
+
+void BitcoinAmountField::SetMinValue(const CAmount& value)
+{
+ amount->SetMinValue(value);
+}
+
+void BitcoinAmountField::SetMaxValue(const CAmount& value)
+{
+ amount->SetMaxValue(value);
+}
+
void BitcoinAmountField::setReadOnly(bool fReadOnly)
{
amount->setReadOnly(fReadOnly);
diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h
index f93579c492..650481e30d 100644
--- a/src/qt/bitcoinamountfield.h
+++ b/src/qt/bitcoinamountfield.h
@@ -31,6 +31,15 @@ public:
CAmount value(bool *value=0) const;
void setValue(const CAmount& value);
+ /** If allow empty is set to false the field will be set to the minimum allowed value if left empty. **/
+ void SetAllowEmpty(bool allow);
+
+ /** Set the minimum value in satoshis **/
+ void SetMinValue(const CAmount& value);
+
+ /** Set the maximum value in satoshis **/
+ void SetMaxValue(const CAmount& value);
+
/** Set single step in satoshis **/
void setSingleStep(const CAmount& step);
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 632ce0750a..ef82351551 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -31,8 +31,9 @@
#include <chainparams.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
+#include <noui.h>
#include <ui_interface.h>
-#include <util.h>
+#include <util/system.h>
#include <iostream>
@@ -91,12 +92,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
windowTitle += tr("Node");
}
windowTitle += " " + networkStyle->getTitleAddText();
-#ifndef Q_OS_MAC
QApplication::setWindowIcon(networkStyle->getTrayAndWindowIcon());
setWindowIcon(networkStyle->getTrayAndWindowIcon());
-#else
- MacDockIconHandler::instance()->setIcon(networkStyle->getAppIcon());
-#endif
setWindowTitle(windowTitle);
rpcConsole = new RPCConsole(node, _platformStyle, 0);
@@ -130,7 +127,9 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
createToolBars();
// Create system tray icon and notification
- createTrayIcon(networkStyle);
+ if (QSystemTrayIcon::isSystemTrayAvailable()) {
+ createTrayIcon(networkStyle);
+ }
// Create status bar
statusBar();
@@ -210,6 +209,10 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay);
}
#endif
+
+#ifdef Q_OS_MAC
+ m_app_nap_inhibitor = new CAppNapInhibitor;
+#endif
}
BitcoinGUI::~BitcoinGUI()
@@ -222,6 +225,7 @@ BitcoinGUI::~BitcoinGUI()
if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
trayIcon->hide();
#ifdef Q_OS_MAC
+ delete m_app_nap_inhibitor;
delete appMenuBar;
MacDockIconHandler::cleanup();
#endif
@@ -272,17 +276,17 @@ void BitcoinGUI::createActions()
#ifdef ENABLE_WALLET
// These showNormalIfMinimized are needed because Send Coins and Receive Coins
// can be triggered from the tray menu, and need to show the GUI to be useful.
- connect(overviewAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(overviewAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(overviewAction, &QAction::triggered, this, &BitcoinGUI::gotoOverviewPage);
- connect(sendCoinsAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(sendCoinsAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(sendCoinsAction, &QAction::triggered, [this]{ gotoSendCoinsPage(); });
- connect(sendCoinsMenuAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(sendCoinsMenuAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(sendCoinsMenuAction, &QAction::triggered, [this]{ gotoSendCoinsPage(); });
- connect(receiveCoinsAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(receiveCoinsAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(receiveCoinsAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage);
- connect(receiveCoinsMenuAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(receiveCoinsMenuAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(receiveCoinsMenuAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage);
- connect(historyAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(historyAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(historyAction, &QAction::triggered, this, &BitcoinGUI::gotoHistoryPage);
#endif // ENABLE_WALLET
@@ -349,7 +353,9 @@ void BitcoinGUI::createActions()
connect(encryptWalletAction, &QAction::triggered, walletFrame, &WalletFrame::encryptWallet);
connect(backupWalletAction, &QAction::triggered, walletFrame, &WalletFrame::backupWallet);
connect(changePassphraseAction, &QAction::triggered, walletFrame, &WalletFrame::changePassphrase);
+ connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(signMessageAction, &QAction::triggered, [this]{ gotoSignMessageTab(); });
+ connect(verifyMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(verifyMessageAction, &QAction::triggered, [this]{ gotoVerifyMessageTab(); });
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses);
@@ -584,6 +590,8 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled)
void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle)
{
+ assert(QSystemTrayIcon::isSystemTrayAvailable());
+
#ifndef Q_OS_MAC
trayIcon = new QSystemTrayIcon(this);
QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " + networkStyle->getTitleAddText();
@@ -598,7 +606,7 @@ void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle)
void BitcoinGUI::createTrayIconMenu()
{
#ifndef Q_OS_MAC
- // return if trayIcon is unset (only on non-Mac OSes)
+ // return if trayIcon is unset (only on non-macOSes)
if (!trayIcon)
return;
@@ -607,27 +615,31 @@ void BitcoinGUI::createTrayIconMenu()
connect(trayIcon, &QSystemTrayIcon::activated, this, &BitcoinGUI::trayIconActivated);
#else
- // Note: On Mac, the dock icon is used to provide the tray's functionality.
+ // Note: On macOS, the Dock icon is used to provide the tray's functionality.
MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
- dockIconHandler->setMainWindow(static_cast<QMainWindow*>(this));
- trayIconMenu = dockIconHandler->dockMenu();
+ connect(dockIconHandler, &MacDockIconHandler::dockIconClicked, this, &BitcoinGUI::macosDockIconActivated);
+
+ trayIconMenu = new QMenu(this);
+ trayIconMenu->setAsDockMenu();
#endif
- // Configuration of the tray icon (or dock icon) icon menu
+ // Configuration of the tray icon (or Dock icon) menu
#ifndef Q_OS_MAC
- // Note: On Mac, the dock icon's menu already has show / hide action.
+ // Note: On macOS, the Dock icon's menu already has Show / Hide action.
trayIconMenu->addAction(toggleHideAction);
trayIconMenu->addSeparator();
#endif
- trayIconMenu->addAction(sendCoinsMenuAction);
- trayIconMenu->addAction(receiveCoinsMenuAction);
- trayIconMenu->addSeparator();
- trayIconMenu->addAction(signMessageAction);
- trayIconMenu->addAction(verifyMessageAction);
- trayIconMenu->addSeparator();
+ if (enableWallet) {
+ trayIconMenu->addAction(sendCoinsMenuAction);
+ trayIconMenu->addAction(receiveCoinsMenuAction);
+ trayIconMenu->addSeparator();
+ trayIconMenu->addAction(signMessageAction);
+ trayIconMenu->addAction(verifyMessageAction);
+ trayIconMenu->addSeparator();
+ trayIconMenu->addAction(openRPCConsoleAction);
+ }
trayIconMenu->addAction(optionsAction);
- trayIconMenu->addAction(openRPCConsoleAction);
-#ifndef Q_OS_MAC // This is built-in on Mac
+#ifndef Q_OS_MAC // This is built-in on macOS
trayIconMenu->addSeparator();
trayIconMenu->addAction(quitAction);
#endif
@@ -642,6 +654,12 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
toggleHidden();
}
}
+#else
+void BitcoinGUI::macosDockIconActivated()
+{
+ show();
+ activateWindow();
+}
#endif
void BitcoinGUI::optionsClicked()
@@ -660,10 +678,7 @@ void BitcoinGUI::aboutClicked()
void BitcoinGUI::showDebugWindow()
{
- rpcConsole->showNormal();
- rpcConsole->show();
- rpcConsole->raise();
- rpcConsole->activateWindow();
+ GUIUtil::bringToFront(rpcConsole);
}
void BitcoinGUI::showDebugWindowActivateConsole()
@@ -783,6 +798,11 @@ void BitcoinGUI::openOptionsDialogWithTab(OptionsDialog::Tab tab)
void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header)
{
+// Disabling macOS App Nap on initial sync, disk and reindex operations.
+#ifdef Q_OS_MAC
+ (m_node.isInitialBlockDownload() || m_node.getReindex() || m_node.getImporting()) ? m_app_nap_inhibitor->disableAppNap() : m_app_nap_inhibitor->enableAppNap();
+#endif
+
if (modalOverlay)
{
if (header)
@@ -1145,24 +1165,11 @@ void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
if(!clientModel)
return;
- // activateWindow() (sometimes) helps with keyboard focus on Windows
- if (isHidden())
- {
- show();
- activateWindow();
- }
- else if (isMinimized())
- {
- showNormal();
- activateWindow();
- }
- else if (GUIUtil::isObscured(this))
- {
- raise();
- activateWindow();
- }
- else if(fToggleHidden)
+ if (!isHidden() && !isMinimized() && !GUIUtil::isObscured(this) && fToggleHidden) {
hide();
+ } else {
+ GUIUtil::bringToFront(this);
+ }
}
void BitcoinGUI::toggleHidden()
@@ -1217,8 +1224,11 @@ void BitcoinGUI::showModalOverlay()
modalOverlay->toggleVisibility();
}
-static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, const std::string& caption, unsigned int style)
+static bool ThreadSafeMessageBox(BitcoinGUI* gui, const std::string& message, const std::string& caption, unsigned int style)
{
+ // Redundantly log and print message in non-gui fashion
+ noui_ThreadSafeMessageBox(message, caption, style);
+
bool modal = (style & CClientUIInterface::MODAL);
// The SECURE flag has no effect in the Qt GUI.
// bool secure = (style & CClientUIInterface::SECURE);
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index dcaca10557..e8b857c17c 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -20,6 +20,10 @@
#include <QPoint>
#include <QSystemTrayIcon>
+#ifdef Q_OS_MAC
+#include <qt/macos_appnap.h>
+#endif
+
#include <memory>
class ClientModel;
@@ -143,6 +147,10 @@ private:
HelpMessageDialog* helpMessageDialog = nullptr;
ModalOverlay* modalOverlay = nullptr;
+#ifdef Q_OS_MAC
+ CAppNapInhibitor* m_app_nap_inhibitor = nullptr;
+#endif
+
/** Keep track of previous number of blocks, to detect progress */
int prevBlocks = 0;
int spinnerFrame = 0;
@@ -260,6 +268,9 @@ public Q_SLOTS:
#ifndef Q_OS_MAC
/** Handle tray icon clicked */
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
+#else
+ /** Handle macOS Dock icon clicked */
+ void macosDockIconActivated();
#endif
/** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */
diff --git a/src/qt/callback.h b/src/qt/callback.h
deleted file mode 100644
index da6b0c4c2e..0000000000
--- a/src/qt/callback.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef BITCOIN_QT_CALLBACK_H
-#define BITCOIN_QT_CALLBACK_H
-
-#include <QObject>
-
-class Callback : public QObject
-{
- Q_OBJECT
-public Q_SLOTS:
- virtual void call() = 0;
-};
-
-template <typename F>
-class FunctionCallback : public Callback
-{
- F f;
-
-public:
- explicit FunctionCallback(F f_) : f(std::move(f_)) {}
- ~FunctionCallback() override {}
- void call() override { f(this); }
-};
-
-template <typename F>
-FunctionCallback<F>* makeCallback(F f)
-{
- return new FunctionCallback<F>(std::move(f));
-}
-
-#endif // BITCOIN_QT_CALLBACK_H
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index b2cf4b6399..75012b279c 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -20,7 +20,7 @@
#include <netbase.h>
#include <txmempool.h>
#include <ui_interface.h>
-#include <util.h>
+#include <util/system.h>
#include <warnings.h>
#include <stdint.h>
@@ -177,6 +177,11 @@ QString ClientModel::dataDir() const
return GUIUtil::boostPathToQString(GetDataDir());
}
+QString ClientModel::blocksDir() const
+{
+ return GUIUtil::boostPathToQString(GetBlocksDir());
+}
+
void ClientModel::updateBanlist()
{
banTableModel->refresh();
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index ed7ecbf73b..79e7074cca 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -69,6 +69,7 @@ public:
bool isReleaseVersion() const;
QString formatClientStartupTime() const;
QString dataDir() const;
+ QString blocksDir() const;
bool getProxyInfo(std::string& ip_port) const;
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 68330c51fa..ea970c0bc9 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -2,10 +2,15 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/coincontroldialog.h>
#include <qt/forms/ui_coincontroldialog.h>
#include <qt/addresstablemodel.h>
+#include <base58.h>
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 695ed61228..dca16d6f78 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -127,6 +127,9 @@
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
+ <property name="toolTip">
+ <string>To specify a non-default location of the data directory use the '%1' option.</string>
+ </property>
<property name="text">
<string>N/A</string>
</property>
@@ -142,13 +145,42 @@
</widget>
</item>
<item row="5" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Blocksdir</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" colspan="2">
+ <widget class="QLabel" name="blocksDir">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="toolTip">
+ <string>To specify a non-default location of the blocks directory use the '%1' option.</string>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Startup time</string>
</property>
</widget>
</item>
- <item row="5" column="1" colspan="2">
+ <item row="6" column="1" colspan="2">
<widget class="QLabel" name="startupTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -164,7 +196,7 @@
</property>
</widget>
</item>
- <item row="6" column="0">
+ <item row="7" column="0">
<widget class="QLabel" name="labelNetwork">
<property name="font">
<font>
@@ -177,14 +209,14 @@
</property>
</widget>
</item>
- <item row="7" column="0">
+ <item row="8" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
- <item row="7" column="1" colspan="2">
+ <item row="8" column="1" colspan="2">
<widget class="QLabel" name="networkName">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -200,14 +232,14 @@
</property>
</widget>
</item>
- <item row="8" column="0">
+ <item row="9" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Number of connections</string>
</property>
</widget>
</item>
- <item row="8" column="1" colspan="2">
+ <item row="9" column="1" colspan="2">
<widget class="QLabel" name="numberOfConnections">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -223,7 +255,7 @@
</property>
</widget>
</item>
- <item row="9" column="0">
+ <item row="10" column="0">
<widget class="QLabel" name="label_10">
<property name="font">
<font>
@@ -236,14 +268,14 @@
</property>
</widget>
</item>
- <item row="10" column="0">
+ <item row="11" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Current number of blocks</string>
</property>
</widget>
</item>
- <item row="10" column="1" colspan="2">
+ <item row="11" column="1" colspan="2">
<widget class="QLabel" name="numberOfBlocks">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -259,14 +291,14 @@
</property>
</widget>
</item>
- <item row="11" column="0">
+ <item row="12" column="0">
<widget class="QLabel" name="labelLastBlockTime">
<property name="text">
<string>Last block time</string>
</property>
</widget>
</item>
- <item row="11" column="1" colspan="2">
+ <item row="12" column="1" colspan="2">
<widget class="QLabel" name="lastBlockTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -282,7 +314,7 @@
</property>
</widget>
</item>
- <item row="12" column="0">
+ <item row="13" column="0">
<widget class="QLabel" name="labelMempoolTitle">
<property name="font">
<font>
@@ -295,14 +327,14 @@
</property>
</widget>
</item>
- <item row="13" column="0">
+ <item row="14" column="0">
<widget class="QLabel" name="labelNumberOfTransactions">
<property name="text">
<string>Current number of transactions</string>
</property>
</widget>
</item>
- <item row="13" column="1">
+ <item row="14" column="1">
<widget class="QLabel" name="mempoolNumberTxs">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -318,14 +350,14 @@
</property>
</widget>
</item>
- <item row="14" column="0">
+ <item row="15" column="0">
<widget class="QLabel" name="labelMemoryUsage">
<property name="text">
<string>Memory usage</string>
</property>
</widget>
</item>
- <item row="14" column="1">
+ <item row="15" column="1">
<widget class="QLabel" name="mempoolSize">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -341,7 +373,7 @@
</property>
</widget>
</item>
- <item row="12" column="2" rowspan="3">
+ <item row="13" column="2" rowspan="3">
<layout class="QVBoxLayout" name="verticalLayoutDebugButton">
<property name="spacing">
<number>3</number>
@@ -381,7 +413,7 @@
</item>
</layout>
</item>
- <item row="15" column="0">
+ <item row="16" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 6b31ddea90..386d559281 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -878,28 +878,15 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<item>
<layout class="QHBoxLayout" name="horizontalLayoutFee8">
<item>
- <widget class="QCheckBox" name="checkBoxMinimumFee">
- <property name="toolTip">
- <string>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="labelMinFeeWarning">
+ <widget class="QLabel" name="labelCustomFeeWarning">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
- <string>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
+ <string>When there is less transaction volume than space in the blocks, miners as well as relaying nodes may enforce a minimum fee. Paying only this minimum fee is just fine, but be aware that this can result in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
</property>
<property name="text">
- <string>(read the tooltip)</string>
- </property>
- <property name="margin">
- <number>5</number>
+ <string>A too low fee might result in a never confirming transaction (read the tooltip)</string>
</property>
</widget>
</item>
@@ -992,9 +979,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<property name="text">
<string/>
</property>
- <property name="margin">
- <number>2</number>
- </property>
</widget>
</item>
<item>
@@ -1009,9 +993,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<property name="text">
<string>(Smart fee not initialized yet. This usually takes a few blocks...)</string>
</property>
- <property name="margin">
- <number>2</number>
- </property>
</widget>
</item>
<item>
@@ -1038,24 +1019,8 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<property name="text">
<string>Confirmation time target:</string>
</property>
- <property name="margin">
- <number>2</number>
- </property>
</widget>
</item>
- <item>
- <spacer name="verticalSpacer_3">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>1</width>
- <height>1</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</item>
<item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index bbf60d96fd..0e9aca21b1 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -18,7 +18,7 @@
#include <protocol.h>
#include <script/script.h>
#include <script/standard.h>
-#include <util.h>
+#include <util/system.h>
#ifdef WIN32
#ifdef _WIN32_WINNT
@@ -38,8 +38,6 @@
#include <shlwapi.h>
#endif
-#include <boost/scoped_array.hpp>
-
#include <QAbstractItemView>
#include <QApplication>
#include <QClipboard>
@@ -49,6 +47,7 @@
#include <QDoubleValidator>
#include <QFileDialog>
#include <QFont>
+#include <QFontDatabase>
#include <QKeyEvent>
#include <QLineEdit>
#include <QSettings>
@@ -57,13 +56,14 @@
#include <QUrlQuery>
#include <QMouseEvent>
+#if defined(Q_OS_MAC)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#if QT_VERSION >= 0x50200
-#include <QFontDatabase>
+#include <objc/objc-runtime.h>
+#include <CoreServices/CoreServices.h>
#endif
-static fs::detail::utf8_codecvt_facet utf8;
-
namespace GUIUtil {
QString dateTimeStr(const QDateTime &date)
@@ -78,13 +78,7 @@ QString dateTimeStr(qint64 nTime)
QFont fixedPitchFont()
{
-#if QT_VERSION >= 0x50200
return QFontDatabase::systemFont(QFontDatabase::FixedFont);
-#else
- QFont font("Monospace");
- font.setStyleHint(QFont::Monospace);
- return font;
-#endif
}
// Just some dummy data to generate a convincing random-looking (but consistent) address
@@ -357,6 +351,27 @@ bool isObscured(QWidget *w)
&& checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
}
+void bringToFront(QWidget* w)
+{
+#ifdef Q_OS_MAC
+ // Force application activation on macOS. With Qt 5.4 this is required when
+ // an action in the dock menu is triggered.
+ id app = objc_msgSend((id) objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
+ objc_msgSend(app, sel_registerName("activateIgnoringOtherApps:"), YES);
+#endif
+
+ if (w) {
+ // activateWindow() (sometimes) helps with keyboard focus on Windows
+ if (w->isMinimized()) {
+ w->showNormal();
+ } else {
+ w->show();
+ }
+ w->activateWindow();
+ w->raise();
+ }
+}
+
void openDebugLogfile()
{
fs::path pathDebug = GetDataDir() / "debug.log";
@@ -371,7 +386,7 @@ bool openBitcoinConf()
fs::path pathConfig = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
/* Create the file */
- fs::ofstream configFile(pathConfig, std::ios_base::app);
+ fsbridge::ofstream configFile(pathConfig, std::ios_base::app);
if (!configFile.good())
return false;
@@ -548,40 +563,28 @@ bool SetStartOnSystemStartup(bool fAutoStart)
CoInitialize(nullptr);
// Get a pointer to the IShellLink interface.
- IShellLink* psl = nullptr;
+ IShellLinkW* psl = nullptr;
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr,
- CLSCTX_INPROC_SERVER, IID_IShellLink,
+ CLSCTX_INPROC_SERVER, IID_IShellLinkW,
reinterpret_cast<void**>(&psl));
if (SUCCEEDED(hres))
{
// Get the current executable path
- TCHAR pszExePath[MAX_PATH];
- GetModuleFileName(nullptr, pszExePath, sizeof(pszExePath));
+ WCHAR pszExePath[MAX_PATH];
+ GetModuleFileNameW(nullptr, pszExePath, ARRAYSIZE(pszExePath));
// Start client minimized
QString strArgs = "-min";
// Set -testnet /-regtest options
strArgs += QString::fromStdString(strprintf(" -testnet=%d -regtest=%d", gArgs.GetBoolArg("-testnet", false), gArgs.GetBoolArg("-regtest", false)));
-#ifdef UNICODE
- boost::scoped_array<TCHAR> args(new TCHAR[strArgs.length() + 1]);
- // Convert the QString to TCHAR*
- strArgs.toWCharArray(args.get());
- // Add missing '\0'-termination to string
- args[strArgs.length()] = '\0';
-#endif
-
// Set the path to the shortcut target
psl->SetPath(pszExePath);
- PathRemoveFileSpec(pszExePath);
+ PathRemoveFileSpecW(pszExePath);
psl->SetWorkingDirectory(pszExePath);
psl->SetShowCmd(SW_SHOWMINNOACTIVE);
-#ifndef UNICODE
- psl->SetArguments(strArgs.toStdString().c_str());
-#else
- psl->SetArguments(args.get());
-#endif
+ psl->SetArguments(strArgs.toStdWString().c_str());
// Query IShellLink for the IPersistFile interface for
// saving the shortcut in persistent storage.
@@ -589,11 +592,8 @@ bool SetStartOnSystemStartup(bool fAutoStart)
hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&ppf));
if (SUCCEEDED(hres))
{
- WCHAR pwsz[MAX_PATH];
- // Ensure that the string is ANSI.
- MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH);
// Save the link by calling IPersistFile::Save.
- hres = ppf->Save(pwsz, TRUE);
+ hres = ppf->Save(StartupShortcutPath().wstring().c_str(), TRUE);
ppf->Release();
psl->Release();
CoUninitialize();
@@ -630,7 +630,7 @@ fs::path static GetAutostartFilePath()
bool GetStartOnSystemStartup()
{
- fs::ifstream optionFile(GetAutostartFilePath());
+ fsbridge::ifstream optionFile(GetAutostartFilePath());
if (!optionFile.good())
return false;
// Scan through file for "Hidden=true":
@@ -661,7 +661,7 @@ bool SetStartOnSystemStartup(bool fAutoStart)
fs::create_directories(GetAutostartDir());
- fs::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc);
+ fsbridge::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc);
if (!optionFile.good())
return false;
std::string chain = gArgs.GetChainName();
@@ -682,13 +682,8 @@ bool SetStartOnSystemStartup(bool fAutoStart)
#elif defined(Q_OS_MAC)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
// based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreServices/CoreServices.h>
-
LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl);
LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl)
{
@@ -781,12 +776,12 @@ void setClipboard(const QString& str)
fs::path qstringToBoostPath(const QString &path)
{
- return fs::path(path.toStdString(), utf8);
+ return fs::path(path.toStdString());
}
QString boostPathToQString(const fs::path &path)
{
- return QString::fromStdString(path.string(utf8));
+ return QString::fromStdString(path.string());
}
QString formatDurationStr(int secs)
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 011827e134..f1d0aa48ef 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -115,6 +115,9 @@ namespace GUIUtil
// Determine whether a widget is hidden behind other windows
bool isObscured(QWidget *w);
+ // Activate, show and raise the widget
+ void bringToFront(QWidget* w);
+
// Open debug.log
void openDebugLogfile();
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index b19cc17a4d..0b61b05318 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -13,7 +13,7 @@
#include <qt/guiutil.h>
#include <interfaces/node.h>
-#include <util.h>
+#include <util/system.h>
#include <QFileDialog>
#include <QSettings>
diff --git a/src/qt/macdockiconhandler.h b/src/qt/macdockiconhandler.h
index 1c28593d4a..ff867e21a7 100644
--- a/src/qt/macdockiconhandler.h
+++ b/src/qt/macdockiconhandler.h
@@ -1,44 +1,27 @@
-// Copyright (c) 2011-2015 The Bitcoin Core developers
+// Copyright (c) 2011-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_QT_MACDOCKICONHANDLER_H
#define BITCOIN_QT_MACDOCKICONHANDLER_H
-#include <QMainWindow>
#include <QObject>
-QT_BEGIN_NAMESPACE
-class QIcon;
-class QMenu;
-class QWidget;
-QT_END_NAMESPACE
-
-/** Macintosh-specific dock icon handler.
+/** macOS-specific Dock icon handler.
*/
class MacDockIconHandler : public QObject
{
Q_OBJECT
public:
- ~MacDockIconHandler();
-
- QMenu *dockMenu();
- void setIcon(const QIcon &icon);
- void setMainWindow(QMainWindow *window);
static MacDockIconHandler *instance();
static void cleanup();
- void handleDockIconClickEvent();
Q_SIGNALS:
void dockIconClicked();
private:
MacDockIconHandler();
-
- QWidget *m_dummyWidget;
- QMenu *m_dockMenu;
- QMainWindow *mainWindow;
};
#endif // BITCOIN_QT_MACDOCKICONHANDLER_H
diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm
index b9ad191da7..102adce6c5 100644
--- a/src/qt/macdockiconhandler.mm
+++ b/src/qt/macdockiconhandler.mm
@@ -1,107 +1,36 @@
-// Copyright (c) 2011-2013 The Bitcoin Core developers
+// Copyright (c) 2011-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "macdockiconhandler.h"
-#include <QImageWriter>
-#include <QMenu>
-#include <QBuffer>
-#include <QWidget>
-
#undef slots
-#include <Cocoa/Cocoa.h>
#include <objc/objc.h>
#include <objc/message.h>
static MacDockIconHandler *s_instance = nullptr;
-bool dockClickHandler(id self,SEL _cmd,...) {
+bool dockClickHandler(id self, SEL _cmd, ...) {
Q_UNUSED(self)
Q_UNUSED(_cmd)
- s_instance->handleDockIconClickEvent();
+ Q_EMIT s_instance->dockIconClicked();
- // Return NO (false) to suppress the default OS X actions
+ // Return NO (false) to suppress the default macOS actions
return false;
}
void setupDockClickHandler() {
- Class cls = objc_getClass("NSApplication");
- id appInst = objc_msgSend((id)cls, sel_registerName("sharedApplication"));
-
- if (appInst != nullptr) {
- id delegate = objc_msgSend(appInst, sel_registerName("delegate"));
- Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
- SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
- if (class_getInstanceMethod(delClass, shouldHandle))
- class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:");
- else
- class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler,"B@:");
- }
+ id app = objc_msgSend((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
+ id delegate = objc_msgSend(app, sel_registerName("delegate"));
+ Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
+ SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
+ class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:");
}
-
MacDockIconHandler::MacDockIconHandler() : QObject()
{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
setupDockClickHandler();
- this->m_dummyWidget = new QWidget();
- this->m_dockMenu = new QMenu(this->m_dummyWidget);
- this->setMainWindow(nullptr);
-#if QT_VERSION >= 0x050200
- this->m_dockMenu->setAsDockMenu();
-#endif
- [pool release];
-}
-
-void MacDockIconHandler::setMainWindow(QMainWindow *window) {
- this->mainWindow = window;
-}
-
-MacDockIconHandler::~MacDockIconHandler()
-{
- delete this->m_dummyWidget;
- this->setMainWindow(nullptr);
-}
-
-QMenu *MacDockIconHandler::dockMenu()
-{
- return this->m_dockMenu;
-}
-
-void MacDockIconHandler::setIcon(const QIcon &icon)
-{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- NSImage *image = nil;
- if (icon.isNull())
- image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
- else {
- // generate NSImage from QIcon and use this as dock icon.
- QSize size = icon.actualSize(QSize(128, 128));
- QPixmap pixmap = icon.pixmap(size);
-
- // Write image into a R/W buffer from raw pixmap, then save the image.
- QBuffer notificationBuffer;
- if (!pixmap.isNull() && notificationBuffer.open(QIODevice::ReadWrite)) {
- QImageWriter writer(&notificationBuffer, "PNG");
- if (writer.write(pixmap.toImage())) {
- NSData* macImgData = [NSData dataWithBytes:notificationBuffer.buffer().data()
- length:notificationBuffer.buffer().size()];
- image = [[NSImage alloc] initWithData:macImgData];
- }
- }
-
- if(!image) {
- // if testnet image could not be created, load std. app icon
- image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
- }
- }
-
- [NSApp setApplicationIconImage:image];
- [image release];
- [pool release];
}
MacDockIconHandler *MacDockIconHandler::instance()
@@ -115,14 +44,3 @@ void MacDockIconHandler::cleanup()
{
delete s_instance;
}
-
-void MacDockIconHandler::handleDockIconClickEvent()
-{
- if (this->mainWindow)
- {
- this->mainWindow->activateWindow();
- this->mainWindow->show();
- }
-
- Q_EMIT this->dockIconClicked();
-}
diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h
index 23993adc2e..03c744c12e 100644
--- a/src/qt/macnotificationhandler.h
+++ b/src/qt/macnotificationhandler.h
@@ -19,7 +19,7 @@ public:
void showNotification(const QString &title, const QString &text);
/** check if OS can handle UserNotifications */
- bool hasUserNotificationCenterSupport(void);
+ bool hasUserNotificationCenterSupport();
static MacNotificationHandler *instance();
};
diff --git a/src/qt/macos_appnap.h b/src/qt/macos_appnap.h
new file mode 100644
index 0000000000..8c2cd840b0
--- /dev/null
+++ b/src/qt/macos_appnap.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_MACOS_APPNAP_H
+#define BITCOIN_QT_MACOS_APPNAP_H
+
+#include <memory>
+
+class CAppNapInhibitor final
+{
+public:
+ explicit CAppNapInhibitor();
+ ~CAppNapInhibitor();
+
+ void disableAppNap();
+ void enableAppNap();
+
+private:
+ class CAppNapImpl;
+ std::unique_ptr<CAppNapImpl> impl;
+};
+
+#endif // BITCOIN_QT_MACOS_APPNAP_H
diff --git a/src/qt/macos_appnap.mm b/src/qt/macos_appnap.mm
new file mode 100644
index 0000000000..22a88782ab
--- /dev/null
+++ b/src/qt/macos_appnap.mm
@@ -0,0 +1,71 @@
+// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "macos_appnap.h"
+
+#include <AvailabilityMacros.h>
+#include <Foundation/NSProcessInfo.h>
+#include <Foundation/Foundation.h>
+
+class CAppNapInhibitor::CAppNapImpl
+{
+public:
+ ~CAppNapImpl()
+ {
+ if(activityId)
+ enableAppNap();
+ }
+
+ void disableAppNap()
+ {
+ if (!activityId)
+ {
+ @autoreleasepool {
+ const NSActivityOptions activityOptions =
+ NSActivityUserInitiatedAllowingIdleSystemSleep &
+ ~(NSActivitySuddenTerminationDisabled |
+ NSActivityAutomaticTerminationDisabled);
+
+ id processInfo = [NSProcessInfo processInfo];
+ if ([processInfo respondsToSelector:@selector(beginActivityWithOptions:reason:)])
+ {
+ activityId = [processInfo beginActivityWithOptions: activityOptions reason:@"Temporarily disable App Nap for bitcoin-qt."];
+ [activityId retain];
+ }
+ }
+ }
+ }
+
+ void enableAppNap()
+ {
+ if(activityId)
+ {
+ @autoreleasepool {
+ id processInfo = [NSProcessInfo processInfo];
+ if ([processInfo respondsToSelector:@selector(endActivity:)])
+ [processInfo endActivity:activityId];
+
+ [activityId release];
+ activityId = nil;
+ }
+ }
+ }
+
+private:
+ NSObject* activityId;
+};
+
+CAppNapInhibitor::CAppNapInhibitor() : impl(new CAppNapImpl()) {}
+
+CAppNapInhibitor::~CAppNapInhibitor() = default;
+
+void CAppNapInhibitor::disableAppNap()
+{
+ impl->disableAppNap();
+}
+
+void CAppNapInhibitor::enableAppNap()
+{
+ impl->enableAppNap();
+}
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index b51322394f..c9871f6c66 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -23,6 +23,7 @@
#include <QIntValidator>
#include <QLocale>
#include <QMessageBox>
+#include <QSystemTrayIcon>
#include <QTimer>
OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
@@ -126,6 +127,13 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
connect(ui->proxyIpTor, &QValidatedLineEdit::validationDidChange, this, &OptionsDialog::updateProxyValidationState);
connect(ui->proxyPort, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState);
connect(ui->proxyPortTor, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState);
+
+ if (!QSystemTrayIcon::isSystemTrayAvailable()) {
+ ui->hideTrayIcon->setChecked(true);
+ ui->hideTrayIcon->setEnabled(false);
+ ui->minimizeToTray->setChecked(false);
+ ui->minimizeToTray->setEnabled(false);
+ }
}
OptionsDialog::~OptionsDialog()
@@ -211,8 +219,10 @@ void OptionsDialog::setMapper()
/* Window */
#ifndef Q_OS_MAC
- mapper->addMapping(ui->hideTrayIcon, OptionsModel::HideTrayIcon);
- mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray);
+ if (QSystemTrayIcon::isSystemTrayAvailable()) {
+ mapper->addMapping(ui->hideTrayIcon, OptionsModel::HideTrayIcon);
+ mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray);
+ }
mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose);
#endif
diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp
index 944bcc8ad0..b962ab1ef2 100644
--- a/src/qt/paymentrequestplus.cpp
+++ b/src/qt/paymentrequestplus.cpp
@@ -9,7 +9,7 @@
#include <qt/paymentrequestplus.h>
-#include <util.h>
+#include <util/system.h>
#include <stdexcept>
@@ -142,7 +142,6 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c
if (result != 1) {
int error = X509_STORE_CTX_get_error(store_ctx);
// For testing payment requests, we allow self signed root certs!
- // This option is just shown in the UI options, if -help-debug is enabled.
if (!(error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && gArgs.GetBoolArg("-allowselfsignedrootcertificates", DEFAULT_SELFSIGNED_ROOTCERTS))) {
throw SSLVerifyError(X509_verify_cert_error_string(error));
} else {
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index bcafc8f859..8148986b51 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/paymentserver.h>
#include <qt/bitcoinunits.h>
@@ -13,7 +17,7 @@
#include <policy/policy.h>
#include <key_io.h>
#include <ui_interface.h>
-#include <util.h>
+#include <util/system.h>
#include <wallet/wallet.h>
#include <cstdlib>
@@ -45,6 +49,7 @@
const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
const QString BITCOIN_IPC_PREFIX("bitcoin:");
+#ifdef ENABLE_BIP70
// BIP70 payment protocol messages
const char* BIP70_MESSAGE_PAYMENTACK = "PaymentACK";
const char* BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest";
@@ -52,21 +57,7 @@ const char* BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest";
const char* BIP71_MIMETYPE_PAYMENT = "application/bitcoin-payment";
const char* BIP71_MIMETYPE_PAYMENTACK = "application/bitcoin-paymentack";
const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/bitcoin-paymentrequest";
-
-struct X509StoreDeleter {
- void operator()(X509_STORE* b) {
- X509_STORE_free(b);
- }
-};
-
-struct X509Deleter {
- void operator()(X509* b) { X509_free(b); }
-};
-
-namespace // Anon namespace
-{
- std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
-}
+#endif
//
// Create a name that is unique for:
@@ -93,94 +84,6 @@ static QString ipcServerName()
static QList<QString> savedPaymentRequests;
-static void ReportInvalidCertificate(const QSslCertificate& cert)
-{
- qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
-}
-
-//
-// Load OpenSSL's list of root certificate authorities
-//
-void PaymentServer::LoadRootCAs(X509_STORE* _store)
-{
- // Unit tests mostly use this, to pass in fake root CAs:
- if (_store)
- {
- certStore.reset(_store);
- return;
- }
-
- // Normal execution, use either -rootcertificates or system certs:
- certStore.reset(X509_STORE_new());
-
- // Note: use "-system-" default here so that users can pass -rootcertificates=""
- // and get 'I don't like X.509 certificates, don't trust anybody' behavior:
- QString certFile = QString::fromStdString(gArgs.GetArg("-rootcertificates", "-system-"));
-
- // Empty store
- if (certFile.isEmpty()) {
- qDebug() << QString("PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
- return;
- }
-
- QList<QSslCertificate> certList;
-
- if (certFile != "-system-") {
- qDebug() << QString("PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
-
- certList = QSslCertificate::fromPath(certFile);
- // Use those certificates when fetching payment requests, too:
- QSslSocket::setDefaultCaCertificates(certList);
- } else
- certList = QSslSocket::systemCaCertificates();
-
- int nRootCerts = 0;
- const QDateTime currentTime = QDateTime::currentDateTime();
-
- for (const QSslCertificate& cert : certList) {
- // Don't log NULL certificates
- if (cert.isNull())
- continue;
-
- // Not yet active/valid, or expired certificate
- if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
- ReportInvalidCertificate(cert);
- continue;
- }
-
- // Blacklisted certificate
- if (cert.isBlacklisted()) {
- ReportInvalidCertificate(cert);
- continue;
- }
- QByteArray certData = cert.toDer();
- const unsigned char *data = (const unsigned char *)certData.data();
-
- std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
- if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
- {
- // Note: X509_STORE increases the reference count to the X509 object,
- // we still have to release our reference to it.
- ++nRootCerts;
- }
- else
- {
- ReportInvalidCertificate(cert);
- continue;
- }
- }
- qWarning() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts << " root certificates";
-
- // Project for another day:
- // Fetch certificate revocation lists, and add them to certStore.
- // Issues to consider:
- // performance (start a thread to fetch in background?)
- // privacy (fetch through tor/proxy so IP address isn't revealed)
- // would it be easier to just use a compiled-in blacklist?
- // or use Qt's blacklist?
- // "certificate stapling" with server-side caching is more efficient
-}
-
//
// Sending to the server is done synchronously, at startup.
// If the server isn't already running, startup continues,
@@ -221,6 +124,7 @@ void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char*
}
}
}
+#ifdef ENABLE_BIP70
else if (QFile::exists(arg)) // Filename
{
savedPaymentRequests.append(arg);
@@ -244,6 +148,7 @@ void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char*
// GUI hasn't started yet so we can't pop up a message box.
qWarning() << "PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg;
}
+#endif
}
}
@@ -290,12 +195,16 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
QObject(parent),
saveURIs(true),
uriServer(0),
- netManager(0),
optionsModel(0)
+#ifdef ENABLE_BIP70
+ ,netManager(0)
+#endif
{
+#ifdef ENABLE_BIP70
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;
+#endif
// Install global event filter to catch QFileOpenEvents
// on Mac: sent when you click bitcoin: links
@@ -319,14 +228,18 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
}
else {
connect(uriServer, &QLocalServer::newConnection, this, &PaymentServer::handleURIConnection);
+#ifdef ENABLE_BIP70
connect(this, &PaymentServer::receivedPaymentACK, this, &PaymentServer::handlePaymentACK);
+#endif
}
}
}
PaymentServer::~PaymentServer()
{
+#ifdef ENABLE_BIP70
google::protobuf::ShutdownProtobufLibrary();
+#endif
}
//
@@ -349,33 +262,11 @@ bool PaymentServer::eventFilter(QObject *object, QEvent *event)
return QObject::eventFilter(object, event);
}
-void PaymentServer::initNetManager()
-{
- if (!optionsModel)
- return;
- delete netManager;
-
- // netManager is used to fetch paymentrequests given in bitcoin: URIs
- netManager = new QNetworkAccessManager(this);
-
- QNetworkProxy proxy;
-
- // Query active SOCKS5 proxy
- if (optionsModel->getProxySettings(proxy)) {
- netManager->setProxy(proxy);
-
- qDebug() << "PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
- }
- else
- qDebug() << "PaymentServer::initNetManager: No active proxy server found.";
-
- connect(netManager, &QNetworkAccessManager::finished, this, &PaymentServer::netRequestFinished);
- connect(netManager, &QNetworkAccessManager::sslErrors, this, &PaymentServer::reportSslErrors);
-}
-
void PaymentServer::uiReady()
{
+#ifdef ENABLE_BIP70
initNetManager();
+#endif
saveURIs = false;
for (const QString& s : savedPaymentRequests)
@@ -403,6 +294,10 @@ void PaymentServer::handleURIOrFile(const QString& s)
QUrlQuery uri((QUrl(s)));
if (uri.hasQueryItem("r")) // payment request URI
{
+#ifdef ENABLE_BIP70
+ Q_EMIT message(tr("URI handling"),
+ tr("You are using a BIP70 URL which will be unsupported in the future."),
+ CClientUIInterface::ICON_WARNING);
QByteArray temp;
temp.append(uri.queryItemValue("r"));
QString decoded = QUrl::fromPercentEncoding(temp);
@@ -420,7 +315,11 @@ void PaymentServer::handleURIOrFile(const QString& s)
tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()),
CClientUIInterface::ICON_WARNING);
}
-
+#else
+ Q_EMIT message(tr("URI handling"),
+ tr("Cannot process payment request because BIP70 support was not compiled in."),
+ CClientUIInterface::ICON_WARNING);
+#endif
return;
}
else // normal URI
@@ -444,6 +343,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
}
}
+#ifdef ENABLE_BIP70
if (QFile::exists(s)) // payment request file
{
PaymentRequestPlus request;
@@ -459,6 +359,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
return;
}
+#endif
}
void PaymentServer::handleURIConnection()
@@ -481,6 +382,140 @@ void PaymentServer::handleURIConnection()
handleURIOrFile(msg);
}
+void PaymentServer::setOptionsModel(OptionsModel *_optionsModel)
+{
+ this->optionsModel = _optionsModel;
+}
+
+#ifdef ENABLE_BIP70
+struct X509StoreDeleter {
+ void operator()(X509_STORE* b) {
+ X509_STORE_free(b);
+ }
+};
+
+struct X509Deleter {
+ void operator()(X509* b) { X509_free(b); }
+};
+
+namespace // Anon namespace
+{
+ std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
+}
+
+static void ReportInvalidCertificate(const QSslCertificate& cert)
+{
+ qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
+}
+
+//
+// Load OpenSSL's list of root certificate authorities
+//
+void PaymentServer::LoadRootCAs(X509_STORE* _store)
+{
+ // Unit tests mostly use this, to pass in fake root CAs:
+ if (_store)
+ {
+ certStore.reset(_store);
+ return;
+ }
+
+ // Normal execution, use either -rootcertificates or system certs:
+ certStore.reset(X509_STORE_new());
+
+ // Note: use "-system-" default here so that users can pass -rootcertificates=""
+ // and get 'I don't like X.509 certificates, don't trust anybody' behavior:
+ QString certFile = QString::fromStdString(gArgs.GetArg("-rootcertificates", "-system-"));
+
+ // Empty store
+ if (certFile.isEmpty()) {
+ qDebug() << QString("PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
+ return;
+ }
+
+ QList<QSslCertificate> certList;
+
+ if (certFile != "-system-") {
+ qDebug() << QString("PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
+
+ certList = QSslCertificate::fromPath(certFile);
+ // Use those certificates when fetching payment requests, too:
+ QSslSocket::setDefaultCaCertificates(certList);
+ } else
+ certList = QSslSocket::systemCaCertificates();
+
+ int nRootCerts = 0;
+ const QDateTime currentTime = QDateTime::currentDateTime();
+
+ for (const QSslCertificate& cert : certList) {
+ // Don't log NULL certificates
+ if (cert.isNull())
+ continue;
+
+ // Not yet active/valid, or expired certificate
+ if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
+ ReportInvalidCertificate(cert);
+ continue;
+ }
+
+ // Blacklisted certificate
+ if (cert.isBlacklisted()) {
+ ReportInvalidCertificate(cert);
+ continue;
+ }
+
+ QByteArray certData = cert.toDer();
+ const unsigned char *data = (const unsigned char *)certData.data();
+
+ std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
+ if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
+ {
+ // Note: X509_STORE increases the reference count to the X509 object,
+ // we still have to release our reference to it.
+ ++nRootCerts;
+ }
+ else
+ {
+ ReportInvalidCertificate(cert);
+ continue;
+ }
+ }
+ qWarning() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts << " root certificates";
+
+ // Project for another day:
+ // Fetch certificate revocation lists, and add them to certStore.
+ // Issues to consider:
+ // performance (start a thread to fetch in background?)
+ // privacy (fetch through tor/proxy so IP address isn't revealed)
+ // would it be easier to just use a compiled-in blacklist?
+ // or use Qt's blacklist?
+ // "certificate stapling" with server-side caching is more efficient
+}
+
+void PaymentServer::initNetManager()
+{
+ if (!optionsModel)
+ return;
+ delete netManager;
+
+ // netManager is used to fetch paymentrequests given in bitcoin: URIs
+ netManager = new QNetworkAccessManager(this);
+
+ QNetworkProxy proxy;
+
+ // Query active SOCKS5 proxy
+ if (optionsModel->getProxySettings(proxy)) {
+ netManager->setProxy(proxy);
+
+ qDebug() << "PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
+ }
+ else
+ qDebug() << "PaymentServer::initNetManager: No active proxy server found.";
+
+ connect(netManager, &QNetworkAccessManager::finished, this, &PaymentServer::netRequestFinished);
+ connect(netManager, &QNetworkAccessManager::sslErrors, this, &PaymentServer::reportSslErrors);
+}
+
//
// Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine()
// so don't use "Q_EMIT message()", but "QMessageBox::"!
@@ -731,11 +766,6 @@ void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError>
Q_EMIT message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR);
}
-void PaymentServer::setOptionsModel(OptionsModel *_optionsModel)
-{
- this->optionsModel = _optionsModel;
-}
-
void PaymentServer::handlePaymentACK(const QString& paymentACKMsg)
{
// currently we don't further process or store the paymentACK message
@@ -794,3 +824,4 @@ X509_STORE* PaymentServer::getCertStore()
{
return certStore.get();
}
+#endif
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index d335db9c85..30b5bc3b6d 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -32,7 +32,13 @@
// sends them to the server.
//
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#ifdef ENABLE_BIP70
#include <qt/paymentrequestplus.h>
+#endif
#include <qt/walletmodel.h>
#include <QObject>
@@ -73,6 +79,10 @@ public:
explicit PaymentServer(QObject* parent, bool startLocalServer = true);
~PaymentServer();
+ // OptionsModel is used for getting proxy settings and display unit
+ void setOptionsModel(OptionsModel *optionsModel);
+
+#ifdef ENABLE_BIP70
// Load root certificate authorities. Pass nullptr (default)
// to read from the file specified in the -rootcertificates setting,
// or, if that's not set, to use the system default root certificates.
@@ -83,9 +93,6 @@ public:
// Return certificate store
static X509_STORE* getCertStore();
- // OptionsModel is used for getting proxy settings and display unit
- void setOptionsModel(OptionsModel *optionsModel);
-
// Verify that the payment request network matches the client network
static bool verifyNetwork(interfaces::Node& node, const payments::PaymentDetails& requestDetails);
// Verify if the payment request is expired
@@ -94,33 +101,40 @@ public:
static bool verifySize(qint64 requestSize);
// Verify the payment request amount is valid
static bool verifyAmount(const CAmount& requestAmount);
+#endif
Q_SIGNALS:
// Fired when a valid payment request is received
void receivedPaymentRequest(SendCoinsRecipient);
- // Fired when a valid PaymentACK is received
- void receivedPaymentACK(const QString &paymentACKMsg);
-
// Fired when a message should be reported to the user
void message(const QString &title, const QString &message, unsigned int style);
+#ifdef ENABLE_BIP70
+ // Fired when a valid PaymentACK is received
+ void receivedPaymentACK(const QString &paymentACKMsg);
+#endif
+
public Q_SLOTS:
// Signal this when the main window's UI is ready
// to display payment requests to the user
void uiReady();
- // Submit Payment message to a merchant, get back PaymentACK:
- void fetchPaymentACK(WalletModel* walletModel, const SendCoinsRecipient& recipient, QByteArray transaction);
-
// Handle an incoming URI, URI with local file scheme or file
void handleURIOrFile(const QString& s);
+#ifdef ENABLE_BIP70
+ // Submit Payment message to a merchant, get back PaymentACK:
+ void fetchPaymentACK(WalletModel* walletModel, const SendCoinsRecipient& recipient, QByteArray transaction);
+#endif
+
private Q_SLOTS:
void handleURIConnection();
+#ifdef ENABLE_BIP70
void netRequestFinished(QNetworkReply*);
void reportSslErrors(QNetworkReply*, const QList<QSslError> &);
void handlePaymentACK(const QString& paymentACKMsg);
+#endif
protected:
// Constructor registers this on the parent QApplication to
@@ -128,19 +142,19 @@ protected:
bool eventFilter(QObject *object, QEvent *event);
private:
+ bool saveURIs; // true during startup
+ QLocalServer* uriServer;
+ OptionsModel *optionsModel;
+
+#ifdef ENABLE_BIP70
static bool readPaymentRequestFromFile(const QString& filename, PaymentRequestPlus& request);
bool processPaymentRequest(const PaymentRequestPlus& request, SendCoinsRecipient& recipient);
void fetchRequest(const QUrl& url);
// Setup networking
void initNetManager();
-
- bool saveURIs; // true during startup
- QLocalServer* uriServer;
-
QNetworkAccessManager* netManager; // Used to fetch payment requests
-
- OptionsModel *optionsModel;
+#endif
};
#endif // BITCOIN_QT_PAYMENTSERVER_H
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index ad13b20ebe..606f1d2910 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -18,7 +18,7 @@
#include <netbase.h>
#include <rpc/server.h>
#include <rpc/client.h>
-#include <util.h>
+#include <util/system.h>
#include <openssl/crypto.h>
@@ -101,7 +101,7 @@ class QtRPCTimerBase: public QObject, public RPCTimerBase
{
Q_OBJECT
public:
- QtRPCTimerBase(std::function<void(void)>& _func, int64_t millis):
+ QtRPCTimerBase(std::function<void()>& _func, int64_t millis):
func(_func)
{
timer.setSingleShot(true);
@@ -111,7 +111,7 @@ public:
~QtRPCTimerBase() {}
private:
QTimer timer;
- std::function<void(void)> func;
+ std::function<void()> func;
};
class QtRPCTimerInterface: public RPCTimerInterface
@@ -119,7 +119,7 @@ class QtRPCTimerInterface: public RPCTimerInterface
public:
~QtRPCTimerInterface() {}
const char *Name() { return "Qt"; }
- RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis)
+ RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis)
{
return new QtRPCTimerBase(func, millis);
}
@@ -459,6 +459,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center());
}
+ QChar nonbreaking_hyphen(8209);
+ ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir"));
+ ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir"));
ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME)));
if (platformStyle->getImagesOnButtons()) {
@@ -536,6 +539,7 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
// forward these events to lineEdit
if(obj == autoCompleter->popup()) {
QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt));
+ autoCompleter->popup()->hide();
return true;
}
break;
@@ -661,6 +665,7 @@ void RPCConsole::setClientModel(ClientModel *model)
ui->clientVersion->setText(model->formatFullVersion());
ui->clientUserAgent->setText(model->formatSubVersion());
ui->dataDir->setText(model->dataDir());
+ ui->blocksDir->setText(model->blocksDir());
ui->startupTime->setText(model->formatClientStartupTime());
ui->networkName->setText(QString::fromStdString(Params().NetworkIDString()));
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 6f66bc19e1..65db0280b7 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/sendcoinsdialog.h>
#include <qt/forms/ui_sendcoinsdialog.h>
@@ -115,13 +119,11 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
settings.setValue("nSmartFeeSliderPosition", 0);
if (!settings.contains("nTransactionFee"))
settings.setValue("nTransactionFee", (qint64)DEFAULT_PAY_TX_FEE);
- if (!settings.contains("fPayOnlyMinFee"))
- settings.setValue("fPayOnlyMinFee", false);
ui->groupFee->setId(ui->radioSmartFee, 0);
ui->groupFee->setId(ui->radioCustomFee, 1);
ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
+ ui->customFee->SetAllowEmpty(false);
ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
- ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
}
@@ -170,14 +172,15 @@ void SendCoinsDialog::setModel(WalletModel *_model)
connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::updateFeeSectionControls);
connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::coinControlUpdateLabels);
connect(ui->customFee, &BitcoinAmountField::valueChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
- connect(ui->checkBoxMinimumFee, &QCheckBox::stateChanged, this, &SendCoinsDialog::setMinimumFee);
- connect(ui->checkBoxMinimumFee, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateFeeSectionControls);
- connect(ui->checkBoxMinimumFee, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
- ui->customFee->setSingleStep(model->wallet().getRequiredFee(1000));
+ CAmount requiredFee = model->wallet().getRequiredFee(1000);
+ ui->customFee->SetMinValue(requiredFee);
+ if (ui->customFee->value() < requiredFee) {
+ ui->customFee->setValue(requiredFee);
+ }
+ ui->customFee->setSingleStep(requiredFee);
updateFeeSectionControls();
- updateMinFeeLabel();
updateSmartFeeLabel();
// set default rbf checkbox state
@@ -206,7 +209,6 @@ SendCoinsDialog::~SendCoinsDialog()
settings.setValue("nFeeRadio", ui->groupFee->checkedId());
settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
- settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
delete ui;
}
@@ -290,7 +292,9 @@ void SendCoinsDialog::on_sendButton_clicked()
QString recipientElement;
recipientElement = "<br />";
+#ifdef ENABLE_BIP70
if (!rcp.paymentRequest.IsInitialized()) // normal payment
+#endif
{
if(rcp.label.length() > 0) // label with address
{
@@ -302,6 +306,7 @@ void SendCoinsDialog::on_sendButton_clicked()
recipientElement.append(tr("%1 to %2").arg(amount, address));
}
}
+#ifdef ENABLE_BIP70
else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request
{
recipientElement.append(tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)));
@@ -310,6 +315,7 @@ void SendCoinsDialog::on_sendButton_clicked()
{
recipientElement.append(tr("%1 to %2").arg(amount, address));
}
+#endif
formatted.append(recipientElement);
}
@@ -534,7 +540,6 @@ void SendCoinsDialog::updateDisplayUnit()
{
setBalance(model->wallet().getBalances());
ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
- updateMinFeeLabel();
updateSmartFeeLabel();
}
@@ -634,11 +639,6 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
}
}
-void SendCoinsDialog::setMinimumFee()
-{
- ui->customFee->setValue(model->wallet().getRequiredFee(1000));
-}
-
void SendCoinsDialog::updateFeeSectionControls()
{
ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked());
@@ -646,10 +646,9 @@ void SendCoinsDialog::updateFeeSectionControls()
ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
- ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
- ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
- ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
- ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
+ ui->labelCustomFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
+ ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked());
+ ui->customFee ->setEnabled(ui->radioCustomFee->isChecked());
}
void SendCoinsDialog::updateFeeMinimizedLabel()
@@ -664,14 +663,6 @@ void SendCoinsDialog::updateFeeMinimizedLabel()
}
}
-void SendCoinsDialog::updateMinFeeLabel()
-{
- if (model && model->getOptionsModel())
- ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg(
- BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getRequiredFee(1000)) + "/kB")
- );
-}
-
void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
{
if (ui->radioCustomFee->isChecked()) {
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 7009855f17..e1ebc77d59 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -92,9 +92,7 @@ private Q_SLOTS:
void coinControlClipboardBytes();
void coinControlClipboardLowOutput();
void coinControlClipboardChange();
- void setMinimumFee();
void updateFeeSectionControls();
- void updateMinFeeLabel();
void updateSmartFeeLabel();
Q_SIGNALS:
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index b394ff3150..76c942c8b9 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/sendcoinsentry.h>
#include <qt/forms/ui_sendcoinsentry.h>
@@ -133,9 +137,11 @@ bool SendCoinsEntry::validate(interfaces::Node& node)
// Check input validity
bool retval = true;
+#ifdef ENABLE_BIP70
// Skip checks for payment request
if (recipient.paymentRequest.IsInitialized())
return retval;
+#endif
if (!model->validateAddress(ui->payTo->text()))
{
@@ -166,9 +172,11 @@ bool SendCoinsEntry::validate(interfaces::Node& node)
SendCoinsRecipient SendCoinsEntry::getValue()
{
+#ifdef ENABLE_BIP70
// Payment request
if (recipient.paymentRequest.IsInitialized())
return recipient;
+#endif
// Normal payment
recipient.address = ui->payTo->text();
@@ -196,6 +204,7 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value)
{
recipient = value;
+#ifdef ENABLE_BIP70
if (recipient.paymentRequest.IsInitialized()) // payment request
{
if (recipient.authenticatedMerchant.isEmpty()) // unauthenticated
@@ -216,6 +225,7 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value)
}
}
else // normal payment
+#endif
{
// message
ui->messageTextLabel->setText(recipient.message);
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index 1eff4f6b65..df38285d08 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -15,7 +15,7 @@
#include <interfaces/node.h>
#include <interfaces/wallet.h>
#include <ui_interface.h>
-#include <util.h>
+#include <util/system.h>
#include <version.h>
#include <QApplication>
@@ -37,9 +37,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
float fontFactor = 1.0;
float devicePixelRatio = 1.0;
-#if QT_VERSION > 0x050100
devicePixelRatio = static_cast<QGuiApplication*>(QCoreApplication::instance())->devicePixelRatio();
-#endif
// define text to place
QString titleText = tr(PACKAGE_NAME);
@@ -53,10 +51,8 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
QSize splashSize(480*devicePixelRatio,320*devicePixelRatio);
pixmap = QPixmap(splashSize);
-#if QT_VERSION > 0x050100
// change to HiDPI if it makes sense
pixmap.setDevicePixelRatio(devicePixelRatio);
-#endif
QPainter pixPaint(&pixmap);
pixPaint.setPen(QColor(100,100,100));
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index c3d33c76d4..3e414df1f0 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -2,21 +2,22 @@
#include <qt/test/util.h>
#include <test/test_bitcoin.h>
+#include <interfaces/chain.h>
#include <interfaces/node.h>
#include <qt/addressbookpage.h>
#include <qt/addresstablemodel.h>
#include <qt/editaddressdialog.h>
-#include <qt/callback.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/qvalidatedlineedit.h>
#include <qt/walletmodel.h>
#include <key.h>
-#include <pubkey.h>
#include <key_io.h>
+#include <pubkey.h>
#include <wallet/wallet.h>
+#include <QApplication>
#include <QTimer>
#include <QMessageBox>
@@ -56,7 +57,8 @@ void EditAddressAndSubmit(
void TestAddAddressesToSendBook()
{
TestChain100Setup test;
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("mock", WalletDatabase::CreateMock());
+ auto chain = interfaces::MakeChain();
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
@@ -139,5 +141,16 @@ void TestAddAddressesToSendBook()
void AddressBookTests::addressBookTests()
{
+#ifdef Q_OS_MAC
+ if (QApplication::platformName() == "minimal") {
+ // Disable for mac on "minimal" platform to avoid crashes inside the Qt
+ // framework when it tries to look up unimplemented cocoa functions,
+ // and fails to handle returned nulls
+ // (https://bugreports.qt.io/browse/QTBUG-49686).
+ QWARN("Skipping AddressBookTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
+ "with 'test_bitcoin-qt -platform cocoa' on mac, or else use a linux or windows build.");
+ return;
+ }
+#endif
TestAddAddressesToSendBook();
}
diff --git a/src/qt/test/compattests.cpp b/src/qt/test/compattests.cpp
index af5c69ea9a..6750c543da 100644
--- a/src/qt/test/compattests.cpp
+++ b/src/qt/test/compattests.cpp
@@ -2,7 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70)
#include <qt/paymentrequestplus.h> // this includes protobuf's port.h which defines its own bswap macos
+#endif
#include <qt/test/compattests.h>
diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp
index 5384b9e8b0..94907595f5 100644
--- a/src/qt/test/paymentservertests.cpp
+++ b/src/qt/test/paymentservertests.cpp
@@ -13,8 +13,8 @@
#include <random.h>
#include <script/script.h>
#include <script/standard.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 2e321c1ba1..ed453336da 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -14,7 +14,7 @@
#include <qt/rpcconsole.h>
#include <test/test_bitcoin.h>
#include <univalue.h>
-#include <util.h>
+#include <util/system.h>
#include <QDir>
#include <QtGlobal>
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index df65a85fb5..b6523604fd 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -8,15 +8,17 @@
#include <chainparams.h>
#include <qt/test/rpcnestedtests.h>
-#include <util.h>
+#include <util/system.h>
#include <qt/test/uritests.h>
#include <qt/test/compattests.h>
#ifdef ENABLE_WALLET
#include <qt/test/addressbooktests.h>
+#ifdef ENABLE_BIP70
#include <qt/test/paymentservertests.h>
+#endif // ENABLE_BIP70
#include <qt/test/wallettests.h>
-#endif
+#endif // ENABLE_WALLET
#include <QApplication>
#include <QObject>
@@ -74,7 +76,7 @@ int main(int argc, char *argv[])
if (QTest::qExec(&test1) != 0) {
fInvalid = true;
}
-#ifdef ENABLE_WALLET
+#if defined(ENABLE_WALLET) && defined(ENABLE_BIP70)
PaymentServerTests test2;
if (QTest::qExec(&test2) != 0) {
fInvalid = true;
diff --git a/src/qt/test/util.cpp b/src/qt/test/util.cpp
index 0bd719326e..ae2fb93bf7 100644
--- a/src/qt/test/util.cpp
+++ b/src/qt/test/util.cpp
@@ -1,15 +1,13 @@
-#include <qt/callback.h>
-
#include <QApplication>
#include <QMessageBox>
-#include <QTimer>
-#include <QString>
#include <QPushButton>
+#include <QString>
+#include <QTimer>
#include <QWidget>
void ConfirmMessage(QString* text, int msec)
{
- QTimer::singleShot(msec, makeCallback([text](Callback* callback) {
+ QTimer::singleShot(msec, [text]() {
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("QMessageBox")) {
QMessageBox* messageBox = qobject_cast<QMessageBox*>(widget);
@@ -17,6 +15,5 @@ void ConfirmMessage(QString* text, int msec)
messageBox->defaultButton()->click();
}
}
- delete callback;
- }), &Callback::call);
+ });
}
diff --git a/src/qt/test/util.h b/src/qt/test/util.h
index 5363c94547..377f07dcba 100644
--- a/src/qt/test/util.h
+++ b/src/qt/test/util.h
@@ -1,6 +1,8 @@
#ifndef BITCOIN_QT_TEST_UTIL_H
#define BITCOIN_QT_TEST_UTIL_H
+#include <QString>
+
/**
* Press "Ok" button in message box dialog.
*
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index a0cfe8ae87..f02fd8aea7 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -1,9 +1,10 @@
#include <qt/test/wallettests.h>
#include <qt/test/util.h>
+#include <interfaces/chain.h>
#include <interfaces/node.h>
+#include <base58.h>
#include <qt/bitcoinamountfield.h>
-#include <qt/callback.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/qvalidatedlineedit.h>
@@ -39,7 +40,7 @@ namespace
//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.
void ConfirmSend(QString* text = nullptr, bool cancel = false)
{
- QTimer::singleShot(0, makeCallback([text, cancel](Callback* callback) {
+ QTimer::singleShot(0, [text, cancel]() {
for (QWidget* widget : QApplication::topLevelWidgets()) {
if (widget->inherits("SendConfirmationDialog")) {
SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget);
@@ -49,8 +50,7 @@ void ConfirmSend(QString* text = nullptr, bool cancel = false)
button->click();
}
}
- delete callback;
- }), &Callback::call);
+ });
}
//! Send coins to address and return txid.
@@ -133,7 +133,8 @@ void TestGUI()
for (int i = 0; i < 5; ++i) {
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("mock", WalletDatabase::CreateMock());
+ auto chain = interfaces::MakeChain();
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
{
@@ -142,7 +143,7 @@ void TestGUI()
wallet->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
}
{
- LOCK(cs_main);
+ auto locked_chain = wallet->chain().lock();
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true);
@@ -243,5 +244,16 @@ void TestGUI()
void WalletTests::walletTests()
{
+#ifdef Q_OS_MAC
+ if (QApplication::platformName() == "minimal") {
+ // Disable for mac on "minimal" platform to avoid crashes inside the Qt
+ // framework when it tries to look up unimplemented cocoa functions,
+ // and fails to handle returned nulls
+ // (https://bugreports.qt.io/browse/QTBUG-49686).
+ QWARN("Skipping WalletTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
+ "with 'test_bitcoin-qt -platform cocoa' on mac, or else use a linux or windows build.");
+ return;
+ }
+#endif
TestGUI();
}
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 59332be754..0d070d9e87 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifdef HAVE_CONFIG_H
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/transactiondesc.h>
#include <qt/bitcoinunits.h>
@@ -15,7 +19,7 @@
#include <validation.h>
#include <script/script.h>
#include <timedata.h>
-#include <util.h>
+#include <util/system.h>
#include <wallet/db.h>
#include <wallet/wallet.h>
#include <policy/policy.h>
@@ -23,7 +27,7 @@
#include <stdint.h>
#include <string>
-QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime)
+QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks)
{
if (!status.is_final)
{
@@ -49,11 +53,10 @@ QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const i
QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wallet, TransactionRecord *rec, int unit)
{
int numBlocks;
- int64_t adjustedTime;
interfaces::WalletTxStatus status;
interfaces::WalletOrderForm orderForm;
bool inMempool;
- interfaces::WalletTx wtx = wallet.getWalletTxDetails(rec->hash, status, orderForm, inMempool, numBlocks, adjustedTime);
+ interfaces::WalletTx wtx = wallet.getWalletTxDetails(rec->hash, status, orderForm, inMempool, numBlocks);
QString strHTML;
@@ -65,7 +68,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
CAmount nDebit = wtx.debit;
CAmount nNet = nCredit - nDebit;
- strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx, status, inMempool, numBlocks, adjustedTime);
+ strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx, status, inMempool, numBlocks);
strHTML += "<br>";
strHTML += "<b>" + tr("Date") + ":</b> " + (nTime ? GUIUtil::dateTimeStr(nTime) : "") + "<br>";
@@ -257,6 +260,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
if (r.first == "Message")
strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r.second, true) + "<br>";
+#ifdef ENABLE_BIP70
//
// PaymentRequest info:
//
@@ -271,6 +275,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
strHTML += "<b>" + tr("Merchant") + ":</b> " + GUIUtil::HtmlEscape(merchant) + "<br>";
}
}
+#endif
if (wtx.is_coinbase)
{
diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h
index 80eca7b97c..cf955a433c 100644
--- a/src/qt/transactiondesc.h
+++ b/src/qt/transactiondesc.h
@@ -29,7 +29,7 @@ public:
private:
TransactionDesc() {}
- static QString FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime);
+ static QString FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks);
};
#endif // BITCOIN_QT_TRANSACTIONDESC_H
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index d1a7527ac7..d88cfe52ed 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -158,7 +158,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
return parts;
}
-void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime)
+void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks)
{
// Determine transaction status
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index e187309d3f..470f70e2ab 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -138,7 +138,7 @@ public:
/** Update status from core wallet tx.
*/
- void updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime);
+ void updateStatus(const interfaces::WalletTxStatus& wtx, int numBlocks);
/** Return whether a status update is needed.
*/
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index b4be068904..1983c3bc92 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -18,7 +18,7 @@
#include <interfaces/node.h>
#include <sync.h>
#include <uint256.h>
-#include <util.h>
+#include <util/system.h>
#include <validation.h>
#include <QColor>
@@ -193,9 +193,8 @@ public:
// simply re-use the cached status.
interfaces::WalletTxStatus wtx;
int numBlocks;
- int64_t adjustedTime;
- if (wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, adjustedTime) && rec->statusUpdateNeeded(numBlocks)) {
- rec->updateStatus(wtx, numBlocks, adjustedTime);
+ if (wallet.tryGetTxStatus(rec->hash, wtx, numBlocks) && rec->statusUpdateNeeded(numBlocks)) {
+ rec->updateStatus(wtx, numBlocks);
}
return rec;
}
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 6d08a3b0fb..68410c8bd6 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -106,7 +106,11 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
} else {
amountWidget->setFixedWidth(100);
}
- amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
+ QDoubleValidator *amountValidator = new QDoubleValidator(0, 1e20, 8, this);
+ QLocale amountLocale(QLocale::C);
+ amountLocale.setNumberOptions(QLocale::RejectGroupSeparator);
+ amountValidator->setLocale(amountLocale);
+ amountWidget->setValidator(amountValidator);
hlayout->addWidget(amountWidget);
// Delay before filtering transactions in ms
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 7d903b3b1c..b051dd159b 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -14,13 +14,16 @@
#include <qt/clientmodel.h>
#include <qt/guiconstants.h>
#include <qt/intro.h>
+#ifdef ENABLE_BIP70
#include <qt/paymentrequestplus.h>
+#endif
#include <qt/guiutil.h>
#include <clientversion.h>
#include <init.h>
#include <interfaces/node.h>
-#include <util.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <stdio.h>
@@ -52,9 +55,9 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo
{
setWindowTitle(tr("About %1").arg(tr(PACKAGE_NAME)));
+ std::string licenseInfo = LicenseInfo();
/// HTML-format the license message from the core
- QString licenseInfo = QString::fromStdString(LicenseInfo());
- QString licenseInfoHTML = licenseInfo;
+ QString licenseInfoHTML = QString::fromStdString(LicenseInfo());
// Make URLs clickable
QRegExp uri("<(.*)>", Qt::CaseSensitive, QRegExp::RegExp2);
uri.setMinimal(true); // use non-greedy matching
@@ -64,7 +67,7 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo
ui->aboutMessage->setTextFormat(Qt::RichText);
ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- text = version + "\n" + licenseInfo;
+ text = version + "\n" + QString::fromStdString(FormatParagraph(licenseInfo));
ui->aboutMessage->setText(version + "<br><br>" + licenseInfoHTML);
ui->aboutMessage->setWordWrap(true);
ui->helpMessage->setVisible(false);
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index f7cc94ae32..353da0c9b4 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/walletmodel.h>
#include <qt/addresstablemodel.h>
@@ -16,7 +20,7 @@
#include <interfaces/node.h>
#include <key_io.h>
#include <ui_interface.h>
-#include <util.h> // for GetBoolArg
+#include <util/system.h> // for GetBoolArg
#include <wallet/coincontrol.h>
#include <wallet/wallet.h>
@@ -142,6 +146,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
if (rcp.fSubtractFeeFromAmount)
fSubtractFeeFromAmount = true;
+#ifdef ENABLE_BIP70
if (rcp.paymentRequest.IsInitialized())
{ // PaymentRequest...
CAmount subtotal = 0;
@@ -164,6 +169,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
total += subtotal;
}
else
+#endif
{ // User-entered bitcoin address / amount:
if(!validateAddress(rcp.address))
{
@@ -235,6 +241,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
std::vector<std::pair<std::string, std::string>> vOrderForm;
for (const SendCoinsRecipient &rcp : transaction.getRecipients())
{
+#ifdef ENABLE_BIP70
if (rcp.paymentRequest.IsInitialized())
{
// Make sure any payment requests involved are still valid.
@@ -247,7 +254,9 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
rcp.paymentRequest.SerializeToString(&value);
vOrderForm.emplace_back("PaymentRequest", std::move(value));
}
- else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
+ else
+#endif
+ if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
vOrderForm.emplace_back("Message", rcp.message.toStdString());
}
@@ -266,7 +275,9 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
for (const SendCoinsRecipient &rcp : transaction.getRecipients())
{
// Don't touch the address book when we have a payment request
+#ifdef ENABLE_BIP70
if (!rcp.paymentRequest.IsInitialized())
+#endif
{
std::string strAddress = rcp.address.toStdString();
CTxDestination dest = DecodeDestination(strAddress);
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index b22728c69b..ec4c5a2a6c 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -10,7 +10,13 @@
#include <serialize.h>
#include <script/standard.h>
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#ifdef ENABLE_BIP70
#include <qt/paymentrequestplus.h>
+#endif
#include <qt/walletmodeltransaction.h>
#include <interfaces/wallet.h>
@@ -63,8 +69,14 @@ public:
// If from a payment request, this is used for storing the memo
QString message;
+#ifdef ENABLE_BIP70
// If from a payment request, paymentRequest.IsInitialized() will be true
PaymentRequestPlus paymentRequest;
+#else
+ // If building with BIP70 is disabled, keep the payment request around as
+ // serialized string to ensure load/store is lossless
+ std::string sPaymentRequest;
+#endif
// Empty if no authentication or invalid signature/cert/etc.
QString authenticatedMerchant;
@@ -80,9 +92,11 @@ public:
std::string sAddress = address.toStdString();
std::string sLabel = label.toStdString();
std::string sMessage = message.toStdString();
+#ifdef ENABLE_BIP70
std::string sPaymentRequest;
if (!ser_action.ForRead() && paymentRequest.IsInitialized())
paymentRequest.SerializeToString(&sPaymentRequest);
+#endif
std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString();
READWRITE(this->nVersion);
@@ -98,8 +112,10 @@ public:
address = QString::fromStdString(sAddress);
label = QString::fromStdString(sLabel);
message = QString::fromStdString(sMessage);
+#ifdef ENABLE_BIP70
if (!sPaymentRequest.empty())
paymentRequest.parse(QByteArray::fromRawData(sPaymentRequest.data(), sPaymentRequest.size()));
+#endif
authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant);
}
}
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index de50616499..eb3b0baf08 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -2,6 +2,10 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifdef HAVE_CONFIG_H
+#include <config/bitcoin-config.h>
+#endif
+
#include <qt/walletmodeltransaction.h>
#include <interfaces/node.h>
@@ -46,6 +50,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
{
SendCoinsRecipient& rcp = (*it);
+#ifdef ENABLE_BIP70
if (rcp.paymentRequest.IsInitialized())
{
CAmount subtotal = 0;
@@ -62,6 +67,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
rcp.amount = subtotal;
}
else // normal recipient (no payment request)
+#endif
{
if (i == nChangePosRet)
i++;
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index 75ede2e2a1..289aee847b 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -8,6 +8,7 @@
#include <qt/walletmodel.h>
#include <memory>
+#include <amount.h>
#include <QObject>
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 053e951921..a619992344 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -292,9 +292,7 @@ void WalletView::usedSendingAddresses()
if(!walletModel)
return;
- usedSendingAddressesPage->show();
- usedSendingAddressesPage->raise();
- usedSendingAddressesPage->activateWindow();
+ GUIUtil::bringToFront(usedSendingAddressesPage);
}
void WalletView::usedReceivingAddresses()
@@ -302,9 +300,7 @@ void WalletView::usedReceivingAddresses()
if(!walletModel)
return;
- usedReceivingAddressesPage->show();
- usedReceivingAddressesPage->raise();
- usedReceivingAddressesPage->activateWindow();
+ GUIUtil::bringToFront(usedReceivingAddressesPage);
}
void WalletView::showProgress(const QString &title, int nProgress)
diff --git a/src/qt/winshutdownmonitor.cpp b/src/qt/winshutdownmonitor.cpp
index d732326b33..08cae76add 100644
--- a/src/qt/winshutdownmonitor.cpp
+++ b/src/qt/winshutdownmonitor.cpp
@@ -6,7 +6,7 @@
#if defined(Q_OS_WIN)
#include <shutdown.h>
-#include <util.h>
+#include <util/system.h>
#include <windows.h>
diff --git a/src/random.cpp b/src/random.cpp
index 503d5b3636..a34c70e1d5 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -13,7 +13,7 @@
#endif
#include <logging.h> // for LogPrint()
#include <sync.h> // for WAIT_LOCK
-#include <utiltime.h> // for GetTime()
+#include <util/time.h> // for GetTime()
#include <stdlib.h>
#include <chrono>
@@ -35,7 +35,7 @@
#include <sys/random.h>
#endif
#ifdef HAVE_SYSCTL_ARND
-#include <utilstrencodings.h> // for ARRAYLEN
+#include <util/strencodings.h> // for ARRAYLEN
#include <sys/sysctl.h>
#endif
diff --git a/src/rest.cpp b/src/rest.cpp
index 6ba15172fa..4988e6ed26 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -3,20 +3,21 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <attributes.h>
#include <chain.h>
#include <chainparams.h>
#include <core_io.h>
+#include <httpserver.h>
#include <index/txindex.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
-#include <validation.h>
-#include <httpserver.h>
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <streams.h>
#include <sync.h>
#include <txmempool.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
+#include <validation.h>
#include <version.h>
#include <boost/algorithm/string.hpp>
@@ -105,15 +106,6 @@ static std::string AvailableDataFormatsString()
return formats;
}
-static bool ParseHashStr(const std::string& strReq, uint256& v)
-{
- if (!IsHex(strReq) || (strReq.size() != 64))
- return false;
-
- v.SetHex(strReq);
- return true;
-}
-
static bool CheckWarmup(HTTPRequest* req)
{
std::string statusmessage;
@@ -157,13 +149,13 @@ static bool rest_headers(HTTPRequest* req,
}
}
- CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
- for (const CBlockIndex *pindex : headers) {
- ssHeader << pindex->GetBlockHeader();
- }
-
switch (rf) {
case RetFormat::BINARY: {
+ CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
+ for (const CBlockIndex *pindex : headers) {
+ ssHeader << pindex->GetBlockHeader();
+ }
+
std::string binaryHeader = ssHeader.str();
req->WriteHeader("Content-Type", "application/octet-stream");
req->WriteReply(HTTP_OK, binaryHeader);
@@ -171,6 +163,11 @@ static bool rest_headers(HTTPRequest* req,
}
case RetFormat::HEX: {
+ CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
+ for (const CBlockIndex *pindex : headers) {
+ ssHeader << pindex->GetBlockHeader();
+ }
+
std::string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
@@ -224,11 +221,10 @@ static bool rest_block(HTTPRequest* req,
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
- CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
- ssBlock << block;
-
switch (rf) {
case RetFormat::BINARY: {
+ CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
+ ssBlock << block;
std::string binaryBlock = ssBlock.str();
req->WriteHeader("Content-Type", "application/octet-stream");
req->WriteReply(HTTP_OK, binaryBlock);
@@ -236,6 +232,8 @@ static bool rest_block(HTTPRequest* req,
}
case RetFormat::HEX: {
+ CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
+ ssBlock << block;
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
@@ -360,11 +358,11 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true))
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
- ssTx << tx;
-
switch (rf) {
case RetFormat::BINARY: {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
+ ssTx << tx;
+
std::string binaryTx = ssTx.str();
req->WriteHeader("Content-Type", "application/octet-stream");
req->WriteReply(HTTP_OK, binaryTx);
@@ -372,6 +370,9 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
}
case RetFormat::HEX: {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
+ ssTx << tx;
+
std::string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 6fb04ab005..403e3e397c 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -12,8 +12,8 @@
#include <checkpoints.h>
#include <coins.h>
#include <consensus/validation.h>
-#include <validation.h>
#include <core_io.h>
+#include <hash.h>
#include <index/txindex.h>
#include <key_io.h>
#include <policy/feerate.h>
@@ -21,15 +21,17 @@
#include <policy/rbf.h>
#include <primitives/transaction.h>
#include <rpc/server.h>
+#include <rpc/util.h>
#include <script/descriptor.h>
#include <streams.h>
#include <sync.h>
#include <txdb.h>
#include <txmempool.h>
-#include <util.h>
-#include <utilstrencodings.h>
-#include <hash.h>
+#include <util/strencodings.h>
+#include <util/system.h>
+#include <validation.h>
#include <validationinterface.h>
+#include <versionbitsinfo.h>
#include <warnings.h>
#include <assert.h>
@@ -37,7 +39,6 @@
#include <univalue.h>
-#include <boost/algorithm/string.hpp>
#include <boost/thread/thread.hpp> // boost::thread::interrupt
#include <memory>
@@ -121,8 +122,8 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
if (chainActive.Contains(blockindex))
confirmations = chainActive.Height() - blockindex->nHeight + 1;
result.pushKV("confirmations", confirmations);
- result.pushKV("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
- result.pushKV("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION));
+ result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
+ result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
result.pushKV("weight", (int)::GetBlockWeight(block));
result.pushKV("height", blockindex->nHeight);
result.pushKV("version", block.nVersion);
@@ -161,8 +162,9 @@ static UniValue getblockcount(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getblockcount\n"
- "\nReturns the number of blocks in the longest blockchain.\n"
+ RPCHelpMan{"getblockcount",
+ "\nReturns the number of blocks in the longest blockchain.\n", {}}
+ .ToString() +
"\nResult:\n"
"n (numeric) The current block count\n"
"\nExamples:\n"
@@ -178,10 +180,11 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getbestblockhash\n"
- "\nReturns the hash of the best (tip) block in the longest blockchain.\n"
+ RPCHelpMan{"getbestblockhash",
+ "\nReturns the hash of the best (tip) block in the longest blockchain.\n", {}}
+ .ToString() +
"\nResult:\n"
- "\"hex\" (string) the block hash hex encoded\n"
+ "\"hex\" (string) the block hash, hex-encoded\n"
"\nExamples:\n"
+ HelpExampleCli("getbestblockhash", "")
+ HelpExampleRpc("getbestblockhash", "")
@@ -205,9 +208,13 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "waitfornewblock (timeout)\n"
- "\nWaits for a specific new block and returns useful info about it.\n"
- "\nReturns the current block on timeout or exit.\n"
+ RPCHelpMan{"waitfornewblock",
+ "\nWaits for a specific new block and returns useful info about it.\n"
+ "\nReturns the current block on timeout or exit.\n",
+ {
+ {"timeout", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
"\nResult:\n"
@@ -243,9 +250,14 @@ static UniValue waitforblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "waitforblock <blockhash> (timeout)\n"
- "\nWaits for a specific new block and returns useful info about it.\n"
- "\nReturns the current block on timeout or exit.\n"
+ RPCHelpMan{"waitforblock",
+ "\nWaits for a specific new block and returns useful info about it.\n"
+ "\nReturns the current block on timeout or exit.\n",
+ {
+ {"blockhash", RPCArg::Type::STR, false},
+ {"timeout", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"blockhash\" (required, string) Block hash to wait for.\n"
"2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
@@ -260,7 +272,7 @@ static UniValue waitforblock(const JSONRPCRequest& request)
);
int timeout = 0;
- uint256 hash = uint256S(request.params[0].get_str());
+ uint256 hash(ParseHashV(request.params[0], "blockhash"));
if (!request.params[1].isNull())
timeout = request.params[1].get_int();
@@ -285,12 +297,17 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "waitforblockheight <height> (timeout)\n"
- "\nWaits for (at least) block height and returns the height and hash\n"
- "of the current tip.\n"
- "\nReturns the current block on timeout or exit.\n"
+ RPCHelpMan{"waitforblockheight",
+ "\nWaits for (at least) block height and returns the height and hash\n"
+ "of the current tip.\n"
+ "\nReturns the current block on timeout or exit.\n",
+ {
+ {"height", RPCArg::Type::NUM, false},
+ {"timeout", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
- "1. height (required, int) Block height to wait for (int)\n"
+ "1. height (int, required) Block height to wait for.\n"
"2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
"\nResult:\n"
"{ (json object)\n"
@@ -327,8 +344,9 @@ static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
- "syncwithvalidationinterfacequeue\n"
- "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n"
+ RPCHelpMan{"syncwithvalidationinterfacequeue",
+ "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n", {}}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("syncwithvalidationinterfacequeue","")
+ HelpExampleRpc("syncwithvalidationinterfacequeue","")
@@ -342,8 +360,9 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getdifficulty\n"
- "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
+ RPCHelpMan{"getdifficulty",
+ "\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n", {}}
+ .ToString() +
"\nResult:\n"
"n.nnn (numeric) the proof-of-work difficulty as a multiple of the minimum difficulty.\n"
"\nExamples:\n"
@@ -476,8 +495,12 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getrawmempool ( verbose )\n"
- "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
+ RPCHelpMan{"getrawmempool",
+ "\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n",
+ {
+ {"verbose", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n"
"\nArguments:\n"
"1. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n"
@@ -508,17 +531,22 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "getmempoolancestors txid (verbose)\n"
- "\nIf txid is in the mempool, returns all in-mempool ancestors.\n"
+ RPCHelpMan{"getmempoolancestors",
+ "\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"verbose", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
"2. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n"
- "\nResult (for verbose=false):\n"
+ "\nResult (for verbose = false):\n"
"[ (json array of strings)\n"
" \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n"
" ,...\n"
"]\n"
- "\nResult (for verbose=true):\n"
+ "\nResult (for verbose = true):\n"
"{ (json object)\n"
" \"transactionid\" : { (json object)\n"
+ EntryDescriptionString()
@@ -572,17 +600,22 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "getmempooldescendants txid (verbose)\n"
- "\nIf txid is in the mempool, returns all in-mempool descendants.\n"
+ RPCHelpMan{"getmempooldescendants",
+ "\nIf txid is in the mempool, returns all in-mempool descendants.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"verbose", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
"2. verbose (boolean, optional, default=false) True for a json object, false for array of transaction ids\n"
- "\nResult (for verbose=false):\n"
+ "\nResult (for verbose = false):\n"
"[ (json array of strings)\n"
" \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n"
" ,...\n"
"]\n"
- "\nResult (for verbose=true):\n"
+ "\nResult (for verbose = true):\n"
"{ (json object)\n"
" \"transactionid\" : { (json object)\n"
+ EntryDescriptionString()
@@ -636,8 +669,12 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "getmempoolentry txid\n"
- "\nReturns mempool data for given transaction\n"
+ RPCHelpMan{"getmempoolentry",
+ "\nReturns mempool data for given transaction\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id (must be in mempool)\n"
"\nResult:\n"
@@ -669,8 +706,12 @@ static UniValue getblockhash(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "getblockhash height\n"
- "\nReturns hash of block in best-block-chain at height provided.\n"
+ RPCHelpMan{"getblockhash",
+ "\nReturns hash of block in best-block-chain at height provided.\n",
+ {
+ {"height", RPCArg::Type::NUM, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. height (numeric, required) The height index\n"
"\nResult:\n"
@@ -694,12 +735,17 @@ static UniValue getblockheader(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "getblockheader \"hash\" ( verbose )\n"
- "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
- "If verbose is true, returns an Object with information about blockheader <hash>.\n"
+ RPCHelpMan{"getblockheader",
+ "\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
+ "If verbose is true, returns an Object with information about blockheader <hash>.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, false},
+ {"verbose", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
- "1. \"hash\" (string, required) The block hash\n"
- "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n"
+ "1. \"blockhash\" (string, required) The block hash\n"
+ "2. verbose (boolean, optional, default=true) true for a json object, false for the hex-encoded data\n"
"\nResult (for verbose = true):\n"
"{\n"
" \"hash\" : \"hash\", (string) the block hash (same as provided)\n"
@@ -727,8 +773,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
LOCK(cs_main);
- std::string strHash = request.params[0].get_str();
- uint256 hash(uint256S(strHash));
+ uint256 hash(ParseHashV(request.params[0], "hash"));
bool fVerbose = true;
if (!request.params[1].isNull())
@@ -773,13 +818,18 @@ static UniValue getblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "getblock \"blockhash\" ( verbosity ) \n"
- "\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
- "If verbosity is 1, returns an Object with information about block <hash>.\n"
- "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n"
+ RPCHelpMan{"getblock",
+ "\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n"
+ "If verbosity is 1, returns an Object with information about block <hash>.\n"
+ "If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. \n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, false},
+ {"verbosity", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"blockhash\" (string, required) The block hash\n"
- "2. verbosity (numeric, optional, default=1) 0 for hex encoded data, 1 for a json object, and 2 for json object with transaction data\n"
+ "2. verbosity (numeric, optional, default=1) 0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data\n"
"\nResult (for verbosity = 0):\n"
"\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n"
"\nResult (for verbosity = 1):\n"
@@ -822,8 +872,7 @@ static UniValue getblock(const JSONRPCRequest& request)
LOCK(cs_main);
- std::string strHash = request.params[0].get_str();
- uint256 hash(uint256S(strHash));
+ uint256 hash(ParseHashV(request.params[0], "blockhash"));
int verbosity = 1;
if (!request.params[1].isNull()) {
@@ -926,7 +975,11 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "pruneblockchain\n"
+ RPCHelpMan{"pruneblockchain", "",
+ {
+ {"height", RPCArg::Type::NUM, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"height\" (numeric, required) The block height to prune up to. May be set to a discrete height, or a unix timestamp\n"
" to prune blocks whose block time is at least 2 hours older than the provided timestamp.\n"
@@ -975,9 +1028,11 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "gettxoutsetinfo\n"
- "\nReturns statistics about the unspent transaction output set.\n"
- "Note this call may take some time.\n"
+ RPCHelpMan{"gettxoutsetinfo",
+ "\nReturns statistics about the unspent transaction output set.\n"
+ "Note this call may take some time.\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"height\":n, (numeric) The current block height (index)\n"
@@ -1017,8 +1072,14 @@ UniValue gettxout(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
throw std::runtime_error(
- "gettxout \"txid\" n ( include_mempool )\n"
- "\nReturns details about an unspent transaction output.\n"
+ RPCHelpMan{"gettxout",
+ "\nReturns details about an unspent transaction output.\n",
+ {
+ {"txid", RPCArg::Type::STR, false},
+ {"n", RPCArg::Type::NUM, false},
+ {"include_mempool", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. \"n\" (numeric, required) vout number\n"
@@ -1047,7 +1108,7 @@ UniValue gettxout(const JSONRPCRequest& request)
+ HelpExampleCli("listunspent", "") +
"\nView the details\n"
+ HelpExampleCli("gettxout", "\"txid\" 1") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("gettxout", "\"txid\", 1")
);
@@ -1055,8 +1116,7 @@ UniValue gettxout(const JSONRPCRequest& request)
UniValue ret(UniValue::VOBJ);
- std::string strHash = request.params[0].get_str();
- uint256 hash(uint256S(strHash));
+ uint256 hash(ParseHashV(request.params[0], "txid"));
int n = request.params[1].get_int();
COutPoint out(hash, n);
bool fMempool = true;
@@ -1098,8 +1158,13 @@ static UniValue verifychain(const JSONRPCRequest& request)
int nCheckDepth = gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS);
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
- "verifychain ( checklevel nblocks )\n"
- "\nVerifies blockchain database.\n"
+ RPCHelpMan{"verifychain",
+ "\nVerifies blockchain database.\n",
+ {
+ {"checklevel", RPCArg::Type::NUM, true},
+ {"nblocks", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. checklevel (numeric, optional, 0-4, default=" + strprintf("%d", nCheckLevel) + ") How thorough the block verification is.\n"
"2. nblocks (numeric, optional, default=" + strprintf("%d", nCheckDepth) + ", 0=all) The number of blocks to check.\n"
@@ -1195,8 +1260,9 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getblockchaininfo\n"
- "Returns an object containing various state info regarding blockchain processing.\n"
+ RPCHelpMan{"getblockchaininfo",
+ "Returns an object containing various state info regarding blockchain processing.\n", {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n"
@@ -1312,9 +1378,11 @@ static UniValue getchaintips(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getchaintips\n"
- "Return information about all known tips in the block tree,"
- " including the main chain as well as orphaned branches.\n"
+ RPCHelpMan{"getchaintips",
+ "Return information about all known tips in the block tree,"
+ " including the main chain as well as orphaned branches.\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
@@ -1429,8 +1497,9 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getmempoolinfo\n"
- "\nReturns details on the active state of the TX memory pool.\n"
+ RPCHelpMan{"getmempoolinfo",
+ "\nReturns details on the active state of the TX memory pool.\n", {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"size\": xxxxx, (numeric) Current tx count\n"
@@ -1452,10 +1521,14 @@ static UniValue preciousblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "preciousblock \"blockhash\"\n"
- "\nTreats a block as if it were received before others with the same work.\n"
- "\nA later preciousblock call can override the effect of an earlier one.\n"
- "\nThe effects of preciousblock are not retained across restarts.\n"
+ RPCHelpMan{"preciousblock",
+ "\nTreats a block as if it were received before others with the same work.\n"
+ "\nA later preciousblock call can override the effect of an earlier one.\n"
+ "\nThe effects of preciousblock are not retained across restarts.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"blockhash\" (string, required) the hash of the block to mark as precious\n"
"\nResult:\n"
@@ -1464,8 +1537,7 @@ static UniValue preciousblock(const JSONRPCRequest& request)
+ HelpExampleRpc("preciousblock", "\"blockhash\"")
);
- std::string strHash = request.params[0].get_str();
- uint256 hash(uint256S(strHash));
+ uint256 hash(ParseHashV(request.params[0], "blockhash"));
CBlockIndex* pblockindex;
{
@@ -1490,8 +1562,12 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "invalidateblock \"blockhash\"\n"
- "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n"
+ RPCHelpMan{"invalidateblock",
+ "\nPermanently marks a block as invalid, as if it violated a consensus rule.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"blockhash\" (string, required) the hash of the block to mark as invalid\n"
"\nResult:\n"
@@ -1500,8 +1576,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
+ HelpExampleRpc("invalidateblock", "\"blockhash\"")
);
- std::string strHash = request.params[0].get_str();
- uint256 hash(uint256S(strHash));
+ uint256 hash(ParseHashV(request.params[0], "blockhash"));
CValidationState state;
{
@@ -1529,9 +1604,13 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "reconsiderblock \"blockhash\"\n"
- "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n"
- "This can be used to undo the effects of invalidateblock.\n"
+ RPCHelpMan{"reconsiderblock",
+ "\nRemoves invalidity status of a block and its descendants, reconsider them for activation.\n"
+ "This can be used to undo the effects of invalidateblock.\n",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"blockhash\" (string, required) the hash of the block to reconsider\n"
"\nResult:\n"
@@ -1540,8 +1619,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
+ HelpExampleRpc("reconsiderblock", "\"blockhash\"")
);
- std::string strHash = request.params[0].get_str();
- uint256 hash(uint256S(strHash));
+ uint256 hash(ParseHashV(request.params[0], "blockhash"));
{
LOCK(cs_main);
@@ -1567,8 +1645,13 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
- "getchaintxstats ( nblocks blockhash )\n"
- "\nCompute statistics about the total number and rate of transactions in the chain.\n"
+ RPCHelpMan{"getchaintxstats",
+ "\nCompute statistics about the total number and rate of transactions in the chain.\n",
+ {
+ {"nblocks", RPCArg::Type::NUM, true},
+ {"blockhash", RPCArg::Type::STR_HEX, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. nblocks (numeric, optional) Size of the window in number of blocks (default: one month).\n"
"2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n"
@@ -1594,7 +1677,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
LOCK(cs_main);
pindex = chainActive.Tip();
} else {
- uint256 hash = uint256S(request.params[1].get_str());
+ uint256 hash(ParseHashV(request.params[1], "blockhash"));
LOCK(cs_main);
pindex = LookupBlockIndex(hash);
if (!pindex) {
@@ -1697,10 +1780,20 @@ static UniValue getblockstats(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) {
throw std::runtime_error(
- "getblockstats hash_or_height ( stats )\n"
- "\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
- "It won't work for some heights with pruning.\n"
- "It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n"
+ RPCHelpMan{"getblockstats",
+ "\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
+ "It won't work for some heights with pruning.\n"
+ "It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n",
+ {
+ {"hash_or_height", RPCArg::Type::NUM, false},
+ {"stats", RPCArg::Type::ARR,
+ {
+ {"height", RPCArg::Type::STR, true},
+ {"time", RPCArg::Type::STR, true},
+ },
+ true, "stats"},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"hash_or_height\" (string or numeric, required) The block hash or height of the target block\n"
"2. \"stats\" (array, optional) Values to plot, by default all values (see result below)\n"
@@ -1768,8 +1861,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
pindex = chainActive[height];
} else {
- const std::string strHash = request.params[0].get_str();
- const uint256 hash(uint256S(strHash));
+ const uint256 hash(ParseHashV(request.params[0], "hash_or_height"));
pindex = LookupBlockIndex(hash);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
@@ -1804,6 +1896,10 @@ static UniValue getblockstats(const JSONRPCRequest& request)
const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "feerate_percentiles", "minfeerate", "maxfeerate");
const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight");
+ if (loop_inputs && !g_txindex) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled");
+ }
+
CAmount maxfee = 0;
CAmount maxfeerate = 0;
CAmount minfee = MAX_MONEY;
@@ -1831,7 +1927,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
if (loop_outputs) {
for (const CTxOut& out : tx->vout) {
tx_total_out += out.nValue;
- utxo_size_inc += GetSerializeSize(out, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
+ utxo_size_inc += GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
}
}
@@ -1867,10 +1963,6 @@ static UniValue getblockstats(const JSONRPCRequest& request)
}
if (loop_inputs) {
-
- if (!g_txindex) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled");
- }
CAmount tx_total_in = 0;
for (const CTxIn& in : tx->vin) {
CTransactionRef tx_in;
@@ -1882,7 +1974,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
CTxOut prevoutput = tx_in->vout[in.prevout.n];
tx_total_in += prevoutput.nValue;
- utxo_size_inc -= GetSerializeSize(prevoutput, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
+ utxo_size_inc -= GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
}
CAmount txfee = tx_total_in - tx_total_out;
@@ -1962,8 +2054,9 @@ static UniValue savemempool(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
- "savemempool\n"
- "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n"
+ RPCHelpMan{"savemempool",
+ "\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n", {}}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("savemempool", "")
+ HelpExampleRpc("savemempool", "")
@@ -2045,20 +2138,34 @@ UniValue scantxoutset(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "scantxoutset <action> ( <scanobjects> )\n"
- "\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
- "\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
- "Examples of output descriptors are:\n"
- " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n"
- " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
- " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n"
- " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
- " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
- "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
- "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n"
- "unhardened or hardened child keys.\n"
- "In the latter case, a range needs to be specified by below if different from 1000.\n"
- "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"
+ RPCHelpMan{"scantxoutset",
+ "\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
+ "\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
+ "Examples of output descriptors are:\n"
+ " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n"
+ " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
+ " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n"
+ " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
+ " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
+ "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
+ "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n"
+ "unhardened or hardened child keys.\n"
+ "In the latter case, a range needs to be specified by below if different from 1000.\n"
+ "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n",
+ {
+ {"action", RPCArg::Type::STR, false},
+ {"scanobjects", RPCArg::Type::ARR,
+ {
+ {"descriptor", RPCArg::Type::OBJ,
+ {
+ {"desc", RPCArg::Type::STR, false},
+ {"range", RPCArg::Type::NUM, true},
+ },
+ false, "scanobjects"},
+ },
+ false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"action\" (string, required) The action to execute\n"
" \"start\" for starting a scan\n"
@@ -2080,6 +2187,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
" \"txid\" : \"transactionid\", (string) The transaction id\n"
" \"vout\": n, (numeric) the vout value\n"
" \"scriptPubKey\" : \"script\", (string) the script key\n"
+ " \"desc\" : \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n"
" \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
" \"height\" : n, (numeric) Height of the unspent transaction output\n"
" }\n"
@@ -2114,6 +2222,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
}
std::set<CScript> needles;
+ std::map<CScript, std::string> descriptors;
CAmount total_in = 0;
// loop through the scan objects
@@ -2146,7 +2255,11 @@ UniValue scantxoutset(const JSONRPCRequest& request)
if (!desc->Expand(i, provider, scripts, provider)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
}
- needles.insert(scripts.begin(), scripts.end());
+ for (const auto& script : scripts) {
+ std::string inferred = InferDescriptor(script, provider)->ToString();
+ needles.emplace(script);
+ descriptors.emplace(std::move(script), std::move(inferred));
+ }
}
}
@@ -2179,6 +2292,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
unspent.pushKV("txid", outpoint.hash.GetHex());
unspent.pushKV("vout", (int32_t)outpoint.n);
unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
+ unspent.pushKV("desc", descriptors[txo.scriptPubKey]);
unspent.pushKV("amount", ValueFromAmount(txo.nValue));
unspent.pushKV("height", (int32_t)coin.nHeight);
@@ -2192,6 +2306,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
return result;
}
+// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -2227,6 +2342,7 @@ static const CRPCCommand commands[] =
{ "hidden", "waitforblockheight", &waitforblockheight, {"height","timeout"} },
{ "hidden", "syncwithvalidationinterfacequeue", &syncwithvalidationinterfacequeue, {} },
};
+// clang-format on
void RegisterBlockchainRPCCommands(CRPCTable &t)
{
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 784d6416cf..2b99808c07 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -5,7 +5,7 @@
#include <rpc/client.h>
#include <rpc/protocol.h>
-#include <util.h>
+#include <util/system.h>
#include <set>
#include <stdint.h>
@@ -18,6 +18,7 @@ public:
std::string paramName; //!< parameter name
};
+// clang-format off
/**
* Specify a (method, idx, name) here if the argument is a non-string RPC
* argument and needs to be converted from JSON.
@@ -44,7 +45,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "listreceivedbyaddress", 0, "minconf" },
{ "listreceivedbyaddress", 1, "include_empty" },
{ "listreceivedbyaddress", 2, "include_watchonly" },
- { "listreceivedbyaddress", 3, "address_filter" },
{ "listreceivedbylabel", 0, "minconf" },
{ "listreceivedbylabel", 1, "include_empty" },
{ "listreceivedbylabel", 2, "include_watchonly" },
@@ -147,7 +147,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "logging", 0, "include" },
{ "logging", 1, "exclude" },
{ "disconnectnode", 1, "nodeid" },
- { "addwitnessaddress", 1, "p2sh" },
// Echo with conversion (For testing only)
{ "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" },
@@ -162,7 +161,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "rescanblockchain", 0, "start_height"},
{ "rescanblockchain", 1, "stop_height"},
{ "createwallet", 1, "disable_private_keys"},
+ { "getnodeaddresses", 0, "count"},
};
+// clang-format on
class CRPCConvertTable
{
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 621b86eec8..c0287ec17f 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -18,12 +18,14 @@
#include <rpc/blockchain.h>
#include <rpc/mining.h>
#include <rpc/server.h>
+#include <rpc/util.h>
#include <shutdown.h>
#include <txmempool.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
+#include <util/system.h>
#include <validation.h>
#include <validationinterface.h>
+#include <versionbitsinfo.h>
#include <warnings.h>
#include <memory>
@@ -85,10 +87,15 @@ static UniValue getnetworkhashps(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
- "getnetworkhashps ( nblocks height )\n"
- "\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"
+ 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",
+ {
+ {"nblocks", RPCArg::Type::NUM, true},
+ {"height", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. nblocks (numeric, optional, default=120) The number of blocks, or -1 for blocks since last difficulty change.\n"
"2. height (numeric, optional, default=-1) To estimate at the time of the given height.\n"
@@ -155,8 +162,14 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
throw std::runtime_error(
- "generatetoaddress nblocks address (maxtries)\n"
- "\nMine blocks immediately to a specified address (before the RPC call returns)\n"
+ RPCHelpMan{"generatetoaddress",
+ "\nMine blocks immediately to a specified address (before the RPC call returns)\n",
+ {
+ {"nblocks", RPCArg::Type::NUM, false},
+ {"address", RPCArg::Type::STR, false},
+ {"maxtries", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. nblocks (numeric, required) How many blocks are generated immediately.\n"
"2. address (string, required) The address to send the newly generated bitcoin to.\n"
@@ -166,6 +179,8 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
"\nExamples:\n"
"\nGenerate 11 blocks to myaddress\n"
+ HelpExampleCli("generatetoaddress", "11 \"myaddress\"")
+ + "If you are running the bitcoin core wallet, you can get a new address to send the newly generated bitcoin to with:\n"
+ + HelpExampleCli("getnewaddress", "")
);
int nGenerate = request.params[0].get_int();
@@ -189,8 +204,9 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getmininginfo\n"
- "\nReturns a json object containing mining-related information."
+ RPCHelpMan{"getmininginfo",
+ "\nReturns a json object containing mining-related information.", {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"blocks\": nnn, (numeric) The current block\n"
@@ -228,8 +244,14 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
throw std::runtime_error(
- "prioritisetransaction <txid> <dummy value> <fee delta>\n"
- "Accepts the transaction into mined blocks at a higher (or lower) priority\n"
+ RPCHelpMan{"prioritisetransaction",
+ "Accepts the transaction into mined blocks at a higher (or lower) priority\n",
+ {
+ {"txid", RPCArg::Type::STR, false},
+ {"dummy", RPCArg::Type::NUM, false},
+ {"fee_delta", RPCArg::Type::NUM, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id.\n"
"2. dummy (numeric, optional) API-Compatibility for previous API. Must be zero or null.\n"
@@ -247,7 +269,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
LOCK(cs_main);
- uint256 hash = ParseHashStr(request.params[0].get_str(), "txid");
+ uint256 hash(ParseHashV(request.params[0], "txid"));
CAmount nAmount = request.params[2].get_int64();
if (!(request.params[1].isNull() || request.params[1].get_real() == 0)) {
@@ -291,15 +313,32 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getblocktemplate ( TemplateRequest )\n"
- "\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"
- " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n"
-
+ 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"
+ " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
+ {
+ {"template_request", RPCArg::Type::OBJ,
+ {
+ {"mode", RPCArg::Type::STR, true},
+ {"capabilities", RPCArg::Type::ARR,
+ {
+ {"support", RPCArg::Type::STR, true},
+ },
+ true},
+ {"rules", RPCArg::Type::ARR,
+ {
+ {"support", RPCArg::Type::STR, true},
+ },
+ true},
+ },
+ true, "\"template_request\""},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. template_request (json object, optional) A json object in the following spec\n"
" {\n"
@@ -361,8 +400,8 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
"}\n"
"\nExamples:\n"
- + HelpExampleCli("getblocktemplate", "")
- + HelpExampleRpc("getblocktemplate", "")
+ + HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}")
+ + HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
);
LOCK(cs_main);
@@ -455,7 +494,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
// Format: <hashBestChain><nTransactionsUpdatedLast>
std::string lpstr = lpval.get_str();
- hashWatchedChain.SetHex(lpstr.substr(0, 64));
+ hashWatchedChain = ParseHashV(lpstr.substr(0, 64), "longpollid");
nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
}
else
@@ -699,10 +738,14 @@ static UniValue submitblock(const JSONRPCRequest& request)
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "submitblock \"hexdata\" ( \"dummy\" )\n"
- "\nAttempts to submit new block to network.\n"
- "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
-
+ RPCHelpMan{"submitblock",
+ "\nAttempts to submit new block to network.\n"
+ "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
+ {
+ {"hexdata", RPCArg::Type::STR_HEX, false},
+ {"dummy", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments\n"
"1. \"hexdata\" (string, required) the hex-encoded block data to submit\n"
"2. \"dummy\" (optional) dummy value, for compatibility with BIP22. This value is ignored.\n"
@@ -750,11 +793,7 @@ static UniValue submitblock(const JSONRPCRequest& request)
RegisterValidationInterface(&sc);
bool accepted = ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block);
UnregisterValidationInterface(&sc);
- if (!new_block) {
- if (!accepted) {
- // TODO Maybe pass down fNewBlock to AcceptBlockHeader, so it is properly set to true in this case?
- return "invalid";
- }
+ if (!new_block && accepted) {
return "duplicate";
}
if (!sc.found) {
@@ -767,9 +806,13 @@ static UniValue submitheader(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "submitheader \"hexdata\"\n"
- "\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid."
- "\nThrows when the header is invalid.\n"
+ 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",
+ {
+ {"hexdata", RPCArg::Type::STR_HEX, false},
+ }}
+ .ToString() +
"\nArguments\n"
"1. \"hexdata\" (string, required) the hex-encoded block header data\n"
"\nResult:\n"
@@ -799,21 +842,20 @@ static UniValue submitheader(const JSONRPCRequest& request)
throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason());
}
-static UniValue estimatefee(const JSONRPCRequest& request)
-{
- throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee was removed in v0.17.\n"
- "Clients should use estimatesmartfee.");
-}
-
static UniValue estimatesmartfee(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "estimatesmartfee conf_target (\"estimate_mode\")\n"
- "\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"
- "in BIP 141 (witness data is discounted).\n"
+ 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"
+ "in BIP 141 (witness data is discounted).\n",
+ {
+ {"conf_target", RPCArg::Type::NUM, false},
+ {"estimate_mode", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. conf_target (numeric) Confirmation target in blocks (1 - 1008)\n"
"2. \"estimate_mode\" (string, optional, default=CONSERVATIVE) The fee estimate mode.\n"
@@ -822,7 +864,7 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
" higher feerate and is more likely to be sufficient for the desired\n"
" target, but is not as responsive to short term drops in the\n"
" prevailing fee market. Must be one of:\n"
- " \"UNSET\" (defaults to CONSERVATIVE)\n"
+ " \"UNSET\"\n"
" \"ECONOMICAL\"\n"
" \"CONSERVATIVE\"\n"
"\nResult:\n"
@@ -870,14 +912,19 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "estimaterawfee conf_target (threshold)\n"
- "\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"
- " and the results it returns will change if the internal implementation changes.\n"
- "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
- "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"
- "defined in BIP 141 (witness data is discounted).\n"
+ 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"
+ " and the results it returns will change if the internal implementation changes.\n"
+ "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
+ "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"
+ "defined in BIP 141 (witness data is discounted).\n",
+ {
+ {"conf_target", RPCArg::Type::NUM, false},
+ {"threshold", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. conf_target (numeric) Confirmation target in blocks (1 - 1008)\n"
"2. threshold (numeric, optional) The proportion of transactions in a given feerate range that must have been\n"
@@ -968,6 +1015,7 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
return result;
}
+// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -981,11 +1029,11 @@ static const CRPCCommand commands[] =
{ "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} },
- { "hidden", "estimatefee", &estimatefee, {} },
{ "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} },
{ "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} },
};
+// clang-format on
void RegisterMiningRPCCommands(CRPCTable &t)
{
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index b7d05cef11..5543035885 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -17,8 +17,8 @@
#include <rpc/server.h>
#include <rpc/util.h>
#include <timedata.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <warnings.h>
#include <stdint.h>
@@ -32,19 +32,23 @@ static UniValue validateaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "validateaddress \"address\"\n"
- "\nReturn information about the given bitcoin address.\n"
- "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n"
- "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n"
- "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n"
- "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n"
+ RPCHelpMan{"validateaddress",
+ "\nReturn information about the given bitcoin address.\n"
+ "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n"
+ "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n"
+ "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n"
+ "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n",
+ {
+ {"address", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to validate\n"
"\nResult:\n"
"{\n"
" \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
" \"address\" : \"address\", (string) The bitcoin address validated\n"
- " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n"
+ " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n"
" \"isscript\" : true|false, (boolean) If the key is a script\n"
" \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
" \"witness_version\" : version (numeric, optional) The version number of the witness program\n"
@@ -99,7 +103,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
"\nExamples:\n"
"\nCreate a multisig address from 2 public keys\n"
+ HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("createmultisig", "2, \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"")
;
throw std::runtime_error(msg);
@@ -142,8 +146,14 @@ static UniValue verifymessage(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
throw std::runtime_error(
- "verifymessage \"address\" \"signature\" \"message\"\n"
- "\nVerify a signed message\n"
+ RPCHelpMan{"verifymessage",
+ "\nVerify a signed message\n",
+ {
+ {"address", RPCArg::Type::STR, false},
+ {"signature", RPCArg::Type::STR, false},
+ {"message", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to use for the signature.\n"
"2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n"
@@ -157,7 +167,7 @@ static UniValue verifymessage(const JSONRPCRequest& request)
+ HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
"\nVerify the signature\n"
+ HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
- "\nAs json rpc\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"signature\", \"my message\"")
);
@@ -198,8 +208,13 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
- "signmessagewithprivkey \"privkey\" \"message\"\n"
- "\nSign a message with the private key of an address\n"
+ RPCHelpMan{"signmessagewithprivkey",
+ "\nSign a message with the private key of an address\n",
+ {
+ {"privkey", RPCArg::Type::STR, false},
+ {"message", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"privkey\" (string, required) The private key to sign the message with.\n"
"2. \"message\" (string, required) The message to create a signature of.\n"
@@ -210,7 +225,7 @@ static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
+ HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") +
"\nVerify the signature\n"
+ HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
- "\nAs json rpc\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")
);
@@ -237,8 +252,12 @@ static UniValue setmocktime(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "setmocktime timestamp\n"
- "\nSet the local time to given timestamp (-regtest only)\n"
+ RPCHelpMan{"setmocktime",
+ "\nSet the local time to given timestamp (-regtest only)\n",
+ {
+ {"timestamp", RPCArg::Type::NUM, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n"
" Pass 0 to go back to using the system time."
@@ -299,8 +318,12 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request)
*/
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getmemoryinfo (\"mode\")\n"
- "Returns an object containing information about memory usage.\n"
+ RPCHelpMan{"getmemoryinfo",
+ "Returns an object containing information about memory usage.\n",
+ {
+ {"mode", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"Arguments:\n"
"1. \"mode\" determines what kind of information is returned. This argument is optional, the default mode is \"stats\".\n"
" - \"stats\" returns general statistics about memory usage in the daemon.\n"
@@ -361,7 +384,7 @@ UniValue logging(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
- "logging ( <include> <exclude> )\n"
+ RPCHelpMan{"logging",
"Gets and sets the logging configuration.\n"
"When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n"
"When called with arguments, adds or removes categories from debug logging and return the lists above.\n"
@@ -371,6 +394,12 @@ UniValue logging(const JSONRPCRequest& request)
"In addition, the following are available as category names with special meanings:\n"
" - \"all\", \"1\" : represent all logging categories.\n"
" - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n"
+ ,
+ {
+ {"include", RPCArg::Type::STR, true},
+ {"exclude", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"include\" (array of strings, optional) A json array of categories to add debug logging\n"
" [\n"
@@ -430,22 +459,25 @@ static UniValue echo(const JSONRPCRequest& request)
{
if (request.fHelp)
throw std::runtime_error(
- "echo|echojson \"message\" ...\n"
- "\nSimply echo back the input arguments. This command is for testing.\n"
- "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in"
- "bitcoin-cli and the GUI. There is no server-side difference."
- );
+ RPCHelpMan{"echo|echojson ...",
+ "\nSimply echo back the input arguments. This command is for testing.\n"
+ "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in "
+ "bitcoin-cli and the GUI. There is no server-side difference.",
+ {}}
+ .ToString() +
+ "");
return request.params;
}
+// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "control", "getmemoryinfo", &getmemoryinfo, {"mode"} },
{ "control", "logging", &logging, {"include", "exclude"}},
{ "util", "validateaddress", &validateaddress, {"address"} },
- { "util", "createmultisig", &createmultisig, {"nrequired","keys"} },
+ { "util", "createmultisig", &createmultisig, {"nrequired","keys","address_type"} },
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} },
{ "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} },
@@ -454,6 +486,7 @@ static const CRPCCommand commands[] =
{ "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
{ "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
};
+// clang-format on
void RegisterMiscRPCCommands(CRPCTable &t)
{
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 169a452659..795b9b089b 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -7,17 +7,18 @@
#include <chainparams.h>
#include <clientversion.h>
#include <core_io.h>
-#include <validation.h>
#include <net.h>
#include <net_processing.h>
#include <netbase.h>
#include <policy/policy.h>
#include <rpc/protocol.h>
+#include <rpc/util.h>
#include <sync.h>
#include <timedata.h>
#include <ui_interface.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
+#include <util/system.h>
+#include <validation.h>
#include <version.h>
#include <warnings.h>
@@ -27,8 +28,9 @@ static UniValue getconnectioncount(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getconnectioncount\n"
- "\nReturns the number of connections to other nodes.\n"
+ RPCHelpMan{"getconnectioncount",
+ "\nReturns the number of connections to other nodes.\n", {}}
+ .ToString() +
"\nResult:\n"
"n (numeric) The connection count\n"
"\nExamples:\n"
@@ -46,10 +48,12 @@ static UniValue ping(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "ping\n"
- "\nRequests that a ping be sent to all other nodes, to measure ping time.\n"
- "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n"
- "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n"
+ RPCHelpMan{"ping",
+ "\nRequests that a ping be sent to all other nodes, to measure ping time.\n"
+ "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n"
+ "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n",
+ {}}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("ping", "")
+ HelpExampleRpc("ping", "")
@@ -69,8 +73,9 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getpeerinfo\n"
- "\nReturns data about each connected network node as a json array of objects.\n"
+ RPCHelpMan{"getpeerinfo",
+ "\nReturns data about each connected network node as a json array of objects.\n", {}}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
@@ -200,11 +205,16 @@ static UniValue addnode(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2 ||
(strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
throw std::runtime_error(
- "addnode \"node\" \"add|remove|onetry\"\n"
- "\nAttempts to add or remove a node from the addnode list.\n"
- "Or try a connection to a node once.\n"
- "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n"
- "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n"
+ RPCHelpMan{"addnode",
+ "\nAttempts to add or remove a node from the addnode list.\n"
+ "Or try a connection to a node once.\n"
+ "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n"
+ "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n",
+ {
+ {"node", RPCArg::Type::STR, false},
+ {"command", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"node\" (string, required) The node (see getpeerinfo for nodes)\n"
"2. \"command\" (string, required) 'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once\n"
@@ -243,13 +253,18 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3)
throw std::runtime_error(
- "disconnectnode \"[address]\" [nodeid]\n"
- "\nImmediately disconnects from the specified peer node.\n"
- "\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
- "\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n"
+ RPCHelpMan{"disconnectnode",
+ "\nImmediately disconnects from the specified peer node.\n"
+ "\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
+ "\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n",
+ {
+ {"address", RPCArg::Type::STR, true},
+ {"nodeid", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"address\" (string, optional) The IP address/port of the node\n"
- "2. \"nodeid\" (number, optional) The node ID (see getpeerinfo for node IDs)\n"
+ "2. nodeid (number, optional) The node ID (see getpeerinfo for node IDs)\n"
"\nExamples:\n"
+ HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"")
+ HelpExampleCli("disconnectnode", "\"\" 1")
@@ -286,9 +301,13 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getaddednodeinfo ( \"node\" )\n"
- "\nReturns information about the given added node, or all added nodes\n"
- "(note that onetry addnodes are not listed here)\n"
+ RPCHelpMan{"getaddednodeinfo",
+ "\nReturns information about the given added node, or all added nodes\n"
+ "(note that onetry addnodes are not listed here)\n",
+ {
+ {"node", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"node\" (string, optional) If provided, return information about this specific node, otherwise all nodes are returned.\n"
"\nResult:\n"
@@ -353,9 +372,11 @@ static UniValue getnettotals(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
- "getnettotals\n"
- "\nReturns information about network traffic, including bytes in, bytes out,\n"
- "and current time.\n"
+ RPCHelpMan{"getnettotals",
+ "\nReturns information about network traffic, including bytes in, bytes out,\n"
+ "and current time.\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"totalbytesrecv\": n, (numeric) Total bytes received\n"
@@ -419,8 +440,9 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getnetworkinfo\n"
- "Returns an object containing various state info regarding P2P networking.\n"
+ RPCHelpMan{"getnetworkinfo",
+ "Returns an object containing various state info regarding P2P networking.\n", {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"version\": xxxxx, (numeric) the server version\n"
@@ -499,8 +521,15 @@ static UniValue setban(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 ||
(strCommand != "add" && strCommand != "remove"))
throw std::runtime_error(
- "setban \"subnet\" \"add|remove\" (bantime) (absolute)\n"
- "\nAttempts to add or remove an IP/Subnet from the banned list.\n"
+ RPCHelpMan{"setban",
+ "\nAttempts to add or remove an IP/Subnet from the banned list.\n",
+ {
+ {"subnet", RPCArg::Type::STR, false},
+ {"command", RPCArg::Type::STR, false},
+ {"bantime", RPCArg::Type::NUM, true},
+ {"absolute", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"subnet\" (string, required) The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)\n"
"2. \"command\" (string, required) 'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list\n"
@@ -559,8 +588,9 @@ static UniValue listbanned(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "listbanned\n"
- "\nList all banned IPs/Subnets.\n"
+ RPCHelpMan{"listbanned",
+ "\nList all banned IPs/Subnets.\n", {}}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("listbanned", "")
+ HelpExampleRpc("listbanned", "")
@@ -592,8 +622,9 @@ static UniValue clearbanned(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "clearbanned\n"
- "\nClear all banned IPs.\n"
+ RPCHelpMan{"clearbanned",
+ "\nClear all banned IPs.\n", {}}
+ .ToString() +
"\nExamples:\n"
+ HelpExampleCli("clearbanned", "")
+ HelpExampleRpc("clearbanned", "")
@@ -610,8 +641,12 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "setnetworkactive true|false\n"
- "\nDisable/enable all p2p network activity.\n"
+ RPCHelpMan{"setnetworkactive",
+ "\nDisable/enable all p2p network activity.\n",
+ {
+ {"state", RPCArg::Type::BOOL, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"state\" (boolean, required) true to enable networking, false to disable\n"
);
@@ -626,6 +661,63 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
return g_connman->GetNetworkActive();
}
+static UniValue getnodeaddresses(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 1) {
+ throw std::runtime_error(
+ RPCHelpMan{"getnodeaddresses",
+ "\nReturn known addresses which can potentially be used to find new nodes in the network\n",
+ {
+ {"count", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
+ "\nArguments:\n"
+ "1. \"count\" (numeric, optional) How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) +
+ " or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses. (default = 1)\n"
+ "\nResult:\n"
+ "[\n"
+ " {\n"
+ " \"time\": ttt, (numeric) Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen\n"
+ " \"services\": n, (numeric) The services offered\n"
+ " \"address\": \"host\", (string) The address of the node\n"
+ " \"port\": n (numeric) The port of the node\n"
+ " }\n"
+ " ,....\n"
+ "]\n"
+ "\nExamples:\n"
+ + HelpExampleCli("getnodeaddresses", "8")
+ + HelpExampleRpc("getnodeaddresses", "8")
+ );
+ }
+ if (!g_connman) {
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ }
+
+ int count = 1;
+ if (!request.params[0].isNull()) {
+ count = request.params[0].get_int();
+ if (count <= 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range");
+ }
+ }
+ // returns a shuffled list of CAddress
+ std::vector<CAddress> vAddr = g_connman->GetAddresses();
+ UniValue ret(UniValue::VARR);
+
+ int address_return_count = std::min<int>(count, vAddr.size());
+ for (int i = 0; i < address_return_count; ++i) {
+ UniValue obj(UniValue::VOBJ);
+ const CAddress& addr = vAddr[i];
+ obj.pushKV("time", (int)addr.nTime);
+ obj.pushKV("services", (uint64_t)addr.nServices);
+ obj.pushKV("address", addr.ToStringIP());
+ obj.pushKV("port", addr.GetPort());
+ ret.push_back(obj);
+ }
+ return ret;
+}
+
+// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -641,7 +733,9 @@ static const CRPCCommand commands[] =
{ "network", "listbanned", &listbanned, {} },
{ "network", "clearbanned", &clearbanned, {} },
{ "network", "setnetworkactive", &setnetworkactive, {"state"} },
+ { "network", "getnodeaddresses", &getnodeaddresses, {"count"} },
};
+// clang-format on
void RegisterNetRPCCommands(CRPCTable &t)
{
diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp
index dab63ab263..23999b305a 100644
--- a/src/rpc/protocol.cpp
+++ b/src/rpc/protocol.cpp
@@ -7,13 +7,11 @@
#include <random.h>
#include <tinyformat.h>
-#include <util.h>
-#include <utilstrencodings.h>
-#include <utiltime.h>
+#include <util/system.h>
+#include <util/strencodings.h>
+#include <util/time.h>
#include <version.h>
-#include <fstream>
-
/**
* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
* but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
@@ -85,9 +83,9 @@ bool GenerateAuthCookie(std::string *cookie_out)
/** the umask determines what permissions are used to create this file -
* these are set to 077 in init.cpp unless overridden with -sysperms.
*/
- std::ofstream file;
+ fsbridge::ofstream file;
fs::path filepath_tmp = GetAuthCookieFile(true);
- file.open(filepath_tmp.string().c_str());
+ file.open(filepath_tmp);
if (!file.is_open()) {
LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath_tmp.string());
return false;
@@ -109,10 +107,10 @@ bool GenerateAuthCookie(std::string *cookie_out)
bool GetAuthCookie(std::string *cookie_out)
{
- std::ifstream file;
+ fsbridge::ifstream file;
std::string cookie;
fs::path filepath = GetAuthCookieFile();
- file.open(filepath.string().c_str());
+ file.open(filepath);
if (!file.is_open())
return false;
std::getline(file, cookie);
@@ -128,7 +126,7 @@ void DeleteAuthCookie()
try {
fs::remove(GetAuthCookieFile());
} catch (const fs::filesystem_error& e) {
- LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what());
+ LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
}
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 6acf383646..41f3ac6b4a 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -9,10 +9,9 @@
#include <consensus/validation.h>
#include <core_io.h>
#include <index/txindex.h>
-#include <keystore.h>
-#include <validation.h>
-#include <validationinterface.h>
+#include <init.h>
#include <key_io.h>
+#include <keystore.h>
#include <merkleblock.h>
#include <net.h>
#include <policy/policy.h>
@@ -20,13 +19,16 @@
#include <primitives/transaction.h>
#include <rpc/rawtransaction.h>
#include <rpc/server.h>
+#include <rpc/util.h>
#include <script/script.h>
#include <script/script_error.h>
#include <script/sign.h>
#include <script/standard.h>
#include <txmempool.h>
#include <uint256.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
+#include <validation.h>
+#include <validationinterface.h>
#include <future>
#include <stdint.h>
@@ -64,13 +66,18 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "getrawtransaction \"txid\" ( verbose \"blockhash\" )\n"
-
- "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n"
- "enabled, it also works for blockchain transactions. If the block which contains the transaction\n"
- "is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n"
- "provided, only that block will be searched and if the transaction is in the mempool or other\n"
- "blocks, or if this node does not have the given block available, the transaction will not be found.\n"
+ RPCHelpMan{"getrawtransaction",
+ "\nNOTE: By default this function only works for mempool transactions. If the -txindex option is\n"
+ "enabled, it also works for blockchain transactions. If the block which contains the transaction\n"
+ "is known, its hash can be provided even for nodes without -txindex. Note that if a blockhash is\n"
+ "provided, only that block will be searched and if the transaction is in the mempool or other\n"
+ "blocks, or if this node does not have the given block available, the transaction will not be found.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"verbose", RPCArg::Type::BOOL, true},
+ {"blockhash", RPCArg::Type::STR_HEX, true},
+ }}
+ .ToString() +
"DEPRECATED: for now, it also works for transactions with unspent outputs.\n"
"\nReturn the raw transaction data.\n"
@@ -204,12 +211,21 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2))
throw std::runtime_error(
- "gettxoutproof [\"txid\",...] ( blockhash )\n"
- "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
- "\nNOTE: By default this function only works sometimes. This is when there is an\n"
- "unspent output in the utxo for this transaction. To make it always work,\n"
- "you need to maintain a transaction index, using the -txindex command line option or\n"
- "specify the block in which the transaction is included manually (by blockhash).\n"
+ RPCHelpMan{"gettxoutproof",
+ "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
+ "\nNOTE: By default this function only works sometimes. This is when there is an\n"
+ "unspent output in the utxo for this transaction. To make it always work,\n"
+ "you need to maintain a transaction index, using the -txindex command line option or\n"
+ "specify the block in which the transaction is included manually (by blockhash).\n",
+ {
+ {"txids", RPCArg::Type::ARR,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ },
+ false},
+ {"blockhash", RPCArg::Type::STR_HEX, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txids\" (string) A json array of txids to filter\n"
" [\n"
@@ -226,9 +242,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
UniValue txids = request.params[0].get_array();
for (unsigned int idx = 0; idx < txids.size(); idx++) {
const UniValue& txid = txids[idx];
- if (txid.get_str().length() != 64 || !IsHex(txid.get_str()))
- throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid txid ")+txid.get_str());
- uint256 hash(uint256S(txid.get_str()));
+ uint256 hash(ParseHashV(txid, "txid"));
if (setTxids.count(hash))
throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ")+txid.get_str());
setTxids.insert(hash);
@@ -239,7 +253,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
uint256 hashBlock;
if (!request.params[1].isNull()) {
LOCK(cs_main);
- hashBlock = uint256S(request.params[1].get_str());
+ hashBlock = ParseHashV(request.params[1], "blockhash");
pblockindex = LookupBlockIndex(hashBlock);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
@@ -298,9 +312,13 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "verifytxoutproof \"proof\"\n"
- "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
- "and throwing an RPC error if the block is not in our best chain\n"
+ RPCHelpMan{"verifytxoutproof",
+ "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n"
+ "and throwing an RPC error if the block is not in our best chain\n",
+ {
+ {"proof", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n"
"\nResult:\n"
@@ -348,7 +366,7 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
if (!locktime.isNull()) {
int64_t nLockTime = locktime.get_int64();
- if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max())
+ if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
rawTx.nLockTime = nLockTime;
}
@@ -370,18 +388,18 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
uint32_t nSequence;
if (rbfOptIn) {
- nSequence = MAX_BIP125_RBF_SEQUENCE;
+ nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */
} else if (rawTx.nLockTime) {
- nSequence = std::numeric_limits<uint32_t>::max() - 1;
+ nSequence = CTxIn::SEQUENCE_FINAL - 1;
} else {
- nSequence = std::numeric_limits<uint32_t>::max();
+ nSequence = CTxIn::SEQUENCE_FINAL;
}
// set the sequence number if passed in the parameters object
const UniValue& sequenceObj = find_value(o, "sequence");
if (sequenceObj.isNum()) {
int64_t seqNr64 = sequenceObj.get_int64();
- if (seqNr64 < 0 || seqNr64 > std::numeric_limits<uint32_t>::max()) {
+ if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
} else {
nSequence = (uint32_t)seqNr64;
@@ -468,13 +486,13 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
" \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
" },\n"
" {\n"
- " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex-encoded data\n"
" }\n"
" ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.\n"
" ]\n"
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
- "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
+ "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125-replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
"\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n"
@@ -505,9 +523,13 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "decoderawtransaction \"hexstring\" ( iswitness )\n"
- "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n"
-
+ RPCHelpMan{"decoderawtransaction",
+ "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, false},
+ {"iswitness", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"hexstring\" (string, required) The transaction hex string\n"
"2. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction\n"
@@ -580,14 +602,18 @@ static UniValue decodescript(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "decodescript \"hexstring\"\n"
- "\nDecode a hex-encoded script.\n"
+ RPCHelpMan{"decodescript",
+ "\nDecode a hex-encoded script.\n",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, false},
+ }}
+ .ToString() +
"\nArguments:\n"
- "1. \"hexstring\" (string) the hex encoded script\n"
+ "1. \"hexstring\" (string) the hex-encoded script\n"
"\nResult:\n"
"{\n"
" \"asm\":\"asm\", (string) Script public key\n"
- " \"hex\":\"hex\", (string) hex encoded public key\n"
+ " \"hex\":\"hex\", (string) hex-encoded public key\n"
" \"type\":\"type\", (string) The output type\n"
" \"reqSigs\": n, (numeric) The required signatures\n"
" \"addresses\": [ (json array of string)\n"
@@ -673,14 +699,20 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
-
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "combinerawtransaction [\"hexstring\",...]\n"
- "\nCombine multiple partially signed transactions into one transaction.\n"
- "The combined transaction may be another partially signed transaction or a \n"
- "fully signed transaction."
-
+ RPCHelpMan{"combinerawtransaction",
+ "\nCombine multiple partially signed transactions into one transaction.\n"
+ "The combined transaction may be another partially signed transaction or a \n"
+ "fully signed transaction.",
+ {
+ {"txs", RPCArg::Type::ARR,
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, false},
+ },
+ false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txs\" (string) A json array of hex strings of partially signed transactions\n"
" [\n"
@@ -756,7 +788,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
return EncodeHexTx(mergedTx);
}
-UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType)
+UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType)
{
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
@@ -899,13 +931,35 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
- "signrawtransactionwithkey \"hexstring\" [\"privatekey1\",...] ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n"
- "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
- "The second argument is an array of base58-encoded private\n"
- "keys that will be the only keys used to sign the transaction.\n"
- "The third optional argument (may be null) is an array of previous transaction outputs that\n"
- "this transaction depends on but may not yet be in the block chain.\n"
-
+ RPCHelpMan{"signrawtransactionwithkey",
+ "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
+ "The second argument is an array of base58-encoded private\n"
+ "keys that will be the only keys used to sign the transaction.\n"
+ "The third optional argument (may be null) is an array of previous transaction outputs that\n"
+ "this transaction depends on but may not yet be in the block chain.\n",
+ {
+ {"hexstring", RPCArg::Type::STR, false},
+ {"privkeys", RPCArg::Type::ARR,
+ {
+ {"privatekey", RPCArg::Type::STR_HEX, false},
+ },
+ false},
+ {"prevtxs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"vout", RPCArg::Type::NUM, false},
+ {"scriptPubKey", RPCArg::Type::STR_HEX, false},
+ {"redeemScript", RPCArg::Type::STR_HEX, false},
+ {"amount", RPCArg::Type::AMOUNT, false},
+ },
+ true},
+ },
+ true},
+ {"sighashtype", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"hexstring\" (string, required) The transaction hex string\n"
"2. \"privkeys\" (string, required) A json array of base58-encoded private keys for signing\n"
@@ -924,7 +978,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
" }\n"
" ,...\n"
" ]\n"
- "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n"
+ "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of:\n"
" \"ALL\"\n"
" \"NONE\"\n"
" \"SINGLE\"\n"
@@ -971,7 +1025,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
keystore.AddKey(key);
}
- return SignTransaction(mtx, request.params[2], &keystore, true, request.params[3]);
+ return SignTransaction(*g_rpc_interfaces->chain, mtx, request.params[2], &keystore, true, request.params[3]);
}
UniValue signrawtransaction(const JSONRPCRequest& request)
@@ -986,9 +1040,14 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "sendrawtransaction \"hexstring\" ( allowhighfees )\n"
- "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
- "\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n"
+ RPCHelpMan{"sendrawtransaction",
+ "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n"
+ "\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, false},
+ {"allowhighfees", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of the raw transaction)\n"
"2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
@@ -1001,7 +1060,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
"\nSend the transaction (signed hex)\n"
+ HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
);
@@ -1083,7 +1142,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
throw std::runtime_error(
// clang-format off
"testmempoolaccept [\"rawtxs\"] ( allowhighfees )\n"
- "\nReturns if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
+ "\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
"\nThis checks if the transaction violates the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n"
"\nArguments:\n"
@@ -1106,7 +1165,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
+ HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
"\nTest acceptance of the transaction (signed hex)\n"
+ HelpExampleCli("testmempoolaccept", "\"signedhex\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
// clang-format on
);
@@ -1179,9 +1238,12 @@ UniValue decodepsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "decodepsbt \"psbt\"\n"
- "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n"
-
+ RPCHelpMan{"decodepsbt",
+ "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n",
+ {
+ {"psbt", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"psbt\" (string, required) The PSBT base64 string\n"
@@ -1454,9 +1516,17 @@ UniValue combinepsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "combinepsbt [\"psbt\",...]\n"
- "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
- "Implements the Combiner role.\n"
+ RPCHelpMan{"combinepsbt",
+ "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
+ "Implements the Combiner role.\n",
+ {
+ {"txs", RPCArg::Type::ARR,
+ {
+ {"psbt", RPCArg::Type::STR_HEX, false},
+ },
+ false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txs\" (string) A json array of base64 strings of partially signed transactions\n"
" [\n"
@@ -1507,11 +1577,16 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "finalizepsbt \"psbt\" ( extract )\n"
- "Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
- "network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n"
- "created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n"
- "Implements the Finalizer and Extractor roles.\n"
+ RPCHelpMan{"finalizepsbt",
+ "Finalize the inputs of a PSBT. If the transaction is fully signed, it will produce a\n"
+ "network serialized transaction which can be broadcast with sendrawtransaction. Otherwise a PSBT will be\n"
+ "created which has the final_scriptSig and final_scriptWitness fields filled for inputs that are complete.\n"
+ "Implements the Finalizer and Extractor roles.\n",
+ {
+ {"psbt", RPCArg::Type::STR, false},
+ {"extract", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"psbt\" (string) A base64 string of a PSBT\n"
"2. \"extract\" (boolean, optional, default=true) If true and the transaction is complete, \n"
@@ -1538,12 +1613,13 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
}
- // Get all of the previous transactions
+ // Finalize input signatures -- in case we have partial signatures that add up to a complete
+ // signature, but have not combined them yet (e.g. because the combiner that created this
+ // PartiallySignedTransaction did not understand them), this will combine them into a final
+ // script.
bool complete = true;
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- PSBTInput& input = psbtx.inputs.at(i);
-
- complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, i, 1);
+ complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
}
UniValue result(UniValue::VOBJ);
@@ -1556,10 +1632,10 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
mtx.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness;
}
ssTx << mtx;
- result.pushKV("hex", HexStr(ssTx.begin(), ssTx.end()));
+ result.pushKV("hex", HexStr(ssTx.str()));
} else {
ssTx << psbtx;
- result.pushKV("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()));
+ result.pushKV("psbt", EncodeBase64(ssTx.str()));
}
result.pushKV("complete", complete);
@@ -1570,9 +1646,39 @@ UniValue createpsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
- "createpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
- "\nCreates a transaction in the Partially Signed Transaction format.\n"
- "Implements the Creator role.\n"
+ RPCHelpMan{"createpsbt",
+ "\nCreates a transaction in the Partially Signed Transaction format.\n"
+ "Implements the Creator role.\n",
+ {
+ {"inputs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"vout", RPCArg::Type::NUM, false},
+ {"sequence", RPCArg::Type::NUM, true},
+ },
+ false},
+ },
+ false},
+ {"outputs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"address", RPCArg::Type::AMOUNT, false},
+ },
+ true},
+ {"", RPCArg::Type::OBJ,
+ {
+ {"data", RPCArg::Type::STR_HEX, false},
+ },
+ true},
+ },
+ false},
+ {"locktime", RPCArg::Type::NUM, true},
+ {"replaceable", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"inputs\" (array, required) A json array of json objects\n"
" [\n"
@@ -1589,7 +1695,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
" \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
" },\n"
" {\n"
- " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex-encoded data\n"
" }\n"
" ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.\n"
@@ -1635,9 +1741,15 @@ UniValue converttopsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "converttopsbt \"hexstring\" ( permitsigdata iswitness )\n"
- "\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
- "createpsbt and walletcreatefundedpsbt should be used for new applications.\n"
+ RPCHelpMan{"converttopsbt",
+ "\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
+ "createpsbt and walletcreatefundedpsbt should be used for new applications.\n",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, false},
+ {"permitsigdata", RPCArg::Type::BOOL, true},
+ {"iswitness", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of a raw transaction\n"
"2. permitsigdata (boolean, optional, default=false) If true, any signatures in the input will be discarded and conversion.\n"
@@ -1671,7 +1783,7 @@ UniValue converttopsbt(const JSONRPCRequest& request)
// Remove all scriptSigs and scriptWitnesses from inputs
for (CTxIn& input : tx.vin) {
- if ((!input.scriptSig.empty() || !input.scriptWitness.IsNull()) && (request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()))) {
+ if ((!input.scriptSig.empty() || !input.scriptWitness.IsNull()) && !permitsigdata) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Inputs must not have scriptSigs and scriptWitnesses");
}
input.scriptSig.clear();
@@ -1695,6 +1807,7 @@ UniValue converttopsbt(const JSONRPCRequest& request)
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
}
+// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -1716,6 +1829,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
};
+// clang-format on
void RegisterRawTransactionRPCCommands(CRPCTable &t)
{
diff --git a/src/rpc/rawtransaction.h b/src/rpc/rawtransaction.h
index 924611ed5a..52d701d1c3 100644
--- a/src/rpc/rawtransaction.h
+++ b/src/rpc/rawtransaction.h
@@ -9,8 +9,12 @@ class CBasicKeyStore;
struct CMutableTransaction;
class UniValue;
+namespace interfaces {
+class Chain;
+} // namespace interfaces
+
/** Sign a transaction with the given keystore and previous transactions */
-UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
+UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
/** Create a transaction from univalue parameters */
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 7d7e83fea8..c565094a10 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -8,11 +8,12 @@
#include <fs.h>
#include <key_io.h>
#include <random.h>
+#include <rpc/util.h>
#include <shutdown.h>
#include <sync.h>
#include <ui_interface.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
+#include <util/system.h>
#include <boost/bind.hpp>
#include <boost/signals2/signal.hpp>
@@ -116,16 +117,12 @@ CAmount AmountFromValue(const UniValue& value)
uint256 ParseHashV(const UniValue& v, std::string strName)
{
- std::string strHex;
- if (v.isStr())
- strHex = v.get_str();
+ std::string strHex(v.get_str());
+ if (64 != strHex.length())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
if (!IsHex(strHex)) // Note: IsHex("") is false
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
- if (64 != strHex.length())
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d)", strName, 64, strHex.length()));
- uint256 result;
- result.SetHex(strHex);
- return result;
+ return uint256S(strHex);
}
uint256 ParseHashO(const UniValue& o, std::string strKey)
{
@@ -207,8 +204,12 @@ UniValue help(const JSONRPCRequest& jsonRequest)
{
if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw std::runtime_error(
- "help ( \"command\" )\n"
- "\nList all commands, or get help for a specified command.\n"
+ RPCHelpMan{"help",
+ "\nList all commands, or get help for a specified command.\n",
+ {
+ {"command", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"command\" (string, optional) The command to get help on\n"
"\nResult:\n"
@@ -228,8 +229,9 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
// Accept the deprecated and ignored 'detach' boolean argument
if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw std::runtime_error(
- "stop\n"
- "\nStop Bitcoin server.");
+ RPCHelpMan{"stop",
+ "\nStop Bitcoin server.", {}}
+ .ToString());
// Event loop will exit after current HTTP requests have been handled, so
// this reply will get back to the client.
StartShutdown();
@@ -238,10 +240,11 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
static UniValue uptime(const JSONRPCRequest& jsonRequest)
{
- if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
+ if (jsonRequest.fHelp || jsonRequest.params.size() > 0)
throw std::runtime_error(
- "uptime\n"
- "\nReturns the total uptime of the server.\n"
+ RPCHelpMan{"uptime",
+ "\nReturns the total uptime of the server.\n", {}}
+ .ToString() +
"\nResult:\n"
"ttt (numeric) The number of seconds that the server has been running\n"
"\nExamples:\n"
@@ -252,6 +255,7 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
return GetTime() - GetStartupTime();
}
+// clang-format off
/**
* Call Table
*/
@@ -263,6 +267,7 @@ static const CRPCCommand vRPCCommands[] =
{ "control", "stop", &stop, {} },
{ "control", "uptime", &uptime, {} },
};
+// clang-format on
CRPCTable::CRPCTable()
{
@@ -538,7 +543,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface)
timerInterface = nullptr;
}
-void RPCRunLater(const std::string& name, std::function<void(void)> func, int64_t nSeconds)
+void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds)
{
if (!timerInterface)
throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 15d0ec80f5..2d62a76f3c 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -110,7 +110,7 @@ public:
* This is needed to cope with the case in which there is no HTTP server, but
* only GUI RPC console, and to break the dependency of pcserver on httprpc.
*/
- virtual RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) = 0;
+ virtual RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) = 0;
};
/** Set the factory function for timers */
@@ -124,7 +124,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface);
* Run func nSeconds from now.
* Overrides previous timer <name> (if any).
*/
-void RPCRunLater(const std::string& name, std::function<void(void)> func, int64_t nSeconds);
+void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds);
typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest);
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index ba414bf3f5..0b6bbcb1dc 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -7,7 +7,9 @@
#include <rpc/protocol.h>
#include <rpc/util.h>
#include <tinyformat.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
+
+InitInterfaces* g_rpc_interfaces = nullptr;
// Converts a hex string to a public key if possible
CPubKey HexToPubKey(const std::string& hex_in)
@@ -126,3 +128,99 @@ UniValue DescribeAddress(const CTxDestination& dest)
{
return boost::apply_visitor(DescribeAddressVisitor(), dest);
}
+
+std::string RPCHelpMan::ToString() const
+{
+ std::string ret;
+
+ ret += m_name;
+ bool is_optional{false};
+ for (const auto& arg : m_args) {
+ ret += " ";
+ if (arg.m_optional) {
+ if (!is_optional) ret += "( ";
+ is_optional = true;
+ } else {
+ // Currently we still support unnamed arguments, so any argument following an optional argument must also be optional
+ // If support for positional arguments is deprecated in the future, remove this line
+ assert(!is_optional);
+ }
+ ret += arg.ToString();
+ }
+ if (is_optional) ret += " )";
+ ret += "\n";
+
+ ret += m_description;
+
+ return ret;
+}
+
+std::string RPCArg::ToStringObj() const
+{
+ std::string res = "\"" + m_name + "\":";
+ switch (m_type) {
+ case Type::STR:
+ return res + "\"str\"";
+ case Type::STR_HEX:
+ return res + "\"hex\"";
+ case Type::NUM:
+ return res + "n";
+ case Type::AMOUNT:
+ return res + "amount";
+ case Type::BOOL:
+ return res + "bool";
+ case Type::ARR:
+ res += "[";
+ for (const auto& i : m_inner) {
+ res += i.ToString() + ",";
+ }
+ return res + "...]";
+ case Type::OBJ:
+ case Type::OBJ_USER_KEYS:
+ // Currently unused, so avoid writing dead code
+ assert(false);
+
+ // no default case, so the compiler can warn about missing cases
+ }
+ assert(false);
+}
+
+std::string RPCArg::ToString() const
+{
+ if (!m_oneline_description.empty()) return m_oneline_description;
+
+ switch (m_type) {
+ case Type::STR_HEX:
+ case Type::STR: {
+ return "\"" + m_name + "\"";
+ }
+ case Type::NUM:
+ case Type::AMOUNT:
+ case Type::BOOL: {
+ return m_name;
+ }
+ case Type::OBJ:
+ case Type::OBJ_USER_KEYS: {
+ std::string res;
+ for (size_t i = 0; i < m_inner.size();) {
+ res += m_inner[i].ToStringObj();
+ if (++i < m_inner.size()) res += ",";
+ }
+ if (m_type == Type::OBJ) {
+ return "{" + res + "}";
+ } else {
+ return "{" + res + ",...}";
+ }
+ }
+ case Type::ARR: {
+ std::string res;
+ for (const auto& i : m_inner) {
+ res += i.ToString() + ",";
+ }
+ return "[" + res + "...]";
+ }
+
+ // no default case, so the compiler can warn about missing cases
+ }
+ assert(false);
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 0a3a156e45..b1ab64247c 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -9,14 +9,18 @@
#include <script/standard.h>
#include <univalue.h>
-#include <boost/variant/static_visitor.hpp>
-
#include <string>
#include <vector>
class CKeyStore;
class CPubKey;
class CScript;
+struct InitInterfaces;
+
+//! Pointers to interfaces that need to be accessible from RPC methods. Due to
+//! limitations of the RPC framework, there's currently no direct way to pass in
+//! state to RPC method implementations.
+extern InitInterfaces* g_rpc_interfaces;
CPubKey HexToPubKey(const std::string& hex_in);
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
@@ -24,4 +28,55 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey
UniValue DescribeAddress(const CTxDestination& dest);
+struct RPCArg {
+ enum class Type {
+ OBJ,
+ ARR,
+ STR,
+ NUM,
+ BOOL,
+ OBJ_USER_KEYS, //!< Special type where the user must set the keys e.g. to define multiple addresses; as opposed to e.g. an options object where the keys are predefined
+ AMOUNT, //!< Special type representing a floating point amount (can be either NUM or STR)
+ STR_HEX, //!< Special type that is a STR with only hex chars
+ };
+ const std::string m_name; //!< The name of the arg (can be empty for inner args)
+ const Type m_type;
+ const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts
+ const bool m_optional;
+ const std::string m_oneline_description; //!< Should be empty unless it is supposed to override the auto-generated summary line
+
+ RPCArg(const std::string& name, const Type& type, const bool optional, const std::string& oneline_description = "")
+ : m_name{name}, m_type{type}, m_optional{optional}, m_oneline_description{oneline_description}
+ {
+ assert(type != Type::ARR && type != Type::OBJ);
+ }
+
+ RPCArg(const std::string& name, const Type& type, const std::vector<RPCArg>& inner, const bool optional, const std::string& oneline_description = "")
+ : m_name{name}, m_type{type}, m_inner{inner}, m_optional{optional}, m_oneline_description{oneline_description}
+ {
+ assert(type == Type::ARR || type == Type::OBJ);
+ }
+
+ std::string ToString() const;
+
+private:
+ std::string ToStringObj() const;
+};
+
+class RPCHelpMan
+{
+public:
+ RPCHelpMan(const std::string& name, const std::string& description, const std::vector<RPCArg>& args)
+ : m_name{name}, m_description{description}, m_args{args}
+ {
+ }
+
+ std::string ToString() const;
+
+private:
+ const std::string m_name;
+ const std::string m_description;
+ const std::vector<RPCArg> m_args;
+};
+
#endif // BITCOIN_RPC_UTIL_H
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index 89dfc2b363..552391d7d0 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -159,7 +159,7 @@ void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() {
}
void SingleThreadedSchedulerClient::ProcessQueue() {
- std::function<void (void)> callback;
+ std::function<void ()> callback;
{
LOCK(m_cs_callbacks_pending);
if (m_are_callbacks_running) return;
@@ -187,7 +187,7 @@ void SingleThreadedSchedulerClient::ProcessQueue() {
callback();
}
-void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void (void)> func) {
+void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void ()> func) {
assert(m_pscheduler);
{
diff --git a/src/scheduler.h b/src/scheduler.h
index 953d6c37de..6c45f508ec 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -40,7 +40,7 @@ public:
CScheduler();
~CScheduler();
- typedef std::function<void(void)> Function;
+ typedef std::function<void()> Function;
// Call func at/after time t
void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now());
@@ -99,7 +99,7 @@ private:
CScheduler *m_pscheduler;
CCriticalSection m_cs_callbacks_pending;
- std::list<std::function<void (void)>> m_callbacks_pending GUARDED_BY(m_cs_callbacks_pending);
+ std::list<std::function<void ()>> m_callbacks_pending GUARDED_BY(m_cs_callbacks_pending);
bool m_are_callbacks_running GUARDED_BY(m_cs_callbacks_pending) = false;
void MaybeScheduleProcessQueue();
@@ -114,7 +114,7 @@ public:
* Practically, this means that callbacks can behave as if they are executed
* in order by a single thread.
*/
- void AddToProcessQueue(std::function<void (void)> func);
+ void AddToProcessQueue(std::function<void ()> func);
// Processes all remaining queue members on the calling thread, blocking until queue is empty
// Must be called after the CScheduler has no remaining processing threads!
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index 01cfeb23f1..15e204062f 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -88,7 +88,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
CTransaction tx(deserialize, stream);
if (nIn >= tx.vin.size())
return set_error(err, bitcoinconsensus_ERR_TX_INDEX);
- if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
+ if (GetSerializeSize(tx, PROTOCOL_VERSION) != txToLen)
return set_error(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH);
// Regardless of the verification result, the tx did not error.
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 45b097dde6..ca80d3451f 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -10,8 +10,8 @@
#include <script/standard.h>
#include <span.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <memory>
#include <string>
@@ -41,7 +41,7 @@ struct PubkeyProvider
virtual ~PubkeyProvider() = default;
/** Derive a public key. */
- virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const = 0;
+ virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const = 0;
/** Whether this represent multiple public keys at different positions. */
virtual bool IsRange() const = 0;
@@ -56,6 +56,37 @@ struct PubkeyProvider
virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
};
+class OriginPubkeyProvider final : public PubkeyProvider
+{
+ KeyOriginInfo m_origin;
+ std::unique_ptr<PubkeyProvider> m_provider;
+
+ std::string OriginString() const
+ {
+ return HexStr(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint)) + FormatKeyPath(m_origin.path);
+ }
+
+public:
+ OriginPubkeyProvider(KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider) : m_origin(std::move(info)), m_provider(std::move(provider)) {}
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
+ {
+ if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
+ std::copy(std::begin(m_origin.fingerprint), std::end(m_origin.fingerprint), info.fingerprint);
+ info.path.insert(info.path.begin(), m_origin.path.begin(), m_origin.path.end());
+ return true;
+ }
+ bool IsRange() const override { return m_provider->IsRange(); }
+ size_t GetSize() const override { return m_provider->GetSize(); }
+ std::string ToString() const override { return "[" + OriginString() + "]" + m_provider->ToString(); }
+ bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
+ {
+ std::string sub;
+ if (!m_provider->ToPrivateString(arg, sub)) return false;
+ ret = "[" + OriginString() + "]" + std::move(sub);
+ return true;
+ }
+};
+
/** An object representing a parsed constant public key in a descriptor. */
class ConstPubkeyProvider final : public PubkeyProvider
{
@@ -63,9 +94,12 @@ class ConstPubkeyProvider final : public PubkeyProvider
public:
ConstPubkeyProvider(const CPubKey& pubkey) : m_pubkey(pubkey) {}
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
{
- out = m_pubkey;
+ key = m_pubkey;
+ info.path.clear();
+ CKeyID keyid = m_pubkey.GetID();
+ std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
return true;
}
bool IsRange() const override { return false; }
@@ -98,7 +132,7 @@ class BIP32PubkeyProvider final : public PubkeyProvider
CKey key;
if (!arg.GetKey(m_extkey.pubkey.GetID(), key)) return false;
ret.nDepth = m_extkey.nDepth;
- std::copy(m_extkey.vchFingerprint, m_extkey.vchFingerprint + 4, ret.vchFingerprint);
+ std::copy(m_extkey.vchFingerprint, m_extkey.vchFingerprint + sizeof(ret.vchFingerprint), ret.vchFingerprint);
ret.nChild = m_extkey.nChild;
ret.chaincode = m_extkey.chaincode;
ret.key = key;
@@ -118,27 +152,32 @@ public:
BIP32PubkeyProvider(const CExtPubKey& extkey, KeyPath path, DeriveType derive) : m_extkey(extkey), m_path(std::move(path)), m_derive(derive) {}
bool IsRange() const override { return m_derive != DeriveType::NO; }
size_t GetSize() const override { return 33; }
- bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const override
+ bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& key, KeyOriginInfo& info) const override
{
if (IsHardened()) {
- CExtKey key;
- if (!GetExtKey(arg, key)) return false;
+ CExtKey extkey;
+ if (!GetExtKey(arg, extkey)) return false;
for (auto entry : m_path) {
- key.Derive(key, entry);
+ extkey.Derive(extkey, entry);
}
- if (m_derive == DeriveType::UNHARDENED) key.Derive(key, pos);
- if (m_derive == DeriveType::HARDENED) key.Derive(key, pos | 0x80000000UL);
- out = key.Neuter().pubkey;
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
+ if (m_derive == DeriveType::HARDENED) extkey.Derive(extkey, pos | 0x80000000UL);
+ key = extkey.Neuter().pubkey;
} else {
// TODO: optimize by caching
- CExtPubKey key = m_extkey;
+ CExtPubKey extkey = m_extkey;
for (auto entry : m_path) {
- key.Derive(key, entry);
+ extkey.Derive(extkey, entry);
}
- if (m_derive == DeriveType::UNHARDENED) key.Derive(key, pos);
+ if (m_derive == DeriveType::UNHARDENED) extkey.Derive(extkey, pos);
assert(m_derive != DeriveType::HARDENED);
- out = key.pubkey;
+ key = extkey.pubkey;
}
+ CKeyID keyid = m_extkey.pubkey.GetID();
+ std::copy(keyid.begin(), keyid.begin() + sizeof(info.fingerprint), info.fingerprint);
+ info.path = m_path;
+ if (m_derive == DeriveType::UNHARDENED) info.path.push_back((uint32_t)pos);
+ if (m_derive == DeriveType::HARDENED) info.path.push_back(((uint32_t)pos) | 0x80000000L);
return true;
}
std::string ToString() const override
@@ -172,6 +211,7 @@ public:
AddressDescriptor(CTxDestination destination) : m_destination(std::move(destination)) {}
bool IsRange() const override { return false; }
+ bool IsSolvable() const override { return false; }
std::string ToString() const override { return "addr(" + EncodeDestination(m_destination) + ")"; }
bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; }
bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
@@ -190,6 +230,7 @@ public:
RawDescriptor(CScript script) : m_script(std::move(script)) {}
bool IsRange() const override { return false; }
+ bool IsSolvable() const override { return false; }
std::string ToString() const override { return "raw(" + HexStr(m_script.begin(), m_script.end()) + ")"; }
bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; }
bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
@@ -210,6 +251,7 @@ public:
SingleKeyDescriptor(std::unique_ptr<PubkeyProvider> prov, const std::function<CScript(const CPubKey&)>& fn, const std::string& name) : m_script_fn(fn), m_fn_name(name), m_provider(std::move(prov)) {}
bool IsRange() const override { return m_provider->IsRange(); }
+ bool IsSolvable() const override { return true; }
std::string ToString() const override { return m_fn_name + "(" + m_provider->ToString() + ")"; }
bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
{
@@ -221,9 +263,11 @@ public:
bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
{
CPubKey key;
- if (!m_provider->GetPubKey(pos, arg, key)) return false;
+ KeyOriginInfo info;
+ if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
output_scripts = std::vector<CScript>{m_script_fn(key)};
- out.pubkeys.emplace(key.GetID(), std::move(key));
+ out.origins.emplace(key.GetID(), std::move(info));
+ out.pubkeys.emplace(key.GetID(), key);
return true;
}
};
@@ -249,6 +293,8 @@ public:
return false;
}
+ bool IsSolvable() const override { return true; }
+
std::string ToString() const override
{
std::string ret = strprintf("multi(%i", m_threshold);
@@ -272,15 +318,19 @@ public:
bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
{
- std::vector<CPubKey> pubkeys;
- pubkeys.reserve(m_providers.size());
+ std::vector<std::pair<CPubKey, KeyOriginInfo>> entries;
+ entries.reserve(m_providers.size());
+ // Construct temporary data in `entries`, to avoid producing output in case of failure.
for (const auto& p : m_providers) {
- CPubKey key;
- if (!p->GetPubKey(pos, arg, key)) return false;
- pubkeys.push_back(key);
+ entries.emplace_back();
+ if (!p->GetPubKey(pos, arg, entries.back().first, entries.back().second)) return false;
}
- for (const CPubKey& key : pubkeys) {
- out.pubkeys.emplace(key.GetID(), std::move(key));
+ std::vector<CPubKey> pubkeys;
+ pubkeys.reserve(entries.size());
+ for (auto& entry : entries) {
+ pubkeys.push_back(entry.first);
+ out.origins.emplace(entry.first.GetID(), std::move(entry.second));
+ out.pubkeys.emplace(entry.first.GetID(), entry.first);
}
output_scripts = std::vector<CScript>{GetScriptForMultisig(m_threshold, pubkeys)};
return true;
@@ -298,6 +348,7 @@ public:
ConvertorDescriptor(std::unique_ptr<Descriptor> descriptor, const std::function<CScript(const CScript&)>& fn, const std::string& name) : m_convert_fn(fn), m_fn_name(name), m_descriptor(std::move(descriptor)) {}
bool IsRange() const override { return m_descriptor->IsRange(); }
+ bool IsSolvable() const override { return m_descriptor->IsSolvable(); }
std::string ToString() const override { return m_fn_name + "(" + m_descriptor->ToString() + ")"; }
bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
{
@@ -332,6 +383,7 @@ public:
ComboDescriptor(std::unique_ptr<PubkeyProvider> provider) : m_provider(std::move(provider)) {}
bool IsRange() const override { return m_provider->IsRange(); }
+ bool IsSolvable() const override { return true; }
std::string ToString() const override { return "combo(" + m_provider->ToString() + ")"; }
bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
{
@@ -343,13 +395,15 @@ public:
bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
{
CPubKey key;
- if (!m_provider->GetPubKey(pos, arg, key)) return false;
+ KeyOriginInfo info;
+ if (!m_provider->GetPubKey(pos, arg, key, info)) return false;
CKeyID keyid = key.GetID();
{
CScript p2pk = GetScriptForRawPubKey(key);
CScript p2pkh = GetScriptForDestination(keyid);
output_scripts = std::vector<CScript>{std::move(p2pk), std::move(p2pkh)};
out.pubkeys.emplace(keyid, key);
+ out.origins.emplace(keyid, std::move(info));
}
if (key.IsCompressed()) {
CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(keyid));
@@ -431,7 +485,7 @@ std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
}
/** Parse a key path, being passed a split list of elements (the first element is ignored). */
-bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
+NODISCARD bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
{
for (size_t i = 1; i < split.size(); ++i) {
Span<const char> elem = split[i];
@@ -447,7 +501,8 @@ bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
return true;
}
-std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
+/** Parse a public key that excludes origin information. */
+std::unique_ptr<PubkeyProvider> ParsePubkeyInner(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
{
auto split = Split(sp, '/');
std::string str(split[0].begin(), split[0].end());
@@ -484,6 +539,28 @@ std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool per
return MakeUnique<BIP32PubkeyProvider>(extpubkey, std::move(path), type);
}
+/** Parse a public key including origin information (if enabled). */
+std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
+{
+ auto origin_split = Split(sp, ']');
+ if (origin_split.size() > 2) return nullptr;
+ if (origin_split.size() == 1) return ParsePubkeyInner(origin_split[0], permit_uncompressed, out);
+ if (origin_split[0].size() < 1 || origin_split[0][0] != '[') return nullptr;
+ auto slash_split = Split(origin_split[0].subspan(1), '/');
+ if (slash_split[0].size() != 8) return nullptr;
+ std::string fpr_hex = std::string(slash_split[0].begin(), slash_split[0].end());
+ if (!IsHex(fpr_hex)) return nullptr;
+ auto fpr_bytes = ParseHex(fpr_hex);
+ KeyOriginInfo info;
+ static_assert(sizeof(info.fingerprint) == 4, "Fingerprint must be 4 bytes");
+ assert(fpr_bytes.size() == 4);
+ std::copy(fpr_bytes.begin(), fpr_bytes.end(), info.fingerprint);
+ if (!ParseKeyPath(slash_split, info.path)) return nullptr;
+ auto provider = ParsePubkeyInner(origin_split[1], permit_uncompressed, out);
+ if (!provider) return nullptr;
+ return MakeUnique<OriginPubkeyProvider>(std::move(info), std::move(provider));
+}
+
/** Parse a script in a particular context. */
std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
{
@@ -555,6 +632,80 @@ std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext
return nullptr;
}
+std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptContext, const SigningProvider& provider)
+{
+ std::unique_ptr<PubkeyProvider> key_provider = MakeUnique<ConstPubkeyProvider>(pubkey);
+ KeyOriginInfo info;
+ if (provider.GetKeyOrigin(pubkey.GetID(), info)) {
+ return MakeUnique<OriginPubkeyProvider>(std::move(info), std::move(key_provider));
+ }
+ return key_provider;
+}
+
+std::unique_ptr<Descriptor> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
+{
+ std::vector<std::vector<unsigned char>> data;
+ txnouttype txntype = Solver(script, data);
+
+ if (txntype == TX_PUBKEY) {
+ CPubKey pubkey(data[0].begin(), data[0].end());
+ if (pubkey.IsValid()) {
+ return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2PKGetScript, "pk");
+ }
+ }
+ if (txntype == TX_PUBKEYHASH) {
+ uint160 hash(data[0]);
+ CKeyID keyid(hash);
+ CPubKey pubkey;
+ if (provider.GetPubKey(keyid, pubkey)) {
+ return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2PKHGetScript, "pkh");
+ }
+ }
+ if (txntype == TX_WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
+ uint160 hash(data[0]);
+ CKeyID keyid(hash);
+ CPubKey pubkey;
+ if (provider.GetPubKey(keyid, pubkey)) {
+ return MakeUnique<SingleKeyDescriptor>(InferPubkey(pubkey, ctx, provider), P2WPKHGetScript, "wpkh");
+ }
+ }
+ if (txntype == TX_MULTISIG) {
+ std::vector<std::unique_ptr<PubkeyProvider>> providers;
+ for (size_t i = 1; i + 1 < data.size(); ++i) {
+ CPubKey pubkey(data[i].begin(), data[i].end());
+ providers.push_back(InferPubkey(pubkey, ctx, provider));
+ }
+ return MakeUnique<MultisigDescriptor>((int)data[0][0], std::move(providers));
+ }
+ if (txntype == TX_SCRIPTHASH && ctx == ParseScriptContext::TOP) {
+ uint160 hash(data[0]);
+ CScriptID scriptid(hash);
+ CScript subscript;
+ if (provider.GetCScript(scriptid, subscript)) {
+ auto sub = InferScript(subscript, ParseScriptContext::P2SH, provider);
+ if (sub) return MakeUnique<ConvertorDescriptor>(std::move(sub), ConvertP2SH, "sh");
+ }
+ }
+ if (txntype == TX_WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
+ CScriptID scriptid;
+ CRIPEMD160().Write(data[0].data(), data[0].size()).Finalize(scriptid.begin());
+ CScript subscript;
+ if (provider.GetCScript(scriptid, subscript)) {
+ auto sub = InferScript(subscript, ParseScriptContext::P2WSH, provider);
+ if (sub) return MakeUnique<ConvertorDescriptor>(std::move(sub), ConvertP2WSH, "wsh");
+ }
+ }
+
+ CTxDestination dest;
+ if (ExtractDestination(script, dest)) {
+ if (GetScriptForDestination(dest) == script) {
+ return MakeUnique<AddressDescriptor>(std::move(dest));
+ }
+ }
+
+ return MakeUnique<RawDescriptor>(script);
+}
+
} // namespace
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out)
@@ -564,3 +715,8 @@ std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProv
if (sp.size() == 0 && ret) return ret;
return nullptr;
}
+
+std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider)
+{
+ return InferScript(script, ParseScriptContext::TOP, provider);
+}
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 87e07369c7..0111972f85 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -32,6 +32,10 @@ struct Descriptor {
/** Whether the expansion of this descriptor depends on the position. */
virtual bool IsRange() const = 0;
+ /** Whether this descriptor has all information about signing ignoring lack of private keys.
+ * This is true for all descriptors except ones that use `raw` or `addr` constructions. */
+ virtual bool IsSolvable() const = 0;
+
/** Convert the descriptor back to a string, undoing parsing. */
virtual std::string ToString() const = 0;
@@ -51,5 +55,20 @@ struct Descriptor {
/** Parse a descriptor string. Included private keys are put in out. Returns nullptr if parsing fails. */
std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out);
-#endif // BITCOIN_SCRIPT_DESCRIPTOR_H
+/** Find a descriptor for the specified script, using information from provider where possible.
+ *
+ * A non-ranged descriptor which only generates the specified script will be returned in all
+ * circumstances.
+ *
+ * For public keys with key origin information, this information will be preserved in the returned
+ * descriptor.
+ *
+ * - If all information for solving `script` is present in `provider`, a descriptor will be returned
+ * which is `IsSolvable()` and encapsulates said information.
+ * - Failing that, if `script` corresponds to a known address type, an "addr()" descriptor will be
+ * returned (which is not `IsSolvable()`).
+ * - Failing that, a "raw()" descriptor is returned.
+ */
+std::unique_ptr<Descriptor> InferDescriptor(const CScript& script, const SigningProvider& provider);
+#endif // BITCOIN_SCRIPT_DESCRIPTOR_H
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
index 1433ebf42f..51bd2d6e9f 100644
--- a/src/script/ismine.cpp
+++ b/src/script/ismine.cpp
@@ -23,9 +23,9 @@ namespace {
*/
enum class IsMineSigVersion
{
- TOP = 0, //! scriptPubKey execution
- P2SH = 1, //! P2SH redeemScript
- WITNESS_V0 = 2 //! P2WSH witness script execution
+ TOP = 0, //!< scriptPubKey execution
+ P2SH = 1, //!< P2SH redeemScript
+ WITNESS_V0 = 2, //!< P2WSH witness script execution
};
/**
@@ -35,10 +35,10 @@ enum class IsMineSigVersion
*/
enum class IsMineResult
{
- NO = 0, //! Not ours
- WATCH_ONLY = 1, //! Included in watch-only balance
- SPENDABLE = 2, //! Included in all balances
- INVALID = 3, //! Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness)
+ NO = 0, //!< Not ours
+ WATCH_ONLY = 1, //!< Included in watch-only balance
+ SPENDABLE = 2, //!< Included in all balances
+ INVALID = 3, //!< Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness)
};
bool PermitsUncompressed(IsMineSigVersion sigversion)
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 9bdf3ed808..982aa241e7 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -6,7 +6,7 @@
#include <script/script.h>
#include <tinyformat.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
const char* GetOpName(opcodetype opcode)
{
diff --git a/src/script/script.h b/src/script/script.h
index 00065a24be..1d8ddba2f2 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -38,6 +38,12 @@ static const int MAX_STACK_SIZE = 1000;
// otherwise as UNIX timestamp.
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
+// Maximum nLockTime. Since a lock time indicates the last invalid timestamp, a
+// transaction with this lock time will never be valid unless lock time
+// checking is disabled (by setting all input sequence numbers to
+// SEQUENCE_FINAL).
+static const uint32_t LOCKTIME_MAX = 0xFFFFFFFFU;
+
template <typename T>
std::vector<unsigned char> ToByteVector(const T& in)
{
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 68f0542294..94005cf6f3 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -9,7 +9,7 @@
#include <pubkey.h>
#include <random.h>
#include <uint256.h>
-#include <util.h>
+#include <util/system.h>
#include <cuckoocache.h>
#include <boost/thread.hpp>
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index d779910425..e5651710f1 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -48,7 +48,7 @@ static bool GetCScript(const SigningProvider& provider, const SignatureData& sig
return false;
}
-static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey)
+static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey)
{
// Look for pubkey in all partial sigs
const auto it = sigdata.signatures.find(address);
@@ -63,25 +63,21 @@ static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, c
return true;
}
// Query the underlying provider
- if (provider.GetPubKey(address, pubkey)) {
- KeyOriginInfo info;
- if (provider.GetKeyOrigin(address, info)) {
- sigdata.misc_pubkeys.emplace(address, std::make_pair(pubkey, std::move(info)));
- }
- return true;
- }
- return false;
+ return provider.GetPubKey(address, pubkey);
}
-static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CKeyID& keyid, const CScript& scriptcode, SigVersion sigversion)
+static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CPubKey& pubkey, const CScript& scriptcode, SigVersion sigversion)
{
+ CKeyID keyid = pubkey.GetID();
const auto it = sigdata.signatures.find(keyid);
if (it != sigdata.signatures.end()) {
sig_out = it->second.second;
return true;
}
- CPubKey pubkey;
- GetPubKey(provider, sigdata, keyid, pubkey);
+ KeyOriginInfo info;
+ if (provider.GetKeyOrigin(keyid, info)) {
+ sigdata.misc_pubkeys.emplace(keyid, std::make_pair(pubkey, std::move(info)));
+ }
if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) {
auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out));
assert(i.second);
@@ -114,15 +110,15 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TX_WITNESS_UNKNOWN:
return false;
case TX_PUBKEY:
- if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false;
+ if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
ret.push_back(std::move(sig));
return true;
case TX_PUBKEYHASH: {
CKeyID keyID = CKeyID(uint160(vSolutions[0]));
- if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false;
- ret.push_back(std::move(sig));
CPubKey pubkey;
- GetPubKey(provider, sigdata, keyID, pubkey);
+ if (!GetPubKey(provider, sigdata, keyID, pubkey)) return false;
+ if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) return false;
+ ret.push_back(std::move(sig));
ret.push_back(ToByteVector(pubkey));
return true;
}
@@ -138,7 +134,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
CPubKey pubkey = CPubKey(vSolutions[i]);
- if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey.GetID(), scriptPubKey, sigversion)) {
+ if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) {
ret.push_back(std::move(sig));
}
}
@@ -236,10 +232,17 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
return sigdata.complete;
}
-bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash)
+bool PSBTInputSigned(PSBTInput& input)
{
- // if this input has a final scriptsig or scriptwitness, don't do anything with it
- if (!input.final_script_sig.empty() || !input.final_script_witness.IsNull()) {
+ return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
+}
+
+bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash)
+{
+ PSBTInput& input = psbt.inputs.at(index);
+ const CMutableTransaction& tx = *psbt.tx;
+
+ if (PSBTInputSigned(input)) {
return true;
}
@@ -250,15 +253,19 @@ bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& t
// Get UTXO
bool require_witness_sig = false;
CTxOut utxo;
+
+ // Verify input sanity, which checks that at most one of witness or non-witness utxos is provided.
+ if (!input.IsSane()) {
+ return false;
+ }
+
if (input.non_witness_utxo) {
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
- if (input.non_witness_utxo->GetHash() != tx.vin[index].prevout.hash) return false;
- // If both witness and non-witness UTXO are provided, verify that they match. This check shouldn't
- // matter, as the PSBT deserializer enforces only one of both is provided, and the only way both
- // can be present is when they're added simultaneously by FillPSBT (in which case they always match).
- // Still, check in order to not rely on callers to enforce this.
- if (!input.witness_utxo.IsNull() && input.non_witness_utxo->vout[tx.vin[index].prevout.n] != input.witness_utxo) return false;
- utxo = input.non_witness_utxo->vout[tx.vin[index].prevout.n];
+ COutPoint prevout = tx.vin[index].prevout;
+ if (input.non_witness_utxo->GetHash() != prevout.hash) {
+ return false;
+ }
+ utxo = input.non_witness_utxo->vout[prevout.n];
} else if (!input.witness_utxo.IsNull()) {
utxo = input.witness_utxo;
// When we're taking our information from a witness UTXO, we can't verify it is actually data from
@@ -277,13 +284,10 @@ bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& t
if (require_witness_sig && !sigdata.witness) return false;
input.FromSignatureData(sigdata);
- // If both UTXO types are present, drop the unnecessary one.
- if (input.non_witness_utxo && !input.witness_utxo.IsNull()) {
- if (sigdata.witness) {
- input.non_witness_utxo = nullptr;
- } else {
- input.witness_utxo.SetNull();
- }
+ // If we have a witness signature, use the smaller witness UTXO.
+ if (sigdata.witness) {
+ input.witness_utxo = utxo;
+ input.non_witness_utxo = nullptr;
}
return sig_complete;
@@ -505,6 +509,12 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
return false;
}
+PartiallySignedTransaction::PartiallySignedTransaction(const CTransaction& tx) : tx(tx)
+{
+ inputs.resize(tx.vin.size());
+ outputs.resize(tx.vout.size());
+}
+
bool PartiallySignedTransaction::IsNull() const
{
return !tx && inputs.empty() && outputs.empty() && unknown.empty();
@@ -683,6 +693,7 @@ bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& inf
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
+bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return LookupHelper(origins, keyid, info); }
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
diff --git a/src/script/sign.h b/src/script/sign.h
index 18b7320998..20c7203b26 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -24,6 +24,11 @@ struct KeyOriginInfo
{
unsigned char fingerprint[4];
std::vector<uint32_t> path;
+
+ friend bool operator==(const KeyOriginInfo& a, const KeyOriginInfo& b)
+ {
+ return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
+ }
};
/** An interface to be implemented by keystores that support signing. */
@@ -34,7 +39,7 @@ public:
virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
- virtual bool GetKeyOrigin(const CKeyID& id, KeyOriginInfo& info) const { return false; }
+ virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
};
extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
@@ -58,10 +63,12 @@ struct FlatSigningProvider final : public SigningProvider
{
std::map<CScriptID, CScript> scripts;
std::map<CKeyID, CPubKey> pubkeys;
+ std::map<CKeyID, KeyOriginInfo> origins;
std::map<CKeyID, CKey> keys;
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
+ bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
};
@@ -147,7 +154,7 @@ static constexpr uint8_t PSBT_SEPARATOR = 0x00;
template<typename Stream, typename... X>
void SerializeToVector(Stream& s, const X&... args)
{
- WriteCompactSize(s, GetSerializeSizeMany(s, args...));
+ WriteCompactSize(s, GetSerializeSizeMany(s.GetVersion(), args...));
SerializeMany(s, args...);
}
@@ -204,6 +211,9 @@ template<typename Stream>
void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, uint8_t type)
{
for (auto keypath_pair : hd_keypaths) {
+ if (!keypath_pair.first.IsValid()) {
+ throw std::ios_base::failure("Invalid CPubKey being serialized");
+ }
SerializeToVector(s, type, MakeSpan(keypath_pair.first));
WriteCompactSize(s, (keypath_pair.second.path.size() + 1) * sizeof(uint32_t));
s << keypath_pair.second.fingerprint;
@@ -300,6 +310,7 @@ struct PSBTInput
template <typename Stream>
inline void Unserialize(Stream& s) {
// Read loop
+ bool found_sep = false;
while(!s.empty()) {
// Read
std::vector<unsigned char> key;
@@ -307,7 +318,10 @@ struct PSBTInput
// the key is empty if that was actually a separator byte
// This is a special case for key lengths 0 as those are not allowed (except for separator)
- if (key.empty()) return;
+ if (key.empty()) {
+ found_sep = true;
+ break;
+ }
// First byte of key is the type
unsigned char type = key[0];
@@ -422,6 +436,10 @@ struct PSBTInput
break;
}
}
+
+ if (!found_sep) {
+ throw std::ios_base::failure("Separator is missing at the end of an input map");
+ }
}
template <typename Stream>
@@ -475,6 +493,7 @@ struct PSBTOutput
template <typename Stream>
inline void Unserialize(Stream& s) {
// Read loop
+ bool found_sep = false;
while(!s.empty()) {
// Read
std::vector<unsigned char> key;
@@ -482,7 +501,10 @@ struct PSBTOutput
// the key is empty if that was actually a separator byte
// This is a special case for key lengths 0 as those are not allowed (except for separator)
- if (key.empty()) return;
+ if (key.empty()) {
+ found_sep = true;
+ break;
+ }
// First byte of key is the type
unsigned char type = key[0];
@@ -527,6 +549,10 @@ struct PSBTOutput
}
}
}
+
+ if (!found_sep) {
+ throw std::ios_base::failure("Separator is missing at the end of an output map");
+ }
}
template <typename Stream>
@@ -548,6 +574,7 @@ struct PartiallySignedTransaction
bool IsSane() const;
PartiallySignedTransaction() {}
PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {}
+ explicit PartiallySignedTransaction(const CTransaction& tx);
// Only checks if they refer to the same transaction
friend bool operator==(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b)
@@ -602,6 +629,7 @@ struct PartiallySignedTransaction
}
// Read global data
+ bool found_sep = false;
while(!s.empty()) {
// Read
std::vector<unsigned char> key;
@@ -609,7 +637,10 @@ struct PartiallySignedTransaction
// the key is empty if that was actually a separator byte
// This is a special case for key lengths 0 as those are not allowed (except for separator)
- if (key.empty()) break;
+ if (key.empty()) {
+ found_sep = true;
+ break;
+ }
// First byte of key is the type
unsigned char type = key[0];
@@ -649,6 +680,10 @@ struct PartiallySignedTransaction
}
}
+ if (!found_sep) {
+ throw std::ios_base::failure("Separator is missing at the end of the global map");
+ }
+
// Make sure that we got an unsigned tx
if (!tx) {
throw std::ios_base::failure("No unsigned transcation was provided");
@@ -703,8 +738,11 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
+/** Checks whether a PSBTInput is already signed. */
+bool PSBTInputSigned(PSBTInput& input);
+
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
-bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash = SIGHASH_ALL);
+bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL);
/** Extract signature data from a transaction input, and insert it. */
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout);
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 08ba1b1e0f..31bfd04b0f 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -8,8 +8,8 @@
#include <crypto/sha256.h>
#include <pubkey.h>
#include <script/script.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
typedef std::vector<unsigned char> valtype;
diff --git a/src/serialize.h b/src/serialize.h
index 8bd3ef5d76..2d0cfbbbf0 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -901,10 +901,9 @@ class CSizeComputer
protected:
size_t nSize;
- const int nType;
const int nVersion;
public:
- CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {}
+ explicit CSizeComputer(int nVersionIn) : nSize(0), nVersion(nVersionIn) {}
void write(const char *psz, size_t _nSize)
{
@@ -929,7 +928,6 @@ public:
}
int GetVersion() const { return nVersion; }
- int GetType() const { return nType; }
};
template<typename Stream>
@@ -980,21 +978,15 @@ inline void WriteCompactSize(CSizeComputer &s, uint64_t nSize)
}
template <typename T>
-size_t GetSerializeSize(const T& t, int nType, int nVersion = 0)
+size_t GetSerializeSize(const T& t, int nVersion = 0)
{
- return (CSizeComputer(nType, nVersion) << t).size();
+ return (CSizeComputer(nVersion) << t).size();
}
-template <typename S, typename T>
-size_t GetSerializeSize(const S& s, const T& t)
+template <typename... T>
+size_t GetSerializeSizeMany(int nVersion, const T&... t)
{
- return (CSizeComputer(s.GetType(), s.GetVersion()) << t).size();
-}
-
-template <typename S, typename... T>
-size_t GetSerializeSizeMany(const S& s, const T&... t)
-{
- CSizeComputer sc(s.GetType(), s.GetVersion());
+ CSizeComputer sc(nVersion);
SerializeMany(sc, t...);
return sc.size();
}
diff --git a/src/sync.cpp b/src/sync.cpp
index c9aa98dcd6..30811f5f89 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -5,7 +5,7 @@
#include <sync.h>
#include <logging.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <stdio.h>
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 42ac345b4a..55fe19cebe 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -40,22 +40,26 @@ public:
CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
{
+ LOCK(cs);
return CAddrMan::Find(addr, pnId);
}
CAddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr)
{
+ LOCK(cs);
return CAddrMan::Create(addr, addrSource, pnId);
}
void Delete(int nId)
{
+ LOCK(cs);
CAddrMan::Delete(nId);
}
// Simulates connection failure so that we can test eviction of offline nodes
void SimConnFail(CService& addr)
{
+ LOCK(cs);
int64_t nLastSuccess = 1;
Good_(addr, true, nLastSuccess); // Set last good connection in the deep past.
diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp
index 1052ada232..5a108dcdad 100644
--- a/src/test/allocator_tests.cpp
+++ b/src/test/allocator_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <util.h>
+#include <util/system.h>
#include <support/allocators/secure.h>
#include <test/test_bitcoin.h>
@@ -163,7 +163,7 @@ private:
BOOST_AUTO_TEST_CASE(lockedpool_tests_mock)
{
// Test over three virtual arenas, of which one will succeed being locked
- std::unique_ptr<LockedPageAllocator> x(new TestLockedPageAllocator(3, 1));
+ std::unique_ptr<LockedPageAllocator> x = MakeUnique<TestLockedPageAllocator>(3, 1);
LockedPool pool(std::move(x));
BOOST_CHECK(pool.stats().total == 0);
BOOST_CHECK(pool.stats().locked == 0);
diff --git a/src/test/amount_tests.cpp b/src/test/amount_tests.cpp
index adffcfeef5..1ff040b077 100644
--- a/src/test/amount_tests.cpp
+++ b/src/test/amount_tests.cpp
@@ -13,8 +13,10 @@ BOOST_FIXTURE_TEST_SUITE(amount_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(MoneyRangeTest)
{
BOOST_CHECK_EQUAL(MoneyRange(CAmount(-1)), false);
- BOOST_CHECK_EQUAL(MoneyRange(MAX_MONEY + CAmount(1)), false);
+ BOOST_CHECK_EQUAL(MoneyRange(CAmount(0)), true);
BOOST_CHECK_EQUAL(MoneyRange(CAmount(1)), true);
+ BOOST_CHECK_EQUAL(MoneyRange(MAX_MONEY), true);
+ BOOST_CHECK_EQUAL(MoneyRange(MAX_MONEY + CAmount(1)), false);
}
BOOST_AUTO_TEST_CASE(GetFeeTest)
@@ -23,43 +25,43 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
feeRate = CFeeRate(0);
// Must always return 0
- BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1e5), 0);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1e5), CAmount(0));
feeRate = CFeeRate(1000);
// Must always just return the arg
- BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1), 1);
- BOOST_CHECK_EQUAL(feeRate.GetFee(121), 121);
- BOOST_CHECK_EQUAL(feeRate.GetFee(999), 999);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 1e3);
- BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), 9e3);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1), CAmount(1));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(121));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(999));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), CAmount(1e3));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(9e3));
feeRate = CFeeRate(-1000);
// Must always just return -1 * arg
- BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1), -1);
- BOOST_CHECK_EQUAL(feeRate.GetFee(121), -121);
- BOOST_CHECK_EQUAL(feeRate.GetFee(999), -999);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), -1e3);
- BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), -9e3);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1), CAmount(-1));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(-121));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(-999));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), CAmount(-1e3));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(-9e3));
feeRate = CFeeRate(123);
// Truncates the result, if not integer
- BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
- BOOST_CHECK_EQUAL(feeRate.GetFee(8), 1); // Special case: returns 1 instead of 0
- BOOST_CHECK_EQUAL(feeRate.GetFee(9), 1);
- BOOST_CHECK_EQUAL(feeRate.GetFee(121), 14);
- BOOST_CHECK_EQUAL(feeRate.GetFee(122), 15);
- BOOST_CHECK_EQUAL(feeRate.GetFee(999), 122);
- BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), 123);
- BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), 1107);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(8), CAmount(1)); // Special case: returns 1 instead of 0
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9), CAmount(1));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(14));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(122), CAmount(15));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(122));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), CAmount(123));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(1107));
feeRate = CFeeRate(-123);
// Truncates the result, if not integer
- BOOST_CHECK_EQUAL(feeRate.GetFee(0), 0);
- BOOST_CHECK_EQUAL(feeRate.GetFee(8), -1); // Special case: returns -1 instead of 0
- BOOST_CHECK_EQUAL(feeRate.GetFee(9), -1);
+ BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
+ BOOST_CHECK_EQUAL(feeRate.GetFee(8), CAmount(-1)); // Special case: returns -1 instead of 0
+ BOOST_CHECK_EQUAL(feeRate.GetFee(9), CAmount(-1));
// check alternate constructor
feeRate = CFeeRate(1000);
@@ -67,6 +69,9 @@ BOOST_AUTO_TEST_CASE(GetFeeTest)
BOOST_CHECK_EQUAL(feeRate.GetFee(100), altFeeRate.GetFee(100));
// Check full constructor
+ BOOST_CHECK(CFeeRate(CAmount(-1), 0) == CFeeRate(0));
+ BOOST_CHECK(CFeeRate(CAmount(0), 0) == CFeeRate(0));
+ BOOST_CHECK(CFeeRate(CAmount(1), 0) == CFeeRate(0));
// default value
BOOST_CHECK(CFeeRate(CAmount(-1), 1000) == CFeeRate(-1));
BOOST_CHECK(CFeeRate(CAmount(0), 1000) == CFeeRate(0));
diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp
index 6b06d2e1b6..32af843bf6 100644
--- a/src/test/base32_tests.cpp
+++ b/src/test/base32_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index 5fc4abaf3d..f8f9b3c1a7 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -6,7 +6,7 @@
#include <base58.h>
#include <test/test_bitcoin.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <univalue.h>
diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp
index daceea262c..0abbb682a7 100644
--- a/src/test/base64_tests.cpp
+++ b/src/test/base64_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp
index 2cdbaca7ba..c9951f4b7e 100644
--- a/src/test/bip32_tests.cpp
+++ b/src/test/bip32_tests.cpp
@@ -7,8 +7,8 @@
#include <key.h>
#include <key_io.h>
#include <uint256.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <string>
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
index 5131fe8235..309b8d2d06 100644
--- a/src/test/blockencodings_tests.cpp
+++ b/src/test/blockencodings_tests.cpp
@@ -344,4 +344,49 @@ BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
}
+BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationMaxTest) {
+ // Check that the highest legal index is decoded correctly
+ BlockTransactionsRequest req0;
+ req0.blockhash = InsecureRand256();
+ req0.indexes.resize(1);
+ req0.indexes[0] = 0xffff;
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << req0;
+
+ BlockTransactionsRequest req1;
+ stream >> req1;
+ BOOST_CHECK_EQUAL(req0.indexes.size(), req1.indexes.size());
+ BOOST_CHECK_EQUAL(req0.indexes[0], req1.indexes[0]);
+}
+
+BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
+ // Any set of index deltas that starts with N values that sum to (0x10000 - N)
+ // causes the edge-case overflow that was originally not checked for. Such
+ // a request cannot be created by serializing a real BlockTransactionsRequest
+ // due to the overflow, so here we'll serialize from raw deltas.
+ BlockTransactionsRequest req0;
+ req0.blockhash = InsecureRand256();
+ req0.indexes.resize(3);
+ req0.indexes[0] = 0x7000;
+ req0.indexes[1] = 0x10000 - 0x7000 - 2;
+ req0.indexes[2] = 0;
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << req0.blockhash;
+ WriteCompactSize(stream, req0.indexes.size());
+ WriteCompactSize(stream, req0.indexes[0]);
+ WriteCompactSize(stream, req0.indexes[1]);
+ WriteCompactSize(stream, req0.indexes[2]);
+
+ BlockTransactionsRequest req1;
+ try {
+ stream >> req1;
+ // before patch: deserialize above succeeds and this check fails, demonstrating the overflow
+ BOOST_CHECK(req1.indexes[1] < req1.indexes[2]);
+ // this shouldn't be reachable before or after patch
+ BOOST_CHECK(0);
+ } catch(std::ios_base::failure &) {
+ // deserialize should fail
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp
index 773de343ea..2144202b8d 100644
--- a/src/test/blockfilter_tests.cpp
+++ b/src/test/blockfilter_tests.cpp
@@ -10,7 +10,7 @@
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <boost/test/unit_test.hpp>
@@ -114,7 +114,8 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test)
unsigned int pos = 0;
/*int block_height =*/ test[pos++].get_int();
- /*uint256 block_hash =*/ ParseHashStr(test[pos++].get_str(), "block_hash");
+ uint256 block_hash;
+ BOOST_CHECK(ParseHashStr(test[pos++].get_str(), block_hash));
CBlock block;
BOOST_REQUIRE(DecodeHexBlk(block, test[pos++].get_str()));
@@ -129,9 +130,11 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test)
tx_undo.vprevout.emplace_back(txout, 0, false);
}
- uint256 prev_filter_header_basic = ParseHashStr(test[pos++].get_str(), "prev_filter_header_basic");
+ uint256 prev_filter_header_basic;
+ BOOST_CHECK(ParseHashStr(test[pos++].get_str(), prev_filter_header_basic));
std::vector<unsigned char> filter_basic = ParseHex(test[pos++].get_str());
- uint256 filter_header_basic = ParseHashStr(test[pos++].get_str(), "filter_header_basic");
+ uint256 filter_header_basic;
+ BOOST_CHECK(ParseHashStr(test[pos++].get_str(), filter_header_basic));
BlockFilter computed_filter_basic(BlockFilterType::BASIC, block, block_undo);
BOOST_CHECK(computed_filter_basic.GetFilter().GetEncoded() == filter_basic);
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 8a8e8bfdc3..c900bee199 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -13,8 +13,8 @@
#include <serialize.h>
#include <streams.h>
#include <uint256.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <vector>
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 2732598b4f..a757e06a9d 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -2,8 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <util.h>
-#include <utiltime.h>
+#include <util/system.h>
+#include <util/time.h>
#include <validation.h>
#include <test/test_bitcoin.h>
@@ -148,7 +148,7 @@ typedef CCheckQueue<FrozenCleanupCheck> FrozenCleanup_Queue;
*/
static void Correct_Queue_range(std::vector<size_t> range)
{
- auto small_queue = std::unique_ptr<Correct_Queue>(new Correct_Queue {QUEUE_BATCH_SIZE});
+ auto small_queue = MakeUnique<Correct_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
for (auto x = 0; x < nScriptCheckThreads; ++x) {
tg.create_thread([&]{small_queue->Thread();});
@@ -213,7 +213,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random)
/** Test that failing checks are caught */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure)
{
- auto fail_queue = std::unique_ptr<Failing_Queue>(new Failing_Queue {QUEUE_BATCH_SIZE});
+ auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
for (auto x = 0; x < nScriptCheckThreads; ++x) {
@@ -246,7 +246,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure)
// future blocks, ie, the bad state is cleared.
BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure)
{
- auto fail_queue = std::unique_ptr<Failing_Queue>(new Failing_Queue {QUEUE_BATCH_SIZE});
+ auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
for (auto x = 0; x < nScriptCheckThreads; ++x) {
tg.create_thread([&]{fail_queue->Thread();});
@@ -274,7 +274,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure)
// more than once as well
BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
{
- auto queue = std::unique_ptr<Unique_Queue>(new Unique_Queue {QUEUE_BATCH_SIZE});
+ auto queue = MakeUnique<Unique_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
for (auto x = 0; x < nScriptCheckThreads; ++x) {
tg.create_thread([&]{queue->Thread();});
@@ -310,7 +310,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
// time could leave the data hanging across a sequence of blocks.
BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory)
{
- auto queue = std::unique_ptr<Memory_Queue>(new Memory_Queue {QUEUE_BATCH_SIZE});
+ auto queue = MakeUnique<Memory_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
for (auto x = 0; x < nScriptCheckThreads; ++x) {
tg.create_thread([&]{queue->Thread();});
@@ -341,7 +341,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory)
// have been destructed
BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
{
- auto queue = std::unique_ptr<FrozenCleanup_Queue>(new FrozenCleanup_Queue {QUEUE_BATCH_SIZE});
+ auto queue = MakeUnique<FrozenCleanup_Queue>(QUEUE_BATCH_SIZE);
boost::thread_group tg;
bool fails = false;
for (auto x = 0; x < nScriptCheckThreads; ++x) {
@@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
/** Test that CCheckQueueControl is threadsafe */
BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks)
{
- auto queue = std::unique_ptr<Standard_Queue>(new Standard_Queue{QUEUE_BATCH_SIZE});
+ auto queue = MakeUnique<Standard_Queue>(QUEUE_BATCH_SIZE);
{
boost::thread_group tg;
std::atomic<int> nThreads {0};
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 6f4b5ecd26..d3cbaedf00 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -2,17 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <attributes.h>
#include <coins.h>
+#include <consensus/validation.h>
#include <script/standard.h>
+#include <test/test_bitcoin.h>
#include <uint256.h>
#include <undo.h>
-#include <utilstrencodings.h>
-#include <test/test_bitcoin.h>
+#include <util/strencodings.h>
#include <validation.h>
-#include <consensus/validation.h>
-#include <vector>
#include <map>
+#include <vector>
#include <boost/test/unit_test.hpp>
@@ -36,7 +37,7 @@ class CCoinsViewTest : public CCoinsView
std::map<COutPoint, Coin> map_;
public:
- bool GetCoin(const COutPoint& outpoint, Coin& coin) const override
+ NODISCARD bool GetCoin(const COutPoint& outpoint, Coin& coin) const override
{
std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
if (it == map_.end()) {
diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp
index a4e99d438f..e686c05165 100644
--- a/src/test/compress_tests.cpp
+++ b/src/test/compress_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <compressor.h>
-#include <util.h>
+#include <util/system.h>
#include <test/test_bitcoin.h>
#include <stdint.h>
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 19521027a9..f3fd83a0cc 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -11,7 +11,7 @@
#include <crypto/hmac_sha256.h>
#include <crypto/hmac_sha512.h>
#include <random.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <vector>
@@ -200,7 +200,7 @@ static void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t see
BOOST_CHECK(out == outres);
}
-static std::string LongTestString(void) {
+static std::string LongTestString() {
std::string ret;
for (int i=0; i<200000; i++) {
ret += (unsigned char)(i);
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 9957ac074b..1034d4ade2 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -102,15 +102,15 @@ BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
char key_res;
uint256 val_res;
- it->GetKey(key_res);
- it->GetValue(val_res);
+ BOOST_REQUIRE(it->GetKey(key_res));
+ BOOST_REQUIRE(it->GetValue(val_res));
BOOST_CHECK_EQUAL(key_res, key);
BOOST_CHECK_EQUAL(val_res.ToString(), in.ToString());
it->Next();
- it->GetKey(key_res);
- it->GetValue(val_res);
+ BOOST_REQUIRE(it->GetKey(key_res));
+ BOOST_REQUIRE(it->GetValue(val_res));
BOOST_CHECK_EQUAL(key_res, key2);
BOOST_CHECK_EQUAL(val_res.ToString(), in2.ToString());
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 52bbe96b96..97cf5ed345 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -11,7 +11,7 @@
#include <pow.h>
#include <script/sign.h>
#include <serialize.h>
-#include <util.h>
+#include <util/system.h>
#include <validation.h>
#include <test/test_bitcoin.h>
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index e739b84a48..0e98f5a826 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -9,7 +9,7 @@
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <script/descriptor.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
namespace {
@@ -43,9 +43,12 @@ std::string MaybeUseHInsteadOfApostrophy(std::string ret)
return ret;
}
-void Check(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts)
+const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}};
+
+void Check(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
{
FlatSigningProvider keys_priv, keys_pub;
+ std::set<std::vector<uint32_t>> left_paths = paths;
// Check that parsing succeeds.
auto parse_priv = Parse(MaybeUseHInsteadOfApostrophy(prv), keys_priv);
@@ -59,7 +62,7 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
// Check that both versions serialize back to the public version.
std::string pub1 = parse_priv->ToString();
- std::string pub2 = parse_priv->ToString();
+ std::string pub2 = parse_pub->ToString();
BOOST_CHECK_EQUAL(pub, pub1);
BOOST_CHECK_EQUAL(pub, pub2);
@@ -84,7 +87,7 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
for (size_t i = 0; i < max; ++i) {
const auto& ref = scripts[(flags & RANGE) ? i : 0];
for (int t = 0; t < 2; ++t) {
- FlatSigningProvider key_provider = (flags & HARDENED) ? keys_priv : keys_pub;
+ const FlatSigningProvider& key_provider = (flags & HARDENED) ? keys_priv : keys_pub;
FlatSigningProvider script_provider;
std::vector<CScript> spks;
BOOST_CHECK((t ? parse_priv : parse_pub)->Expand(i, key_provider, spks, script_provider));
@@ -99,10 +102,29 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
spend.vout.resize(1);
BOOST_CHECK_MESSAGE(SignSignature(Merge(keys_priv, script_provider), spks[n], spend, 0, 1, SIGHASH_ALL), prv);
}
+
+ /* Infer a descriptor from the generated script, and verify its solvability and that it roundtrips. */
+ auto inferred = InferDescriptor(spks[n], script_provider);
+ BOOST_CHECK_EQUAL(inferred->IsSolvable(), !(flags & UNSOLVABLE));
+ std::vector<CScript> spks_inferred;
+ FlatSigningProvider provider_inferred;
+ BOOST_CHECK(inferred->Expand(0, provider_inferred, spks_inferred, provider_inferred));
+ BOOST_CHECK_EQUAL(spks_inferred.size(), 1);
+ BOOST_CHECK(spks_inferred[0] == spks[n]);
+ BOOST_CHECK_EQUAL(IsSolvable(provider_inferred, spks_inferred[0]), !(flags & UNSOLVABLE));
+ BOOST_CHECK(provider_inferred.origins == script_provider.origins);
}
+ // Test whether the observed key path is present in the 'paths' variable (which contains expected, unobserved paths),
+ // and then remove it from that set.
+ for (const auto& origin : script_provider.origins) {
+ BOOST_CHECK_MESSAGE(paths.count(origin.second.path), "Unexpected key path: " + prv);
+ left_paths.erase(origin.second.path);
+ }
}
}
+ // Verify no expected paths remain that were not observed.
+ BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv);
}
}
@@ -114,7 +136,7 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
// Basic single-key compressed
Check("combo(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "combo(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac","76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac","00149a1c78a507689f6f54b847ad1cef1e614ee23f1e","a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}});
Check("pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pk(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"}});
- Check("pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}});
+ Check("pkh([deadbeef/1/2'/3/4']L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "pkh([deadbeef/1/2'/3/4']03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"76a9149a1c78a507689f6f54b847ad1cef1e614ee23f1e88ac"}}, {{1,0x80000002UL,3,0x80000004UL}});
Check("wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)", SIGNABLE, {{"00149a1c78a507689f6f54b847ad1cef1e614ee23f1e"}});
Check("sh(wpkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1))", "sh(wpkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd))", SIGNABLE, {{"a91484ab21b1b2fd065d4504ff693d832434b6108d7b87"}});
@@ -135,20 +157,26 @@ BOOST_AUTO_TEST_CASE(descriptor_test)
Check("sh(wsh(pkh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)))", "sh(wsh(pkh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)))", SIGNABLE, {{"a914b61b92e2ca21bac1e72a3ab859a742982bea960a87"}});
// Versions with BIP32 derivations
- Check("combo(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}});
- Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}});
- Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}});
- Check("wpkh(xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh(xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}});
- Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}});
- Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}});
+ Check("combo([01234567]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([01234567]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)", SIGNABLE, {{"2102d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0ac","76a91431a507b815593dfc51ffc7245ae7e5aee304246e88ac","001431a507b815593dfc51ffc7245ae7e5aee304246e","a9142aafb926eb247cb18240a7f4c07983ad1f37922687"}});
+ Check("pk(xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0)", "pk(xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0)", DEFAULT, {{"210379e45b3cf75f9c5f9befd8e9506fb962f6a9d185ac87001ec44a8d3df8d4a9e3ac"}}, {{0}});
+ Check("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0)", HARDENED, {{"76a914ebdc90806a9c4356c1c88e42216611e1cb4c1c1788ac"}}, {{0xFFFFFFFFUL,0}});
+ Check("wpkh([ffffffff/13']xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*)", "wpkh([ffffffff/13']xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*)", RANGE, {{"0014326b2249e3a25d5dc60935f044ee835d090ba859"},{"0014af0bd98abc2f2cae66e36896a39ffe2d32984fb7"},{"00141fa798efd1cbf95cebf912c031b8a4a6e9fb9f27"}}, {{0x8000000DUL, 1, 2, 0}, {0x8000000DUL, 1, 2, 1}, {0x8000000DUL, 1, 2, 2}});
+ Check("sh(wpkh(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "sh(wpkh(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", RANGE | HARDENED, {{"a9149a4d9901d6af519b2a23d4a2f51650fcba87ce7b87"},{"a914bed59fc0024fae941d6e20a3b44a109ae740129287"},{"a9148483aa1116eb9c05c482a72bada4b1db24af654387"}}, {{10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
+ Check("combo(xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334/*)", "combo(xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV/*)", RANGE, {{"2102df12b7035bdac8e3bab862a3a83d06ea6b17b6753d52edecba9be46f5d09e076ac","76a914f90e3178ca25f2c808dc76624032d352fdbdfaf288ac","0014f90e3178ca25f2c808dc76624032d352fdbdfaf2","a91408f3ea8c68d4a7585bf9e8bda226723f70e445f087"},{"21032869a233c9adff9a994e4966e5b821fd5bac066da6c3112488dc52383b4a98ecac","76a914a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b788ac","0014a8409d1b6dfb1ed2a3e8aa5e0ef2ff26b15b75b7","a91473e39884cb71ae4e5ac9739e9225026c99763e6687"}}, {{0}, {1}});
+ CheckUnparsable("combo([012345678]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)", "combo([012345678]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)"); // Too long key fingerprint
CheckUnparsable("pkh(xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483648)", "pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483648)"); // BIP 32 path element overflow
// Multisig constructions
Check("multi(1,L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1,5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss)", "multi(1,03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)", SIGNABLE, {{"512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd4104a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea23552ae"}});
- Check("sh(multi(2,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}});
- Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}});
+ Check("sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))", "sh(multi(2,[00000000/111'/222]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL,xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y/0))", DEFAULT, {{"a91445a9a622a8b0a1269944be477640eedc447bbd8487"}}, {{0x8000006FUL,222},{0}});
+ Check("wsh(multi(2,xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))", HARDENED | RANGE, {{"0020b92623201f3bb7c3771d45b2ad1d0351ea8fbf8cfe0a0e570264e1075fa1948f"},{"002036a08bbe4923af41cf4316817c93b8d37e2f635dd25cfff06bd50df6ae7ea203"},{"0020a96e7ab4607ca6b261bfe3245ffda9c746b28d3f59e83d34820ec0e2b36c139c"}}, {{0xFFFFFFFFUL,0}, {1,2,0}, {1,2,1}, {1,2,2}, {10, 20, 30, 40, 0x80000000UL}, {10, 20, 30, 40, 0x80000001UL}, {10, 20, 30, 40, 0x80000002UL}});
Check("sh(wsh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9)))","sh(wsh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232)))", SIGNABLE, {{"a9147fc63e13dc25e8a95a3cee3d9a714ac3afd96f1e87"}});
CheckUnparsable("sh(multi(16,KzoAz5CanayRKex3fSLQ2BwJpN7U52gZvxMyk78nDMHuqrUxuSJy,KwGNz6YCCQtYvFzMtrC6D3tKTKdBBboMrLTsjr2NYVBwapCkn7Mr,KxogYhiNfwxuswvXV66eFyKcCpm7dZ7TqHVqujHAVUjJxyivxQ9X,L2BUNduTSyZwZjwNHynQTF14mv2uz2NRq5n5sYWTb4FkkmqgEE9f,L1okJGHGn1kFjdXHKxXjwVVtmCMR2JA5QsbKCSpSb7ReQjezKeoD,KxDCNSST75HFPaW5QKpzHtAyaCQC7p9Vo3FYfi2u4dXD1vgMiboK,L5edQjFtnkcf5UWURn6UuuoFrabgDQUHdheKCziwN42aLwS3KizU,KzF8UWFcEC7BYTq8Go1xVimMkDmyNYVmXV5PV7RuDicvAocoPB8i,L3nHUboKG2w4VSJ5jYZ5CBM97oeK6YuKvfZxrefdShECcjEYKMWZ,KyjHo36dWkYhimKmVVmQTq3gERv3pnqA4xFCpvUgbGDJad7eS8WE,KwsfyHKRUTZPQtysN7M3tZ4GXTnuov5XRgjdF2XCG8faAPmFruRF,KzCUbGhN9LJhdeFfL9zQgTJMjqxdBKEekRGZX24hXdgCNCijkkap,KzgpMBwwsDLwkaC5UrmBgCYaBD2WgZ7PBoGYXR8KT7gCA9UTN5a3,KyBXTPy4T7YG4q9tcAM3LkvfRpD1ybHMvcJ2ehaWXaSqeGUxEdkP,KzJDe9iwJRPtKP2F2AoN6zBgzS7uiuAwhWCfGdNeYJ3PC1HNJ8M8,L1xbHrxynrqLKkoYc4qtoQPx6uy5qYXR5ZDYVYBSRmCV5piU3JG9))","sh(multi(16,03669b8afcec803a0d323e9a17f3ea8e68e8abe5a278020a929adbec52421adbd0,0260b2003c386519fc9eadf2b5cf124dd8eea4c4e68d5e154050a9346ea98ce600,0362a74e399c39ed5593852a30147f2959b56bb827dfa3e60e464b02ccf87dc5e8,0261345b53de74a4d721ef877c255429961b7e43714171ac06168d7e08c542a8b8,02da72e8b46901a65d4374fe6315538d8f368557dda3a1dcf9ea903f3afe7314c8,0318c82dd0b53fd3a932d16e0ba9e278fcc937c582d5781be626ff16e201f72286,0297ccef1ef99f9d73dec9ad37476ddb232f1238aff877af19e72ba04493361009,02e502cfd5c3f972fe9a3e2a18827820638f96b6f347e54d63deb839011fd5765d,03e687710f0e3ebe81c1037074da939d409c0025f17eb86adb9427d28f0f7ae0e9,02c04d3a5274952acdbc76987f3184b346a483d43be40874624b29e3692c1df5af,02ed06e0f418b5b43a7ec01d1d7d27290fa15f75771cb69b642a51471c29c84acd,036d46073cbb9ffee90473f3da429abc8de7f8751199da44485682a989a4bebb24,02f5d1ff7c9029a80a4e36b9a5497027ef7f3e73384a4a94fbfe7c4e9164eec8bc,02e41deffd1b7cce11cde209a781adcffdabd1b91c0ba0375857a2bfd9302419f3,02d76625f7956a7fc505ab02556c23ee72d832f1bac391bcd2d3abce5710a13d06,0399eb0a5487515802dc14544cf10b3666623762fbed2ec38a3975716e2c29c232))"); // P2SH does not fit 16 compressed pubkeys in a redeemscript
+ CheckUnparsable("wsh(multi(2,[aaaaaaaa][aaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa][aaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Double key origin descriptor
+ CheckUnparsable("wsh(multi(2,[aaaagaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaagaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Non hex fingerprint
+ CheckUnparsable("wsh(multi(2,[aaaaaaaa],xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaa],xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // No public key with origin
+ CheckUnparsable("wsh(multi(2,[aaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Too short fingerprint
+ CheckUnparsable("wsh(multi(2,[aaaaaaaaa]xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U/2147483647'/0,xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt/1/2/*,xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/10/20/30/40/*'))", "wsh(multi(2,[aaaaaaaaa]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/2147483647'/0,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/2/*,xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/10/20/30/40/*'))"); // Too long fingerprint
// Check for invalid nesting of structures
CheckUnparsable("sh(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)", "sh(03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd)"); // P2SH needs a script, not a key
diff --git a/src/test/fs_tests.cpp b/src/test/fs_tests.cpp
new file mode 100644
index 0000000000..93aee10bb7
--- /dev/null
+++ b/src/test/fs_tests.cpp
@@ -0,0 +1,56 @@
+// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+//
+#include <fs.h>
+#include <test/test_bitcoin.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(fs_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(fsbridge_fstream)
+{
+ fs::path tmpfolder = SetDataDir("fsbridge_fstream");
+ // tmpfile1 should be the same as tmpfile2
+ fs::path tmpfile1 = tmpfolder / "fs_tests_₿_🏃";
+ fs::path tmpfile2 = tmpfolder / L"fs_tests_₿_🏃";
+ {
+ fsbridge::ofstream file(tmpfile1);
+ file << "bitcoin";
+ }
+ {
+ fsbridge::ifstream file(tmpfile2);
+ std::string input_buffer;
+ file >> input_buffer;
+ BOOST_CHECK_EQUAL(input_buffer, "bitcoin");
+ }
+ {
+ fsbridge::ifstream file(tmpfile1, std::ios_base::in | std::ios_base::ate);
+ std::string input_buffer;
+ file >> input_buffer;
+ BOOST_CHECK_EQUAL(input_buffer, "");
+ }
+ {
+ fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::app);
+ file << "tests";
+ }
+ {
+ fsbridge::ifstream file(tmpfile1);
+ std::string input_buffer;
+ file >> input_buffer;
+ BOOST_CHECK_EQUAL(input_buffer, "bitcointests");
+ }
+ {
+ fsbridge::ofstream file(tmpfile2, std::ios_base::out | std::ios_base::trunc);
+ file << "bitcoin";
+ }
+ {
+ fsbridge::ifstream file(tmpfile1);
+ std::string input_buffer;
+ file >> input_buffer;
+ BOOST_CHECK_EQUAL(input_buffer, "bitcoin");
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp
index 7592330b10..14ddf4d10e 100644
--- a/src/test/getarg_tests.cpp
+++ b/src/test/getarg_tests.cpp
@@ -2,7 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <util.h>
+#include <util/system.h>
#include <test/test_bitcoin.h>
#include <string>
@@ -28,7 +28,7 @@ static void ResetArgs(const std::string& strArg)
vecChar.push_back(s.c_str());
std::string error;
- gArgs.ParseParameters(vecChar.size(), vecChar.data(), error);
+ BOOST_CHECK(gArgs.ParseParameters(vecChar.size(), vecChar.data(), error));
}
static void SetupArgs(const std::vector<std::string>& args)
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index 6ed0209895..e8e5040855 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -2,8 +2,9 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <crypto/siphash.h>
#include <hash.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <vector>
diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp
index a0c10d8ddd..5db62f4bba 100644
--- a/src/test/key_io_tests.cpp
+++ b/src/test/key_io_tests.cpp
@@ -8,7 +8,7 @@
#include <key.h>
#include <key_io.h>
#include <script/script.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/key_properties.cpp b/src/test/key_properties.cpp
index 14e3c85359..c564b4eab8 100644
--- a/src/test/key_properties.cpp
+++ b/src/test/key_properties.cpp
@@ -6,8 +6,8 @@
#include <base58.h>
#include <script/script.h>
#include <uint256.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <string>
#include <vector>
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index 61db70decb..91cafd05d9 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -7,8 +7,8 @@
#include <key_io.h>
#include <script/script.h>
#include <uint256.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <string>
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index 0e15464fd9..db38c9623c 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -4,7 +4,7 @@
#include <policy/policy.h>
#include <txmempool.h>
-#include <util.h>
+#include <util/system.h>
#include <test/test_bitcoin.h>
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index eb0e4219d3..a7074a5e43 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -15,8 +15,8 @@
#include <script/standard.h>
#include <txmempool.h>
#include <uint256.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
@@ -92,8 +92,8 @@ static CBlockIndex CreateBlockIndex(int nHeight)
static bool TestSequenceLocks(const CTransaction &tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- LOCK(mempool.cs);
- return CheckSequenceLocks(tx, flags);
+ LOCK(::mempool.cs);
+ return CheckSequenceLocks(::mempool, tx, flags);
}
// Test suite for ancestor feerate transaction selection.
@@ -139,7 +139,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
uint256 hashFreeTx = tx.GetHash();
mempool.addUnchecked(entry.Fee(0).FromTx(tx));
- size_t freeTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
+ size_t freeTxSize = ::GetSerializeSize(tx, PROTOCOL_VERSION);
// Calculate a fee on child transaction that will put the package just
// below the block min tx fee (assuming 1 child tx of the same size).
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index eaa8b16182..c1ee231d8a 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -11,7 +11,7 @@
#include <net.h>
#include <netbase.h>
#include <chainparams.h>
-#include <util.h>
+#include <util/system.h>
#include <memory>
@@ -179,14 +179,52 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
bool fInboundIn = false;
// Test that fFeeler is false by default.
- std::unique_ptr<CNode> pnode1(new CNode(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, fInboundIn));
+ std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, fInboundIn);
BOOST_CHECK(pnode1->fInbound == false);
BOOST_CHECK(pnode1->fFeeler == false);
fInboundIn = true;
- std::unique_ptr<CNode> pnode2(new CNode(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, fInboundIn));
+ std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, fInboundIn);
BOOST_CHECK(pnode2->fInbound == true);
BOOST_CHECK(pnode2->fFeeler == false);
}
+// prior to PR #14728, this test triggers an undefined behavior
+BOOST_AUTO_TEST_CASE(ipv4_peer_with_ipv6_addrMe_test)
+{
+ // set up local addresses; all that's necessary to reproduce the bug is
+ // that a normal IPv4 address is among the entries, but if this address is
+ // !IsRoutable the undefined behavior is easier to trigger deterministically
+ {
+ LOCK(cs_mapLocalHost);
+ in_addr ipv4AddrLocal;
+ ipv4AddrLocal.s_addr = 0x0100007f;
+ CNetAddr addr = CNetAddr(ipv4AddrLocal);
+ LocalServiceInfo lsi;
+ lsi.nScore = 23;
+ lsi.nPort = 42;
+ mapLocalHost[addr] = lsi;
+ }
+
+ // create a peer with an IPv4 address
+ 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{}, false);
+ pnode->fSuccessfullyConnected.store(true);
+
+ // the peer claims to be reaching us via IPv6
+ in6_addr ipv6AddrLocal;
+ memset(ipv6AddrLocal.s6_addr, 0, 16);
+ ipv6AddrLocal.s6_addr[0] = 0xcc;
+ CAddress addrLocal = CAddress(CService(ipv6AddrLocal, 7777), NODE_NETWORK);
+ pnode->SetAddrLocal(addrLocal);
+
+ // before patch, this causes undefined behavior detectable with clang's -fsanitize=memory
+ AdvertiseLocal(&*pnode);
+
+ // suppress no-checks-run warning; if this test fails, it's by triggering a sanitizer
+ BOOST_CHECK(1);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 8072eb922d..0d557cff13 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -4,7 +4,7 @@
#include <netbase.h>
#include <test/test_bitcoin.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <string>
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 2022ed6659..51668cbe64 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -6,7 +6,7 @@
#include <policy/fees.h>
#include <txmempool.h>
#include <uint256.h>
-#include <util.h>
+#include <util/system.h>
#include <test/test_bitcoin.h>
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index 1ac9adc740..26cdc9bc5c 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -1,12 +1,12 @@
// Copyright (c) 2015-2018 The Bitcoin Core developers
-// Distributed under the MIT/X11 software license, see the accompanying
+// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <chainparams.h>
#include <pow.h>
#include <random.h>
-#include <util.h>
+#include <util/system.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index a49796d6f4..121b72a5f7 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -4,8 +4,11 @@
#include <rpc/server.h>
#include <rpc/client.h>
+#include <rpc/util.h>
#include <core_io.h>
+#include <init.h>
+#include <interfaces/chain.h>
#include <key_io.h>
#include <netbase.h>
@@ -112,10 +115,14 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign)
std::string notsigned = r.get_str();
std::string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\"";
std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\"";
+ InitInterfaces interfaces;
+ interfaces.chain = interfaces::MakeChain();
+ g_rpc_interfaces = &interfaces;
r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" [] "+prevout);
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false);
r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" ["+privkey1+","+privkey2+"] "+prevout);
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true);
+ g_rpc_interfaces = nullptr;
}
BOOST_AUTO_TEST_CASE(rpc_createraw_op_return)
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 67c377778f..1c70fdcce6 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -10,8 +10,8 @@
#include <script/script.h>
#include <script/script_error.h>
#include <script/sign.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <test/test_bitcoin.h>
#include <rpc/server.h>
@@ -1013,21 +1013,21 @@ BOOST_AUTO_TEST_CASE(script_PushData)
ScriptError err;
std::vector<std::vector<unsigned char> > directStack;
- BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK(EvalScript(directStack, CScript(direct, direct + sizeof(direct)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata1Stack;
- BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK(EvalScript(pushdata1Stack, CScript(pushdata1, pushdata1 + sizeof(pushdata1)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK(pushdata1Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata2Stack;
- BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK(EvalScript(pushdata2Stack, CScript(pushdata2, pushdata2 + sizeof(pushdata2)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK(pushdata2Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata4Stack;
- BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
+ BOOST_CHECK(EvalScript(pushdata4Stack, CScript(pushdata4, pushdata4 + sizeof(pushdata4)), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SigVersion::BASE, &err));
BOOST_CHECK(pushdata4Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index 3a06145861..e754996d2f 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -182,13 +182,13 @@ BOOST_AUTO_TEST_CASE(varints)
CDataStream::size_type size = 0;
for (int i = 0; i < 100000; i++) {
ss << VARINT(i, VarIntMode::NONNEGATIVE_SIGNED);
- size += ::GetSerializeSize(VARINT(i, VarIntMode::NONNEGATIVE_SIGNED), 0, 0);
+ size += ::GetSerializeSize(VARINT(i, VarIntMode::NONNEGATIVE_SIGNED), 0);
BOOST_CHECK(size == ss.size());
}
for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) {
ss << VARINT(i);
- size += ::GetSerializeSize(VARINT(i), 0, 0);
+ size += ::GetSerializeSize(VARINT(i), 0);
BOOST_CHECK(size == ss.size());
}
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index e6905457bb..c329844341 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -11,8 +11,8 @@
#include <serialize.h>
#include <streams.h>
#include <test/test_bitcoin.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <version.h>
#include <iostream>
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index c0754618fb..5c46976ace 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
-#include <util.h>
+#include <util/system.h>
#include <test/test_bitcoin.h>
#include <vector>
@@ -170,7 +170,6 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_edge_test)
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-1)->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::min())->nHeight, 0);
- BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::min())->nHeight, 0);
BOOST_CHECK_EQUAL(chain.FindEarliestAtLeast(-int64_t(std::numeric_limits<unsigned int>::max()) - 1)->nHeight, 0);
BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<int64_t>::max()));
BOOST_CHECK(!chain.FindEarliestAtLeast(std::numeric_limits<unsigned int>::max()));
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 0d7c9f0abf..f7874e6882 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -6,6 +6,7 @@
#include <chainparams.h>
#include <consensus/consensus.h>
+#include <consensus/params.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
#include <miner.h>
@@ -58,6 +59,9 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
InitSignatureCache();
InitScriptExecutionCache();
fCheckBlockIndex = true;
+ // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
+ // TODO: fix the code to support SegWit blocks.
+ gArgs.ForceSetArg("-vbparams", strprintf("segwit:0:%d", (int64_t)Consensus::BIP9Deployment::NO_TIMEOUT));
SelectParams(chainName);
noui_connect();
}
@@ -107,7 +111,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
nScriptCheckThreads = 3;
for (int i=0; i < nScriptCheckThreads-1; i++)
threadGroup.create_thread(&ThreadScriptCheck);
- g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests.
+ g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
connman = g_connman.get();
peerLogic.reset(new PeerLogicValidation(connman, scheduler, /*enable_bip61=*/true));
}
@@ -128,9 +132,6 @@ TestingSetup::~TestingSetup()
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{
- // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
- // TODO: fix the code to support SegWit blocks.
- UpdateVersionBitsParameters(Consensus::DEPLOYMENT_SEGWIT, 0, Consensus::BIP9Deployment::NO_TIMEOUT);
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index b87d9bea5d..3872767133 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -37,6 +37,8 @@ static inline uint64_t InsecureRandBits(int bits) { return insecure_rand_ctx.ran
static inline uint64_t InsecureRandRange(uint64_t range) { return insecure_rand_ctx.randrange(range); }
static inline bool InsecureRandBool() { return insecure_rand_ctx.randbool(); }
+static constexpr CAmount CENT{1000000};
+
/** Basic testing setup.
* This just configures logging and chain parameters.
*/
diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp
index 11b7c66ccd..88c082ff66 100644
--- a/src/test/test_bitcoin_fuzzy.cpp
+++ b/src/test/test_bitcoin_fuzzy.cpp
@@ -279,7 +279,7 @@ static int test_one_input(std::vector<uint8_t> buffer) {
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
void initialize() {
- globalVerifyHandle = std::unique_ptr<ECCVerifyHandle>(new ECCVerifyHandle());
+ globalVerifyHandle = MakeUnique<ECCVerifyHandle>();
}
// This function is used by libFuzzer
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index c527ad448c..9978c71661 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -19,7 +19,7 @@
#include <script/sign.h>
#include <script/script_error.h>
#include <script/standard.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <map>
#include <string>
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index 2a160b9988..43e025c58f 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -5,8 +5,8 @@
#include <index/txindex.h>
#include <script/standard.h>
#include <test/test_bitcoin.h>
-#include <util.h>
-#include <utiltime.h>
+#include <util/system.h>
+#include <util/time.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 2bf835a756..506a60d173 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -12,7 +12,7 @@
#include <script/standard.h>
#include <script/sign.h>
#include <test/test_bitcoin.h>
-#include <utiltime.h>
+#include <util/time.h>
#include <core_io.h>
#include <keystore.h>
#include <policy/policy.h>
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index f91b633b83..cca5e20296 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -184,8 +184,8 @@ BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHex begin() end() size() GetLow64 G
BOOST_CHECK(OneL.begin() + 32 == OneL.end());
BOOST_CHECK(MaxL.begin() + 32 == MaxL.end());
BOOST_CHECK(TmpL.begin() + 32 == TmpL.end());
- BOOST_CHECK(GetSerializeSize(R1L, 0, PROTOCOL_VERSION) == 32);
- BOOST_CHECK(GetSerializeSize(ZeroL, 0, PROTOCOL_VERSION) == 32);
+ BOOST_CHECK(GetSerializeSize(R1L, PROTOCOL_VERSION) == 32);
+ BOOST_CHECK(GetSerializeSize(ZeroL, PROTOCOL_VERSION) == 32);
CDataStream ss(0, PROTOCOL_VERSION);
ss << R1L;
@@ -230,8 +230,8 @@ BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHex begin() end() size() GetLow64 G
BOOST_CHECK(OneS.begin() + 20 == OneS.end());
BOOST_CHECK(MaxS.begin() + 20 == MaxS.end());
BOOST_CHECK(TmpS.begin() + 20 == TmpS.end());
- BOOST_CHECK(GetSerializeSize(R1S, 0, PROTOCOL_VERSION) == 20);
- BOOST_CHECK(GetSerializeSize(ZeroS, 0, PROTOCOL_VERSION) == 20);
+ BOOST_CHECK(GetSerializeSize(R1S, PROTOCOL_VERSION) == 20);
+ BOOST_CHECK(GetSerializeSize(ZeroS, PROTOCOL_VERSION) == 20);
ss << R1S;
BOOST_CHECK(ss.str() == std::string(R1Array,R1Array+20));
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index c74eb7531a..9acebdd820 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -2,13 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <util.h>
+#include <util/system.h>
#include <clientversion.h>
#include <primitives/transaction.h>
#include <sync.h>
-#include <utilstrencodings.h>
-#include <utilmoneystr.h>
+#include <util/strencodings.h>
+#include <util/moneystr.h>
#include <test/test_bitcoin.h>
#include <stdint.h>
@@ -187,7 +187,7 @@ struct TestArgsManager : public ArgsManager
m_config_args.clear();
}
std::string error;
- ReadConfigStream(streamConfig, error);
+ BOOST_REQUIRE(ReadConfigStream(streamConfig, error));
}
void SetNetworkOnlyArg(const std::string arg)
{
@@ -210,13 +210,13 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters)
std::string error;
testArgs.SetupArgs(4, avail_args);
- testArgs.ParseParameters(0, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(0, (char**)argv_test, error));
BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty());
- testArgs.ParseParameters(1, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(1, (char**)argv_test, error));
BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty());
- testArgs.ParseParameters(7, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
// expectation: -ignored is ignored (program name argument),
// -a, -b and -ccc end up in map, -d ignored because it is after
// a non-option argument (non-GNU option parsing)
@@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg)
"ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"};
std::string error;
testArgs.SetupArgs(6, avail_args);
- testArgs.ParseParameters(7, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(7, (char**)argv_test, error));
// Each letter should be set.
for (const char opt : "abcdef")
@@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases)
const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"};
testArgs.SetupArgs(2, avail_args);
std::string error;
- testArgs.ParseParameters(4, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(4, (char**)argv_test, error));
// This was passed twice, second one overrides the negative setting.
BOOST_CHECK(!testArgs.IsArgNegated("-foo"));
@@ -290,7 +290,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases)
// Config test
const char *conf_test = "nofoo=1\nfoo=1\nnobar=0\n";
- testArgs.ParseParameters(1, (char**)argv_test, error);
+ BOOST_CHECK(testArgs.ParseParameters(1, (char**)argv_test, error));
testArgs.ReadConfigString(conf_test);
// This was passed twice, second one overrides the negative setting,
@@ -305,7 +305,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases)
// Combined test
const char *combo_test_args[] = {"ignored", "-nofoo", "-bar"};
const char *combo_test_conf = "foo=1\nnobar=1\n";
- testArgs.ParseParameters(3, (char**)combo_test_args, error);
+ BOOST_CHECK(testArgs.ParseParameters(3, (char**)combo_test_args, error));
testArgs.ReadConfigString(combo_test_conf);
// Command line overrides, but doesn't erase old setting
@@ -557,38 +557,38 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
const char* testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1";
std::string error;
- test_args.ParseParameters(0, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "main");
- test_args.ParseParameters(2, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(2, (char**)argv_regtest, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "regtest");
- test_args.ParseParameters(3, (char**)argv_test_no_reg, error);
+ BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_test_no_reg, error));
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(3, (char**)argv_both, error);
+ BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error));
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
- test_args.ParseParameters(0, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(2, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(2, (char**)argv_regtest, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
- test_args.ParseParameters(3, (char**)argv_test_no_reg, error);
+ BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_test_no_reg, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(3, (char**)argv_both, error);
+ BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
@@ -596,23 +596,23 @@ BOOST_AUTO_TEST_CASE(util_GetChainName)
// [test] regtest=1 potentially relevant) doesn't break things
test_args.SelectConfigNetwork("test");
- test_args.ParseParameters(0, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(0, (char**)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(2, (char**)argv_testnet, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_testnet, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(2, (char**)argv_regtest, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_regtest, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
- test_args.ParseParameters(2, (char**)argv_test_no_reg, error);
+ BOOST_CHECK(test_args.ParseParameters(2, (char**)argv_test_no_reg, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_EQUAL(test_args.GetChainName(), "test");
- test_args.ParseParameters(3, (char**)argv_both, error);
+ BOOST_CHECK(test_args.ParseParameters(3, (char**)argv_both, error));
test_args.ReadConfigString(testnetconf);
BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error);
}
diff --git a/src/threadsafety.h b/src/threadsafety.h
index 47e6b2ea38..33acddc65c 100644
--- a/src/threadsafety.h
+++ b/src/threadsafety.h
@@ -54,4 +54,15 @@
#define ASSERT_EXCLUSIVE_LOCK(...)
#endif // __GNUC__
+// Utility class for indicating to compiler thread analysis that a mutex is
+// locked (when it couldn't be determined otherwise).
+struct SCOPED_LOCKABLE LockAnnotation
+{
+ template <typename Mutex>
+ explicit LockAnnotation(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
+ {
+ }
+ ~LockAnnotation() UNLOCK_FUNCTION() {}
+};
+
#endif // BITCOIN_THREADSAFETY_H
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 291111feb2..9c022c9ad1 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -11,8 +11,8 @@
#include <netaddress.h>
#include <sync.h>
#include <ui_interface.h>
-#include <util.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/strencodings.h>
#include <warnings.h>
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index c88f61f1ec..229cc7d553 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -4,10 +4,10 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <torcontrol.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <netbase.h>
#include <net.h>
-#include <util.h>
+#include <util/system.h>
#include <crypto/hmac_sha256.h>
#include <vector>
diff --git a/src/txdb.cpp b/src/txdb.cpp
index cbea550739..8447352c54 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -11,7 +11,7 @@
#include <pow.h>
#include <shutdown.h>
#include <uint256.h>
-#include <util.h>
+#include <util/system.h>
#include <ui_interface.h>
#include <stdint.h>
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 39434e4bb6..68f47d5cce 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -14,9 +14,9 @@
#include <reverse_iterator.h>
#include <streams.h>
#include <timedata.h>
-#include <util.h>
-#include <utilmoneystr.h>
-#include <utiltime.h>
+#include <util/system.h>
+#include <util/moneystr.h>
+#include <util/time.h>
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
int64_t _nTime, unsigned int _entryHeight,
@@ -156,9 +156,9 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
// GetMemPoolParents() is only valid for entries in the mempool, so we
// iterate mapTx to find parents.
for (unsigned int i = 0; i < tx.vin.size(); i++) {
- txiter piter = mapTx.find(tx.vin[i].prevout.hash);
- if (piter != mapTx.end()) {
- parentHashes.insert(piter);
+ boost::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);
+ if (piter) {
+ parentHashes.insert(*piter);
if (parentHashes.size() + 1 > limitAncestorCount) {
errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount);
return false;
@@ -364,12 +364,10 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
// Update transaction for any feeDelta created by PrioritiseTransaction
// TODO: refactor so that the fee delta is calculated before inserting
// into mapTx.
- std::map<uint256, CAmount>::const_iterator pos = mapDeltas.find(entry.GetTx().GetHash());
- if (pos != mapDeltas.end()) {
- const CAmount &delta = pos->second;
- if (delta) {
+ CAmount delta{0};
+ ApplyDelta(entry.GetTx().GetHash(), delta);
+ if (delta) {
mapTx.modify(newit, update_fee_delta(delta));
- }
}
// Update cachedInnerUsage to include contained transaction's usage.
@@ -391,11 +389,8 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
// to clean up the mess we're leaving here.
// Update ancestors with information about this tx
- for (const uint256 &phash : setParentTransactions) {
- txiter pit = mapTx.find(phash);
- if (pit != mapTx.end()) {
+ for (const auto& pit : GetIterSet(setParentTransactions)) {
UpdateParent(newit, pit, true);
- }
}
UpdateAncestorsOf(true, newit, setAncestors);
UpdateEntryForAncestors(newit, setAncestors);
@@ -503,7 +498,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
const CTransaction& tx = it->GetTx();
LockPoints lp = it->GetLockPoints();
bool validLP = TestLockPointValidity(&lp);
- if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(tx, flags, &lp, validLP)) {
+ if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(*this, tx, flags, &lp, validLP)) {
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
// So it's critical that we remove the tx and not depend on the LockPoints.
txToRemove.insert(it);
@@ -864,6 +859,29 @@ void CTxMemPool::ClearPrioritisation(const uint256 hash)
mapDeltas.erase(hash);
}
+const CTransaction* CTxMemPool::GetConflictTx(const COutPoint& prevout) const
+{
+ const auto it = mapNextTx.find(prevout);
+ return it == mapNextTx.end() ? nullptr : it->second;
+}
+
+boost::optional<CTxMemPool::txiter> CTxMemPool::GetIter(const uint256& txid) const
+{
+ auto it = mapTx.find(txid);
+ if (it != mapTx.end()) return it;
+ return boost::optional<txiter>{};
+}
+
+CTxMemPool::setEntries CTxMemPool::GetIterSet(const std::set<uint256>& hashes) const
+{
+ CTxMemPool::setEntries ret;
+ for (const auto& h : hashes) {
+ const auto mi = GetIter(h);
+ if (mi) ret.insert(*mi);
+ }
+ return ret;
+}
+
bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
{
for (unsigned int i = 0; i < tx.vin.size(); i++)
diff --git a/src/txmempool.h b/src/txmempool.h
index 2163b5eb21..fadb554723 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -15,6 +15,7 @@
#include <amount.h>
#include <coins.h>
+#include <crypto/siphash.h>
#include <indirectmap.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
@@ -343,13 +344,13 @@ struct TxMempoolInfo
* this is passed to the notification signal.
*/
enum class MemPoolRemovalReason {
- UNKNOWN = 0, //! Manually removed or unknown reason
- EXPIRY, //! Expired from mempool
- SIZELIMIT, //! Removed in size limiting
- REORG, //! Removed for reorganization
- BLOCK, //! Removed for block
- CONFLICT, //! Removed for conflict with in-block transaction
- REPLACED //! Removed for replacement
+ UNKNOWN = 0, //!< Manually removed or unknown reason
+ EXPIRY, //!< Expired from mempool
+ SIZELIMIT, //!< Removed in size limiting
+ REORG, //!< Removed for reorganization
+ BLOCK, //!< Removed for block
+ CONFLICT, //!< Removed for conflict with in-block transaction
+ REPLACED, //!< Removed for replacement
};
class SaltedTxidHasher
@@ -566,7 +567,15 @@ public:
void ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const;
void ClearPrioritisation(const uint256 hash);
-public:
+ /** Get the transaction in the pool that spends the same prevout */
+ const CTransaction* GetConflictTx(const COutPoint& prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ /** Returns an iterator to the given hash, if found */
+ boost::optional<txiter> GetIter(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
+ /** Translate a set of hashes into a set of pool iterators to avoid repeated lookups */
+ setEntries GetIterSet(const std::set<uint256>& hashes) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
/** Remove a set of transactions from the mempool.
* If a transaction is in this set, then all in-mempool descendants must
* also be in the set, unless this transaction is being removed for being
@@ -639,7 +648,7 @@ public:
return totalTxSize;
}
- bool exists(uint256 hash) const
+ bool exists(const uint256& hash) const
{
LOCK(cs);
return (mapTx.count(hash) != 0);
diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp
index 22b4768059..947d7e2308 100644
--- a/src/ui_interface.cpp
+++ b/src/ui_interface.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <ui_interface.h>
-#include <util.h>
+#include <util/system.h>
#include <boost/signals2/last_value.hpp>
#include <boost/signals2/signal.hpp>
diff --git a/src/uint256.cpp b/src/uint256.cpp
index e513dc1de7..d9da668036 100644
--- a/src/uint256.cpp
+++ b/src/uint256.cpp
@@ -5,7 +5,7 @@
#include <uint256.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <stdio.h>
#include <string.h>
@@ -29,7 +29,7 @@ void base_blob<BITS>::SetHex(const char* psz)
memset(data, 0, sizeof(data));
// skip leading spaces
- while (isspace(*psz))
+ while (IsSpace(*psz))
psz++;
// skip 0x
diff --git a/src/undo.h b/src/undo.h
index 4a78238944..3f50f4caad 100644
--- a/src/undo.h
+++ b/src/undo.h
@@ -11,6 +11,7 @@
#include <consensus/consensus.h>
#include <primitives/transaction.h>
#include <serialize.h>
+#include <version.h>
/** Undo information for a CTxIn
*
@@ -61,7 +62,7 @@ public:
explicit TxInUndoDeserializer(Coin* coin) : txout(coin) {}
};
-static const size_t MIN_TRANSACTION_INPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxIn(), SER_NETWORK, PROTOCOL_VERSION);
+static const size_t MIN_TRANSACTION_INPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxIn(), PROTOCOL_VERSION);
static const size_t MAX_INPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_INPUT_WEIGHT;
/** Undo information for a CTransaction */
diff --git a/src/univalue/gen/gen.cpp b/src/univalue/gen/gen.cpp
index 17f361941d..85fe20924a 100644
--- a/src/univalue/gen/gen.cpp
+++ b/src/univalue/gen/gen.cpp
@@ -12,8 +12,6 @@
#include <string.h>
#include "univalue.h"
-using namespace std;
-
static bool initEscapes;
static std::string escapes[256];
diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h
index c15b2f051e..91b104e56e 100644
--- a/src/univalue/include/univalue.h
+++ b/src/univalue/include/univalue.h
@@ -15,7 +15,6 @@
#include <cassert>
#include <sstream> // .get_int64()
-#include <utility> // std::pair
class UniValue {
public:
@@ -177,76 +176,9 @@ public:
const UniValue& get_array() const;
enum VType type() const { return getType(); }
- bool push_back(std::pair<std::string,UniValue> pear) {
- return pushKV(pear.first, pear.second);
- }
friend const UniValue& find_value( const UniValue& obj, const std::string& name);
};
-//
-// The following were added for compatibility with json_spirit.
-// Most duplicate other methods, and should be removed.
-//
-static inline std::pair<std::string,UniValue> Pair(const char *cKey, const char *cVal)
-{
- std::string key(cKey);
- UniValue uVal(cVal);
- return std::make_pair(key, uVal);
-}
-
-static inline std::pair<std::string,UniValue> Pair(const char *cKey, std::string strVal)
-{
- std::string key(cKey);
- UniValue uVal(strVal);
- return std::make_pair(key, uVal);
-}
-
-static inline std::pair<std::string,UniValue> Pair(const char *cKey, uint64_t u64Val)
-{
- std::string key(cKey);
- UniValue uVal(u64Val);
- return std::make_pair(key, uVal);
-}
-
-static inline std::pair<std::string,UniValue> Pair(const char *cKey, int64_t i64Val)
-{
- std::string key(cKey);
- UniValue uVal(i64Val);
- return std::make_pair(key, uVal);
-}
-
-static inline std::pair<std::string,UniValue> Pair(const char *cKey, bool iVal)
-{
- std::string key(cKey);
- UniValue uVal(iVal);
- return std::make_pair(key, uVal);
-}
-
-static inline std::pair<std::string,UniValue> Pair(const char *cKey, int iVal)
-{
- std::string key(cKey);
- UniValue uVal(iVal);
- return std::make_pair(key, uVal);
-}
-
-static inline std::pair<std::string,UniValue> Pair(const char *cKey, double dVal)
-{
- std::string key(cKey);
- UniValue uVal(dVal);
- return std::make_pair(key, uVal);
-}
-
-static inline std::pair<std::string,UniValue> Pair(const char *cKey, const UniValue& uVal)
-{
- std::string key(cKey);
- return std::make_pair(key, uVal);
-}
-
-static inline std::pair<std::string,UniValue> Pair(std::string key, const UniValue& uVal)
-{
- return std::make_pair(key, uVal);
-}
-
enum jtokentype {
JTOK_ERR = -1,
JTOK_NONE = 0, // eof
diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp
index d8ad7c4b90..4c9c15d63e 100644
--- a/src/univalue/lib/univalue.cpp
+++ b/src/univalue/lib/univalue.cpp
@@ -10,8 +10,6 @@
#include "univalue.h"
-using namespace std;
-
const UniValue NullUniValue;
void UniValue::clear()
@@ -37,15 +35,15 @@ bool UniValue::setBool(bool val_)
return true;
}
-static bool validNumStr(const string& s)
+static bool validNumStr(const std::string& s)
{
- string tokenVal;
+ std::string tokenVal;
unsigned int consumed;
enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size());
return (tt == JTOK_NUMBER);
}
-bool UniValue::setNumStr(const string& val_)
+bool UniValue::setNumStr(const std::string& val_)
{
if (!validNumStr(val_))
return false;
@@ -58,7 +56,7 @@ bool UniValue::setNumStr(const string& val_)
bool UniValue::setInt(uint64_t val_)
{
- ostringstream oss;
+ std::ostringstream oss;
oss << val_;
@@ -67,7 +65,7 @@ bool UniValue::setInt(uint64_t val_)
bool UniValue::setInt(int64_t val_)
{
- ostringstream oss;
+ std::ostringstream oss;
oss << val_;
@@ -76,7 +74,7 @@ bool UniValue::setInt(int64_t val_)
bool UniValue::setFloat(double val_)
{
- ostringstream oss;
+ std::ostringstream oss;
oss << std::setprecision(16) << val_;
@@ -85,7 +83,7 @@ bool UniValue::setFloat(double val_)
return ret;
}
-bool UniValue::setStr(const string& val_)
+bool UniValue::setStr(const std::string& val_)
{
clear();
typ = VSTR;
diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp
index ae75cb462a..14834db24d 100644
--- a/src/univalue/lib/univalue_read.cpp
+++ b/src/univalue/lib/univalue_read.cpp
@@ -8,8 +8,6 @@
#include "univalue.h"
#include "univalue_utffilter.h"
-using namespace std;
-
static bool json_isdigit(int ch)
{
return ((ch >= '0') && (ch <= '9'));
@@ -42,7 +40,7 @@ static const char *hatoui(const char *first, const char *last,
return first;
}
-enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
+enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed,
const char *raw, const char *end)
{
tokenVal.clear();
@@ -114,7 +112,7 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
case '8':
case '9': {
// part 1: int
- string numStr;
+ std::string numStr;
const char *first = raw;
@@ -174,7 +172,7 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed,
case '"': {
raw++; // skip "
- string valStr;
+ std::string valStr;
JSONUTF8StringFilter writer(valStr);
while (true) {
@@ -255,9 +253,9 @@ bool UniValue::read(const char *raw, size_t size)
clear();
uint32_t expectMask = 0;
- vector<UniValue*> stack;
+ std::vector<UniValue*> stack;
- string tokenVal;
+ std::string tokenVal;
unsigned int consumed;
enum jtokentype tok = JTOK_NONE;
enum jtokentype last_tok = JTOK_NONE;
diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp
index cf27835991..827eb9b271 100644
--- a/src/univalue/lib/univalue_write.cpp
+++ b/src/univalue/lib/univalue_write.cpp
@@ -8,11 +8,9 @@
#include "univalue.h"
#include "univalue_escapes.h"
-using namespace std;
-
-static string json_escape(const string& inS)
+static std::string json_escape(const std::string& inS)
{
- string outS;
+ std::string outS;
outS.reserve(inS.size() * 2);
for (unsigned int i = 0; i < inS.size(); i++) {
@@ -28,10 +26,10 @@ static string json_escape(const string& inS)
return outS;
}
-string UniValue::write(unsigned int prettyIndent,
- unsigned int indentLevel) const
+std::string UniValue::write(unsigned int prettyIndent,
+ unsigned int indentLevel) const
{
- string s;
+ std::string s;
s.reserve(1024);
unsigned int modIndent = indentLevel;
@@ -62,12 +60,12 @@ string UniValue::write(unsigned int prettyIndent,
return s;
}
-static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, string& s)
+static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, std::string& s)
{
s.append(prettyIndent * indentLevel, ' ');
}
-void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, string& s) const
+void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const
{
s += "[";
if (prettyIndent)
@@ -89,7 +87,7 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s
s += "]";
}
-void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, string& s) const
+void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const
{
s += "{";
if (prettyIndent)
diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp
index 2c37794a4b..75c0dc225a 100644
--- a/src/univalue/test/unitester.cpp
+++ b/src/univalue/test/unitester.cpp
@@ -17,8 +17,7 @@
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif
-using namespace std;
-string srcdir(JSON_TEST_SRC);
+std::string srcdir(JSON_TEST_SRC);
static bool test_failed = false;
#define d_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", filename.c_str()); } }
@@ -30,9 +29,9 @@ static std::string rtrim(std::string s)
return s;
}
-static void runtest(string filename, const string& jdata)
+static void runtest(std::string filename, const std::string& jdata)
{
- string prefix = filename.substr(0, 4);
+ std::string prefix = filename.substr(0, 4);
bool wantPass = (prefix == "pass") || (prefix == "roun");
bool wantFail = (prefix == "fail");
@@ -56,19 +55,19 @@ static void runtest(string filename, const string& jdata)
static void runtest_file(const char *filename_)
{
- string basename(filename_);
- string filename = srcdir + "/" + basename;
+ std::string basename(filename_);
+ std::string filename = srcdir + "/" + basename;
FILE *f = fopen(filename.c_str(), "r");
assert(f != NULL);
- string jdata;
+ std::string jdata;
char buf[4096];
while (!feof(f)) {
int bread = fread(buf, 1, sizeof(buf), f);
assert(!ferror(f));
- string s(buf, bread);
+ std::string s(buf, bread);
jdata += s;
}
diff --git a/src/util/bytevectorhash.cpp b/src/util/bytevectorhash.cpp
new file mode 100644
index 0000000000..f87d0e04b3
--- /dev/null
+++ b/src/util/bytevectorhash.cpp
@@ -0,0 +1,18 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <crypto/siphash.h>
+#include <random.h>
+#include <util/bytevectorhash.h>
+
+ByteVectorHash::ByteVectorHash()
+{
+ GetRandBytes(reinterpret_cast<unsigned char*>(&m_k0), sizeof(m_k0));
+ GetRandBytes(reinterpret_cast<unsigned char*>(&m_k1), sizeof(m_k1));
+}
+
+size_t ByteVectorHash::operator()(const std::vector<unsigned char>& input) const
+{
+ return CSipHasher(m_k0, m_k1).Write(input.data(), input.size()).Finalize();
+}
diff --git a/src/util/bytevectorhash.h b/src/util/bytevectorhash.h
new file mode 100644
index 0000000000..b88c17460b
--- /dev/null
+++ b/src/util/bytevectorhash.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_UTIL_BYTEVECTORHASH_H
+#define BITCOIN_UTIL_BYTEVECTORHASH_H
+
+#include <stdint.h>
+#include <vector>
+
+/**
+ * Implementation of Hash named requirement for types that internally store a byte array. This may
+ * be used as the hash function in std::unordered_set or std::unordered_map over such types.
+ * Internally, this uses a random instance of SipHash-2-4.
+ */
+class ByteVectorHash final
+{
+private:
+ uint64_t m_k0, m_k1;
+
+public:
+ ByteVectorHash();
+ size_t operator()(const std::vector<unsigned char>& input) const;
+};
+
+#endif // BITCOIN_UTIL_BYTEVECTORHASH_H
diff --git a/src/utilmemory.h b/src/util/memory.h
index e71fe92284..15ecf8f80d 100644
--- a/src/utilmemory.h
+++ b/src/util/memory.h
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_UTILMEMORY_H
-#define BITCOIN_UTILMEMORY_H
+#ifndef BITCOIN_UTIL_MEMORY_H
+#define BITCOIN_UTIL_MEMORY_H
#include <memory>
#include <utility>
diff --git a/src/utilmoneystr.cpp b/src/util/moneystr.cpp
index a9af59a11d..4c4de7b729 100644
--- a/src/utilmoneystr.cpp
+++ b/src/util/moneystr.cpp
@@ -3,11 +3,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <utilmoneystr.h>
+#include <util/moneystr.h>
#include <primitives/transaction.h>
#include <tinyformat.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
std::string FormatMoney(const CAmount& n)
{
@@ -41,14 +41,14 @@ bool ParseMoney(const char* pszIn, CAmount& nRet)
std::string strWhole;
int64_t nUnits = 0;
const char* p = pszIn;
- while (isspace(*p))
+ while (IsSpace(*p))
p++;
for (; *p; p++)
{
if (*p == '.')
{
p++;
- int64_t nMult = CENT*10;
+ int64_t nMult = COIN / 10;
while (isdigit(*p) && (nMult > 0))
{
nUnits += nMult * (*p++ - '0');
@@ -56,14 +56,14 @@ bool ParseMoney(const char* pszIn, CAmount& nRet)
}
break;
}
- if (isspace(*p))
+ if (IsSpace(*p))
break;
if (!isdigit(*p))
return false;
strWhole.insert(strWhole.end(), *p);
}
for (; *p; p++)
- if (!isspace(*p))
+ if (!IsSpace(*p))
return false;
if (strWhole.size() > 10) // guard against 63 bit overflow
return false;
diff --git a/src/utilmoneystr.h b/src/util/moneystr.h
index 67579adb77..b8e2812a96 100644
--- a/src/utilmoneystr.h
+++ b/src/util/moneystr.h
@@ -6,19 +6,20 @@
/**
* Money parsing/formatting utilities.
*/
-#ifndef BITCOIN_UTILMONEYSTR_H
-#define BITCOIN_UTILMONEYSTR_H
-
-#include <stdint.h>
-#include <string>
+#ifndef BITCOIN_UTIL_MONEYSTR_H
+#define BITCOIN_UTIL_MONEYSTR_H
#include <amount.h>
+#include <attributes.h>
+
+#include <cstdint>
+#include <string>
/* Do not use these functions to represent or parse monetary amounts to or from
* JSON but use AmountFromValue and ValueFromAmount for that.
*/
std::string FormatMoney(const CAmount& n);
-bool ParseMoney(const std::string& str, CAmount& nRet);
-bool ParseMoney(const char* pszIn, CAmount& nRet);
+NODISCARD bool ParseMoney(const std::string& str, CAmount& nRet);
+NODISCARD bool ParseMoney(const char* pszIn, CAmount& nRet);
-#endif // BITCOIN_UTILMONEYSTR_H
+#endif // BITCOIN_UTIL_MONEYSTR_H
diff --git a/src/utilstrencodings.cpp b/src/util/strencodings.cpp
index 4940267bae..46146be66f 100644
--- a/src/utilstrencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -3,7 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <tinyformat.h>
@@ -20,6 +20,7 @@ static const std::string SAFE_CHARS[] =
CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
+ CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
};
std::string SanitizeString(const std::string& str, int rule)
@@ -85,7 +86,7 @@ std::vector<unsigned char> ParseHex(const char* psz)
std::vector<unsigned char> vch;
while (true)
{
- while (isspace(*psz))
+ while (IsSpace(*psz))
psz++;
signed char c = HexDigit(*psz++);
if (c == (signed char)-1)
@@ -262,11 +263,11 @@ std::string DecodeBase32(const std::string& str)
return std::string((const char*)vchRet.data(), vchRet.size());
}
-static bool ParsePrechecks(const std::string& str)
+NODISCARD static bool ParsePrechecks(const std::string& str)
{
if (str.empty()) // No empty string allowed
return false;
- if (str.size() >= 1 && (isspace(str[0]) || isspace(str[str.size()-1]))) // No padding allowed
+ if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size()-1]))) // No padding allowed
return false;
if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed
return false;
diff --git a/src/utilstrencodings.h b/src/util/strencodings.h
index 846a3917a2..7d16d7dcfd 100644
--- a/src/utilstrencodings.h
+++ b/src/util/strencodings.h
@@ -6,10 +6,12 @@
/**
* Utilities for converting data from/to strings.
*/
-#ifndef BITCOIN_UTILSTRENCODINGS_H
-#define BITCOIN_UTILSTRENCODINGS_H
+#ifndef BITCOIN_UTIL_STRENCODINGS_H
+#define BITCOIN_UTIL_STRENCODINGS_H
-#include <stdint.h>
+#include <attributes.h>
+
+#include <cstdint>
#include <string>
#include <vector>
@@ -25,6 +27,7 @@ enum SafeChars
SAFE_CHARS_DEFAULT, //!< The full set of allowed chars
SAFE_CHARS_UA_COMMENT, //!< BIP-0014 subset
SAFE_CHARS_FILENAME, //!< Chars allowed in filenames
+ SAFE_CHARS_URI, //!< Chars allowed in URIs (RFC 3986)
};
/**
@@ -72,39 +75,54 @@ constexpr bool IsDigit(char c)
}
/**
+ * Tests if the given character is a whitespace character. The whitespace characters
+ * are: space, form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal
+ * tab ('\t'), and vertical tab ('\v').
+ *
+ * This function is locale independent. Under the C locale this function gives the
+ * same result as std::isspace.
+ *
+ * @param[in] c character to test
+ * @return true if the argument is a whitespace character; otherwise false
+ */
+constexpr inline bool IsSpace(char c) noexcept {
+ return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v';
+}
+
+/**
* Convert string to signed 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
-bool ParseInt32(const std::string& str, int32_t *out);
+NODISCARD bool ParseInt32(const std::string& str, int32_t *out);
/**
* Convert string to signed 64-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
-bool ParseInt64(const std::string& str, int64_t *out);
+NODISCARD bool ParseInt64(const std::string& str, int64_t *out);
/**
* Convert decimal string to unsigned 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
-bool ParseUInt32(const std::string& str, uint32_t *out);
+NODISCARD bool ParseUInt32(const std::string& str, uint32_t *out);
/**
* Convert decimal string to unsigned 64-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
-bool ParseUInt64(const std::string& str, uint64_t *out);
+NODISCARD bool ParseUInt64(const std::string& str, uint64_t *out);
/**
* Convert string to double with strict parse error feedback.
* @returns true if the entire string could be parsed as valid double,
* false if not the entire string could be parsed or when overflow or underflow occurred.
*/
-bool ParseDouble(const std::string& str, double *out);
+NODISCARD bool ParseDouble(const std::string& str, double *out);
template<typename T>
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
@@ -157,7 +175,7 @@ bool TimingResistantEqual(const T& a, const T& b)
* @returns true on success, false on error.
* @note The result must be in the range (-10^18,10^18), otherwise an overflow error will trigger.
*/
-bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
+NODISCARD bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
/** Convert from one power-of-2 number base to another. */
template<int frombits, int tobits, bool pad, typename O, typename I>
@@ -184,7 +202,7 @@ bool ConvertBits(const O& outfn, I it, I end) {
}
/** Parse an HD keypaths like "m/7/0'/2000". */
-bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath);
+NODISCARD bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath);
/**
* Converts the given character to its lowercase equivalent.
@@ -230,4 +248,4 @@ constexpr unsigned char ToUpper(unsigned char c)
*/
std::string Capitalize(std::string str);
-#endif // BITCOIN_UTILSTRENCODINGS_H
+#endif // BITCOIN_UTIL_STRENCODINGS_H
diff --git a/src/util.cpp b/src/util/system.cpp
index 84d8175389..8e201ec590 100644
--- a/src/util.cpp
+++ b/src/util/system.cpp
@@ -3,12 +3,12 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <util.h>
+#include <util/system.h>
#include <chainparamsbase.h>
#include <random.h>
#include <serialize.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <stdarg.h>
@@ -58,8 +58,10 @@
#ifndef NOMINMAX
#define NOMINMAX
#endif
+#include <codecvt>
#include <io.h> /* for _commit */
+#include <shellapi.h>
#include <shlobj.h>
#endif
@@ -71,7 +73,6 @@
#include <malloc.h>
#endif
-#include <boost/thread.hpp>
#include <openssl/crypto.h>
#include <openssl/rand.h>
#include <openssl/conf.h>
@@ -370,15 +371,17 @@ ArgsManager::ArgsManager() :
// nothing to do
}
-void ArgsManager::WarnForSectionOnlyArgs()
+const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
{
+ std::set<std::string> unsuitables;
+
LOCK(cs_args);
// if there's no section selected, don't worry
- if (m_network.empty()) return;
+ if (m_network.empty()) return std::set<std::string> {};
// if it's okay to use the default section for this network, don't worry
- if (m_network == CBaseChainParams::MAIN) return;
+ if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {};
for (const auto& arg : m_network_only_args) {
std::pair<bool, std::string> found_result;
@@ -396,8 +399,28 @@ void ArgsManager::WarnForSectionOnlyArgs()
if (!found_result.first) continue;
// otherwise, issue a warning
- LogPrintf("Warning: Config setting for %s only applied on %s network when in [%s] section.\n", arg, m_network, m_network);
+ unsuitables.insert(arg);
}
+ return unsuitables;
+}
+
+
+const std::set<std::string> ArgsManager::GetUnrecognizedSections() const
+{
+ // Section names to be recognized in the config file.
+ static const std::set<std::string> available_sections{
+ CBaseChainParams::REGTEST,
+ CBaseChainParams::TESTNET,
+ CBaseChainParams::MAIN
+ };
+ std::set<std::string> diff;
+
+ LOCK(cs_args);
+ std::set_difference(
+ m_config_sections.begin(), m_config_sections.end(),
+ available_sections.begin(), available_sections.end(),
+ std::inserter(diff, diff.end()));
+ return diff;
}
void ArgsManager::SelectConfigNetwork(const std::string& network)
@@ -659,7 +682,7 @@ std::string ArgsManager::GetHelpMessage() const
bool HelpRequested(const ArgsManager& args)
{
- return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help");
+ return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug");
}
static const int screenWidth = 79;
@@ -818,27 +841,38 @@ static std::string TrimString(const std::string& str, const std::string& pattern
return str.substr(front, end - front + 1);
}
-static bool GetConfigOptions(std::istream& stream, std::string& error, std::vector<std::pair<std::string, std::string>> &options)
+static bool GetConfigOptions(std::istream& stream, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::set<std::string>& sections)
{
std::string str, prefix;
std::string::size_type pos;
int linenr = 1;
while (std::getline(stream, str)) {
+ bool used_hash = false;
if ((pos = str.find('#')) != std::string::npos) {
str = str.substr(0, pos);
+ used_hash = true;
}
const static std::string pattern = " \t\r\n";
str = TrimString(str, pattern);
if (!str.empty()) {
if (*str.begin() == '[' && *str.rbegin() == ']') {
- prefix = str.substr(1, str.size() - 2) + '.';
+ const std::string section = str.substr(1, str.size() - 2);
+ sections.insert(section);
+ prefix = section + '.';
} else if (*str.begin() == '-') {
error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
return false;
} else if ((pos = str.find('=')) != std::string::npos) {
std::string name = prefix + TrimString(str.substr(0, pos), pattern);
std::string value = TrimString(str.substr(pos + 1), pattern);
+ if (used_hash && name == "rpcpassword") {
+ error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr);
+ return false;
+ }
options.emplace_back(name, value);
+ if ((pos = name.rfind('.')) != std::string::npos) {
+ sections.insert(name.substr(0, pos));
+ }
} else {
error = strprintf("parse error on line %i: %s", linenr, str);
if (str.size() >= 2 && str.substr(0, 2) == "no") {
@@ -856,7 +890,8 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, boo
{
LOCK(cs_args);
std::vector<std::pair<std::string, std::string>> options;
- if (!GetConfigOptions(stream, error, options)) {
+ m_config_sections.clear();
+ if (!GetConfigOptions(stream, error, options, m_config_sections)) {
return false;
}
for (const std::pair<std::string, std::string>& option : options) {
@@ -890,7 +925,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
}
const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
- fs::ifstream stream(GetConfigFile(confPath));
+ fsbridge::ifstream stream(GetConfigFile(confPath));
// ok to not have a config file
if (stream.good()) {
@@ -923,7 +958,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
}
for (const std::string& to_include : includeconf) {
- fs::ifstream include_config(GetConfigFile(to_include));
+ fsbridge::ifstream include_config(GetConfigFile(to_include));
if (include_config.good()) {
if (!ReadConfigStream(include_config, error, ignore_invalid_keys)) {
return false;
@@ -997,7 +1032,7 @@ void CreatePidFile(const fs::path &path, pid_t pid)
bool RenameOver(fs::path src, fs::path dest)
{
#ifdef WIN32
- return MoveFileExA(src.string().c_str(), dest.string().c_str(),
+ return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(),
MOVEFILE_REPLACE_EXISTING) != 0;
#else
int rc = std::rename(src.string().c_str(), dest.string().c_str());
@@ -1139,14 +1174,14 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
{
- char pszPath[MAX_PATH] = "";
+ WCHAR pszPath[MAX_PATH] = L"";
- if(SHGetSpecialFolderPathA(nullptr, pszPath, nFolder, fCreate))
+ if(SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate))
{
return fs::path(pszPath);
}
- LogPrintf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n");
+ LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
return fs::path("");
}
#endif
@@ -1154,7 +1189,11 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
void runCommand(const std::string& strCommand)
{
if (strCommand.empty()) return;
+#ifndef WIN32
int nErr = ::system(strCommand.c_str());
+#else
+ int nErr = ::_wsystem(std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().from_bytes(strCommand).c_str());
+#endif
if (nErr)
LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
}
@@ -1195,13 +1234,21 @@ void SetupEnvironment()
} catch (const std::runtime_error&) {
setenv("LC_ALL", "C", 1);
}
+#elif defined(WIN32)
+ // Set the default input/output charset is utf-8
+ SetConsoleCP(CP_UTF8);
+ SetConsoleOutputCP(CP_UTF8);
#endif
// The path locale is lazy initialized and to avoid deinitialization errors
// in multithreading environments, it is set explicitly by the main thread.
// A dummy locale is used to extract the internal default locale, used by
// fs::path, which is then used to explicitly imbue the path.
std::locale loc = fs::path::imbue(std::locale::classic());
+#ifndef WIN32
fs::path::imbue(loc);
+#else
+ fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16<wchar_t>()));
+#endif
}
bool SetupNetworking()
@@ -1243,7 +1290,7 @@ fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific)
return fs::absolute(path, GetDataDir(net_specific));
}
-int ScheduleBatchPriority(void)
+int ScheduleBatchPriority()
{
#ifdef SCHED_BATCH
const static sched_param param{0};
@@ -1256,3 +1303,30 @@ int ScheduleBatchPriority(void)
return 1;
#endif
}
+
+namespace util {
+#ifdef WIN32
+WinCmdLineArgs::WinCmdLineArgs()
+{
+ wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
+ argv = new char*[argc];
+ args.resize(argc);
+ for (int i = 0; i < argc; i++) {
+ args[i] = utf8_cvt.to_bytes(wargv[i]);
+ argv[i] = &*args[i].begin();
+ }
+ LocalFree(wargv);
+}
+
+WinCmdLineArgs::~WinCmdLineArgs()
+{
+ delete[] argv;
+}
+
+std::pair<int, char**> WinCmdLineArgs::get()
+{
+ return std::make_pair(argc, argv);
+}
+#endif
+} // namespace util
diff --git a/src/util.h b/src/util/system.h
index 7bf9fdbe12..dca32cc6fc 100644
--- a/src/util.h
+++ b/src/util/system.h
@@ -7,20 +7,21 @@
* Server/client environment: argument handling, config file parsing,
* thread wrappers, startup time
*/
-#ifndef BITCOIN_UTIL_H
-#define BITCOIN_UTIL_H
+#ifndef BITCOIN_UTIL_SYSTEM_H
+#define BITCOIN_UTIL_SYSTEM_H
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
+#include <attributes.h>
#include <compat.h>
#include <fs.h>
#include <logging.h>
#include <sync.h>
#include <tinyformat.h>
-#include <utilmemory.h>
-#include <utiltime.h>
+#include <util/memory.h>
+#include <util/time.h>
#include <atomic>
#include <exception>
@@ -29,6 +30,7 @@
#include <stdint.h>
#include <string>
#include <unordered_set>
+#include <utility>
#include <vector>
#include <boost/thread/condition_variable.hpp> // for boost::thread_interrupted
@@ -147,8 +149,9 @@ protected:
std::string m_network GUARDED_BY(cs_args);
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
+ std::set<std::string> m_config_sections GUARDED_BY(cs_args);
- bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false);
+ NODISCARD bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false);
public:
ArgsManager();
@@ -158,8 +161,8 @@ public:
*/
void SelectConfigNetwork(const std::string& network);
- bool ParseParameters(int argc, const char* const argv[], std::string& error);
- bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
+ NODISCARD bool ParseParameters(int argc, const char* const argv[], std::string& error);
+ NODISCARD bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
/**
* Log warnings for options in m_section_only_args when
@@ -167,7 +170,12 @@ public:
* on the command line or in a network-specific section in the
* config file.
*/
- void WarnForSectionOnlyArgs();
+ const std::set<std::string> GetUnsuitableSectionOnlyArgs() const;
+
+ /**
+ * Log warnings for unrecognized section names in the config file.
+ */
+ const std::set<std::string> GetUnrecognizedSections() const;
/**
* Return a vector of strings of the given argument
@@ -347,7 +355,7 @@ std::string CopyrightHolders(const std::string& strPrefix);
* @return The return value of sched_setschedule(), or 1 on systems without
* sched_setschedule().
*/
-int ScheduleBatchPriority(void);
+int ScheduleBatchPriority();
namespace util {
@@ -361,6 +369,21 @@ inline void insert(std::set<TsetT>& dst, const Tsrc& src) {
dst.insert(src.begin(), src.end());
}
+#ifdef WIN32
+class WinCmdLineArgs
+{
+public:
+ WinCmdLineArgs();
+ ~WinCmdLineArgs();
+ std::pair<int, char**> get();
+
+private:
+ int argc;
+ char** argv;
+ std::vector<std::string> args;
+};
+#endif
+
} // namespace util
-#endif // BITCOIN_UTIL_H
+#endif // BITCOIN_UTIL_SYSTEM_H
diff --git a/src/utiltime.cpp b/src/util/time.cpp
index 908791da48..83a7937d8f 100644
--- a/src/utiltime.cpp
+++ b/src/util/time.cpp
@@ -7,7 +7,7 @@
#include <config/bitcoin-config.h>
#endif
-#include <utiltime.h>
+#include <util/time.h>
#include <atomic>
#include <boost/date_time/posix_time/posix_time.hpp>
diff --git a/src/utiltime.h b/src/util/time.h
index c3e3da3014..f2e2747434 100644
--- a/src/utiltime.h
+++ b/src/util/time.h
@@ -3,8 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#ifndef BITCOIN_UTILTIME_H
-#define BITCOIN_UTILTIME_H
+#ifndef BITCOIN_UTIL_TIME_H
+#define BITCOIN_UTIL_TIME_H
#include <stdint.h>
#include <string>
@@ -35,4 +35,4 @@ std::string FormatISO8601DateTime(int64_t nTime);
std::string FormatISO8601Date(int64_t nTime);
std::string FormatISO8601Time(int64_t nTime);
-#endif // BITCOIN_UTILTIME_H
+#endif // BITCOIN_UTIL_TIME_H
diff --git a/src/validation.cpp b/src/validation.cpp
index 7ec0c6a961..6333dd98d2 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -35,9 +35,9 @@
#include <txmempool.h>
#include <ui_interface.h>
#include <undo.h>
-#include <util.h>
-#include <utilmoneystr.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/moneystr.h>
+#include <util/strencodings.h>
#include <validationinterface.h>
#include <warnings.h>
@@ -361,10 +361,10 @@ bool TestLockPointValidity(const LockPoints* lp)
return true;
}
-bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool useExistingLockPoints)
+bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp, bool useExistingLockPoints)
{
AssertLockHeld(cs_main);
- AssertLockHeld(mempool.cs);
+ AssertLockHeld(pool.cs);
CBlockIndex* tip = chainActive.Tip();
assert(tip != nullptr);
@@ -387,7 +387,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
}
else {
// pcoinsTip contains the UTXO set for chainActive.Tip()
- CCoinsViewMemPool viewMemPool(pcoinsTip.get(), mempool);
+ CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
@@ -584,7 +584,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// Do not work on transactions that are too small.
// A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes.
// Transactions smaller than this are not relayed to reduce unnecessary malloc overhead.
- if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE)
+ if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE)
return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small");
// Only accept nLockTime-using transactions that can be mined in the next
@@ -602,10 +602,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
std::set<uint256> setConflicts;
for (const CTxIn &txin : tx.vin)
{
- auto itConflicting = pool.mapNextTx.find(txin.prevout);
- if (itConflicting != pool.mapNextTx.end())
- {
- const CTransaction *ptxConflicting = itConflicting->second;
+ const CTransaction* ptxConflicting = pool.GetConflictTx(txin.prevout);
+ if (ptxConflicting) {
if (!setConflicts.count(ptxConflicting->GetHash()))
{
// Allow opt-out of transaction replacement by setting
@@ -681,7 +679,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// be mined yet.
// Must keep pool.cs for this unless we change CheckSequenceLocks to take a
// CoinsViewCache instead of create its own
- if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
+ if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
CAmount nFees = 0;
@@ -786,16 +784,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
CFeeRate newFeeRate(nModifiedFees, nSize);
std::set<uint256> setConflictsParents;
const int maxDescendantsToVisit = 100;
- CTxMemPool::setEntries setIterConflicting;
- for (const uint256 &hashConflicting : setConflicts)
- {
- CTxMemPool::txiter mi = pool.mapTx.find(hashConflicting);
- if (mi == pool.mapTx.end())
- continue;
-
- // Save these to avoid repeated lookups
- setIterConflicting.insert(mi);
-
+ const CTxMemPool::setEntries setIterConflicting = pool.GetIterSet(setConflicts);
+ for (const auto& mi : setIterConflicting) {
// Don't allow the replacement to reduce the feerate of the
// mempool.
//
@@ -861,11 +851,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// Rather than check the UTXO set - potentially expensive -
// it's cheaper to just check if the new input refers to a
// tx that's in the mempool.
- if (pool.mapTx.find(tx.vin[j].prevout.hash) != pool.mapTx.end())
+ if (pool.exists(tx.vin[j].prevout.hash)) {
return state.DoS(0, false,
REJECT_NONSTANDARD, "replacement-adds-unconfirmed", false,
strprintf("replacement %s adds unconfirmed input, idx %d",
hash.ToString(), j));
+ }
}
}
@@ -927,7 +918,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
- unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), Params().GetConsensus());
+ unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), chainparams.GetConsensus());
if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), FormatStateMessage(state));
@@ -1060,7 +1051,7 @@ static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMes
return error("WriteBlockToDisk: OpenBlockFile failed");
// Write index header
- unsigned int nSize = GetSerializeSize(fileout, block);
+ unsigned int nSize = GetSerializeSize(block, fileout.GetVersion());
fileout << messageStart << nSize;
// Write block
@@ -1470,7 +1461,7 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint
return error("%s: OpenUndoFile failed", __func__);
// Write index header
- unsigned int nSize = GetSerializeSize(fileout, blockundo);
+ unsigned int nSize = GetSerializeSize(blockundo, fileout.GetVersion());
fileout << messageStart << nSize;
// Write undo data
@@ -1668,7 +1659,7 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState&
// Write undo information to disk
if (pindex->GetUndoPos().IsNull()) {
CDiskBlockPos _pos;
- if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40))
+ if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, CLIENT_VERSION) + 40))
return error("ConnectBlock(): FindUndoPos failed");
if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
return AbortNode(state, "Failed to write undo data");
@@ -1689,8 +1680,7 @@ void ThreadScriptCheck() {
scriptcheckqueue.Thread();
}
-// Protected by cs_main
-VersionBitsCache versionbitscache;
+VersionBitsCache versionbitscache GUARDED_BY(cs_main);
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
@@ -1731,8 +1721,7 @@ public:
}
};
-// Protected by cs_main
-static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
+static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_main);
// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for
// mainnet. We no longer need to support disabling the segwit deployment
@@ -3119,7 +3108,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
// checks that use witness data may be performed here.
// Size limits
- if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
+ if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
// First transaction must be coinbase, the rest must not be
@@ -3131,7 +3120,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
// Check transactions
for (const auto& tx : block.vtx)
- if (!CheckTransaction(*tx, state, false))
+ if (!CheckTransaction(*tx, state, true))
return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(),
strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage()));
@@ -3436,7 +3425,7 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
static CDiskBlockPos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const CDiskBlockPos* dbp) {
- unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
+ unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION);
CDiskBlockPos blockPos;
if (dbp != nullptr)
blockPos = *dbp;
@@ -4142,6 +4131,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) {
const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight);
LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight);
+ uiInterface.ShowProgress(_("Replaying blocks..."), (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false);
if (!RollforwardBlock(pindex, cache, params)) return false;
}
@@ -4690,7 +4680,7 @@ int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::D
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
-bool LoadMempool(void)
+bool LoadMempool()
{
const CChainParams& chainparams = Params();
int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
@@ -4767,13 +4757,16 @@ bool LoadMempool(void)
return true;
}
-bool DumpMempool(void)
+bool DumpMempool()
{
int64_t start = GetTimeMicros();
std::map<uint256, CAmount> mapDeltas;
std::vector<TxMempoolInfo> vinfo;
+ static Mutex dump_mutex;
+ LOCK(dump_mutex);
+
{
LOCK(mempool.cs);
for (const auto &i : mempool.mapDeltas) {
diff --git a/src/validation.h b/src/validation.h
index 7cfbc81473..b5548a9293 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -54,9 +54,9 @@ static const bool DEFAULT_WHITELISTFORCERELAY = true;
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
//! -maxtxfee default
-static const CAmount DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN;
+static const CAmount DEFAULT_TRANSACTION_MAXFEE = COIN / 10;
//! Discourage users to set fees higher than this amount (in satoshis) per kB
-static const CAmount HIGH_TX_FEE_PER_KB = 0.01 * COIN;
+static const CAmount HIGH_TX_FEE_PER_KB = COIN / 100;
//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB;
/** Default for -limitancestorcount, max number of in-mempool ancestors */
@@ -261,7 +261,7 @@ bool LoadGenesisBlock(const CChainParams& chainparams);
* initializing state if we're running with -reindex. */
bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Update the chain tip based on database information. */
-bool LoadChainTip(const CChainParams& chainparams);
+bool LoadChainTip(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Unload database information */
void UnloadBlockIndex();
/** Run an instance of the script checking thread */
@@ -351,7 +351,7 @@ bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
*
* See consensus/consensus.h for flag definitions.
*/
-bool CheckSequenceLocks(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);
/**
* Closure representing one script verification
@@ -440,7 +440,7 @@ inline CBlockIndex* LookupBlockIndex(const uint256& hash)
}
/** Find the last common block between the parameter chain and a locator. */
-CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator);
+CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Mark a block as precious and reorganize.
*
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index e25eca2368..214a9ffba9 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -8,7 +8,7 @@
#include <primitives/block.h>
#include <scheduler.h>
#include <txmempool.h>
-#include <util.h>
+#include <util/system.h>
#include <validation.h>
#include <list>
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index b0d2bc8a14..3f297c0ebb 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -5,21 +5,6 @@
#include <versionbits.h>
#include <consensus/params.h>
-const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = {
- {
- /*.name =*/ "testdummy",
- /*.gbt_force =*/ true,
- },
- {
- /*.name =*/ "csv",
- /*.gbt_force =*/ true,
- },
- {
- /*.name =*/ "segwit",
- /*.gbt_force =*/ true,
- }
-};
-
ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
{
int nPeriod = Period(params);
diff --git a/src/versionbits.h b/src/versionbits.h
index e4bf9cb9be..cdc947cd9e 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -30,13 +30,6 @@ enum class ThresholdState {
// will either be nullptr or a block with (height + 1) % Period() == 0.
typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache;
-struct VBDeploymentInfo {
- /** Deployment name */
- const char *name;
- /** Whether GBT clients can safely ignore this rule in simplified usage */
- bool gbt_force;
-};
-
struct BIP9Stats {
int period;
int threshold;
@@ -45,8 +38,6 @@ struct BIP9Stats {
bool possible;
};
-extern const struct VBDeploymentInfo VersionBitsDeploymentInfo[];
-
/**
* Abstract class that implements BIP9-style threshold logic, and caches results.
*/
diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp
new file mode 100644
index 0000000000..ecf3482927
--- /dev/null
+++ b/src/versionbitsinfo.cpp
@@ -0,0 +1,22 @@
+// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <versionbitsinfo.h>
+
+#include <consensus/params.h>
+
+const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = {
+ {
+ /*.name =*/ "testdummy",
+ /*.gbt_force =*/ true,
+ },
+ {
+ /*.name =*/ "csv",
+ /*.gbt_force =*/ true,
+ },
+ {
+ /*.name =*/ "segwit",
+ /*.gbt_force =*/ true,
+ }
+};
diff --git a/src/versionbitsinfo.h b/src/versionbitsinfo.h
new file mode 100644
index 0000000000..a7822bc747
--- /dev/null
+++ b/src/versionbitsinfo.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2016-2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_VERSIONBITSINFO_H
+#define BITCOIN_VERSIONBITSINFO_H
+
+struct VBDeploymentInfo {
+ /** Deployment name */
+ const char *name;
+ /** Whether GBT clients can safely ignore this rule in simplified usage */
+ bool gbt_force;
+};
+
+extern const struct VBDeploymentInfo VersionBitsDeploymentInfo[];
+
+#endif // BITCOIN_VERSIONBITSINFO_H
diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp
index 645981faa4..87d2c4f06e 100644
--- a/src/wallet/coincontrol.cpp
+++ b/src/wallet/coincontrol.cpp
@@ -4,7 +4,7 @@
#include <wallet/coincontrol.h>
-#include <util.h>
+#include <util/system.h>
void CCoinControl::SetNull()
{
diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp
index fdeb89553b..5e955b8495 100644
--- a/src/wallet/coinselection.cpp
+++ b/src/wallet/coinselection.cpp
@@ -4,8 +4,8 @@
#include <wallet/coinselection.h>
-#include <util.h>
-#include <utilmoneystr.h>
+#include <util/system.h>
+#include <util/moneystr.h>
#include <boost/optional.hpp>
diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h
index 6d755d0969..5348401f45 100644
--- a/src/wallet/coinselection.h
+++ b/src/wallet/coinselection.h
@@ -10,7 +10,7 @@
#include <random.h>
//! target minimum change amount
-static const CAmount MIN_CHANGE = CENT;
+static constexpr CAmount MIN_CHANGE{COIN / 100};
//! final minimum change amount after paying for fees
static const CAmount MIN_FINAL_CHANGE = MIN_CHANGE/2;
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index 729e4e39b0..f5ac6e98b2 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -8,7 +8,7 @@
#include <crypto/sha512.h>
#include <script/script.h>
#include <script/standard.h>
-#include <util.h>
+#include <util/system.h>
#include <string>
#include <vector>
@@ -202,7 +202,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
if (keyPass && keyFail)
{
LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
- assert(false);
+ throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt.");
}
if (keyFail || !keyPass)
return false;
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index dfd60ae5eb..d75e30d336 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -8,7 +8,7 @@
#include <addrman.h>
#include <hash.h>
#include <protocol.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <wallet/walletutil.h>
#include <stdint.h>
@@ -20,6 +20,7 @@
#include <boost/thread.hpp>
namespace {
+
//! Make sure database has a unique fileid within the environment. If it
//! doesn't, throw an error. BDB caches do not work properly when more than one
//! open database has the same fileid (values written to one database may show
@@ -29,25 +30,19 @@ namespace {
//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
//! so bitcoin should never create different databases with the same fileid, but
//! this error can be triggered if users manually copy database files.
-void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db)
+void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
{
if (env.IsMock()) return;
- u_int8_t fileid[DB_FILE_ID_LEN];
- int ret = db.get_mpf()->get_fileid(fileid);
+ int ret = db.get_mpf()->get_fileid(fileid.value);
if (ret != 0) {
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
}
- for (const auto& item : env.mapDb) {
- u_int8_t item_fileid[DB_FILE_ID_LEN];
- if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 &&
- memcmp(fileid, item_fileid, sizeof(fileid)) == 0) {
- const char* item_filename = nullptr;
- item.second->get_dbname(&item_filename, nullptr);
+ for (const auto& item : env.m_fileids) {
+ if (fileid == item.second && &fileid != &item.second) {
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
- HexStr(std::begin(item_fileid), std::end(item_fileid)),
- item_filename ? item_filename : "(unknown database)"));
+ HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
}
}
}
@@ -56,9 +51,13 @@ CCriticalSection cs_db;
std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment.
} // namespace
-BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
+bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
+{
+ return memcmp(value, &rhs.value, sizeof(value)) == 0;
+}
+
+static void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename)
{
- fs::path env_directory;
if (fs::is_regular_file(wallet_path)) {
// Special case for backwards compatibility: if wallet path points to an
// existing file, treat it as the path to a BDB data file in a parent
@@ -71,6 +70,23 @@ BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& data
env_directory = wallet_path;
database_filename = "wallet.dat";
}
+}
+
+bool IsWalletLoaded(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;
+ return env->second.IsDatabaseLoaded(database_filename);
+}
+
+BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
+{
+ fs::path env_directory;
+ SplitWalletPath(wallet_path, env_directory, database_filename);
LOCK(cs_db);
// Note: An unused temporary BerkeleyEnvironment object may be created inside the
// emplace function if the key already exists. This is a little inefficient,
@@ -90,13 +106,13 @@ void BerkeleyEnvironment::Close()
fDbEnvInit = false;
- for (auto& db : mapDb) {
+ for (auto& db : m_databases) {
auto count = mapFileUseCount.find(db.first);
assert(count == mapFileUseCount.end() || count->second == 0);
- if (db.second) {
- db.second->close(0);
- delete db.second;
- db.second = nullptr;
+ BerkeleyDatabase& database = db.second.get();
+ if (database.m_db) {
+ database.m_db->close(0);
+ database.m_db.reset();
}
}
@@ -463,7 +479,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
if (!env->Open(false /* retry */))
throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
- pdb = env->mapDb[strFilename];
+ pdb = database.m_db.get();
if (pdb == nullptr) {
int ret;
std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
@@ -504,11 +520,11 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
// versions of BDB have an set_lk_exclusive method for this
// purpose, but the older version we use does not.)
for (const auto& env : g_dbenvs) {
- CheckUniqueFileid(env.second, strFilename, *pdb_temp);
+ CheckUniqueFileid(env.second, strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
}
pdb = pdb_temp.release();
- env->mapDb[strFilename] = pdb;
+ database.m_db.reset(pdb);
if (fCreate && !Exists(std::string("version"))) {
bool fTmp = fReadOnly;
@@ -556,22 +572,50 @@ void BerkeleyBatch::Close()
LOCK(cs_db);
--env->mapFileUseCount[strFile];
}
+ env->m_db_in_use.notify_all();
}
void BerkeleyEnvironment::CloseDb(const std::string& strFile)
{
{
LOCK(cs_db);
- if (mapDb[strFile] != nullptr) {
+ auto it = m_databases.find(strFile);
+ assert(it != m_databases.end());
+ BerkeleyDatabase& database = it->second.get();
+ if (database.m_db) {
// Close the database handle
- Db* pdb = mapDb[strFile];
- pdb->close(0);
- delete pdb;
- mapDb[strFile] = nullptr;
+ database.m_db->close(0);
+ database.m_db.reset();
}
}
}
+void BerkeleyEnvironment::ReloadDbEnv()
+{
+ // Make sure that no Db's are in use
+ AssertLockNotHeld(cs_db);
+ std::unique_lock<CCriticalSection> lock(cs_db);
+ m_db_in_use.wait(lock, [this](){
+ for (auto& count : mapFileUseCount) {
+ if (count.second > 0) return false;
+ }
+ return true;
+ });
+
+ std::vector<std::string> filenames;
+ for (auto it : m_databases) {
+ filenames.push_back(it.first);
+ }
+ // Close the individual Db's
+ for (const std::string& filename : filenames) {
+ CloseDb(filename);
+ }
+ // Reset the environment
+ Flush(true); // This will flush and close the environment
+ Reset();
+ Open(true);
+}
+
bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
{
if (database.IsDummy()) {
@@ -697,7 +741,6 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
if (!fMockDb) {
fs::remove_all(fs::path(strPath) / "database");
}
- g_dbenvs.erase(strPath);
}
}
}
@@ -783,7 +826,7 @@ bool BerkeleyDatabase::Backup(const std::string& strDest)
LogPrintf("copied %s to %s\n", strFile, pathDest.string());
return true;
} catch (const fs::filesystem_error& e) {
- LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), e.what());
+ LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), fsbridge::get_filesystem_error_message(e));
return false;
}
}
@@ -796,6 +839,24 @@ void BerkeleyDatabase::Flush(bool shutdown)
{
if (!IsDummy()) {
env->Flush(shutdown);
- if (shutdown) env = nullptr;
+ if (shutdown) {
+ LOCK(cs_db);
+ g_dbenvs.erase(env->Directory().string());
+ env = nullptr;
+ } else {
+ // TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
+ // first database shutdown when multiple databases are open in the same
+ // environment, should replace raw database `env` pointers with shared or weak
+ // pointers, or else separate the database and environment shutdowns so
+ // environments can be shut down after databases.
+ env->m_fileids.erase(strFile);
+ }
+ }
+}
+
+void BerkeleyDatabase::ReloadDbEnv()
+{
+ if (!IsDummy()) {
+ env->ReloadDbEnv();
}
}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index b078edab7b..e453d441d7 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -11,13 +11,14 @@
#include <serialize.h>
#include <streams.h>
#include <sync.h>
-#include <util.h>
+#include <util/system.h>
#include <version.h>
#include <atomic>
#include <map>
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
#include <db_cxx.h>
@@ -25,6 +26,13 @@
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
static const bool DEFAULT_WALLET_PRIVDB = true;
+struct WalletDatabaseFileId {
+ u_int8_t value[DB_FILE_ID_LEN];
+ bool operator==(const WalletDatabaseFileId& rhs) const;
+};
+
+class BerkeleyDatabase;
+
class BerkeleyEnvironment
{
private:
@@ -37,7 +45,9 @@ private:
public:
std::unique_ptr<DbEnv> dbenv;
std::map<std::string, int> mapFileUseCount;
- std::map<std::string, Db*> mapDb;
+ std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
+ std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
+ std::condition_variable_any m_db_in_use;
BerkeleyEnvironment(const fs::path& env_directory);
~BerkeleyEnvironment();
@@ -46,6 +56,7 @@ public:
void MakeMock();
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; }
/**
@@ -75,6 +86,7 @@ public:
void CheckpointLSN(const std::string& strFile);
void CloseDb(const std::string& strFile);
+ void ReloadDbEnv();
DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
{
@@ -86,6 +98,9 @@ public:
}
};
+/** Return whether a wallet database is currently loaded. */
+bool IsWalletLoaded(const fs::path& wallet_path);
+
/** Get BerkeleyEnvironment and database filename given a wallet path. */
BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
@@ -106,6 +121,8 @@ public:
nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0)
{
env = GetWalletEnv(wallet_path, strFile);
+ auto inserted = env->m_databases.emplace(strFile, std::ref(*this));
+ assert(inserted.second);
if (mock) {
env->Close();
env->Reset();
@@ -113,6 +130,13 @@ public:
}
}
+ ~BerkeleyDatabase() {
+ if (env) {
+ size_t erased = env->m_databases.erase(strFile);
+ assert(erased == 1);
+ }
+ }
+
/** Return object for accessing database at specified path. */
static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
{
@@ -145,11 +169,16 @@ public:
void IncrementUpdateCounter();
+ void ReloadDbEnv();
+
std::atomic<unsigned int> nUpdateCounter;
unsigned int nLastSeen;
unsigned int nLastFlushed;
int64_t nLastWalletUpdate;
+ /** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
+ std::unique_ptr<Db> m_db;
+
private:
/** BerkeleyDB specific */
BerkeleyEnvironment *env;
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 677bf74b5d..7a71aea715 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/validation.h>
+#include <interfaces/chain.h>
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
#include <wallet/fees.h>
@@ -12,13 +13,13 @@
#include <policy/rbf.h>
#include <validation.h> //for mempool access
#include <txmempool.h>
-#include <utilmoneystr.h>
-#include <util.h>
+#include <util/moneystr.h>
+#include <util/system.h>
#include <net.h>
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
-static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(cs_main, wallet->cs_wallet)
+static feebumper::Result PreconditionChecks(interfaces::Chain::Lock& locked_chain, const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet)
{
if (wallet->HasWalletSpend(wtx.GetHash())) {
errors.push_back("Transaction has descendants in the wallet");
@@ -34,7 +35,7 @@ static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWallet
}
}
- if (wtx.GetDepthInMainChain() != 0) {
+ if (wtx.GetDepthInMainChain(locked_chain) != 0) {
errors.push_back("Transaction has been mined, or is conflicted with a mined transaction");
return feebumper::Result::WALLET_ERROR;
}
@@ -64,19 +65,21 @@ namespace feebumper {
bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid)
{
- LOCK2(cs_main, wallet->cs_wallet);
+ auto locked_chain = wallet->chain().lock();
+ LOCK(wallet->cs_wallet);
const CWalletTx* wtx = wallet->GetWalletTx(txid);
if (wtx == nullptr) return false;
std::vector<std::string> errors_dummy;
- feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy);
+ feebumper::Result res = PreconditionChecks(*locked_chain, wallet, *wtx, errors_dummy);
return res == feebumper::Result::OK;
}
Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors,
CAmount& old_fee, CAmount& new_fee, CMutableTransaction& mtx)
{
- LOCK2(cs_main, wallet->cs_wallet);
+ auto locked_chain = wallet->chain().lock();
+ LOCK(wallet->cs_wallet);
errors.clear();
auto it = wallet->mapWallet.find(txid);
if (it == wallet->mapWallet.end()) {
@@ -85,7 +88,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
}
const CWalletTx& wtx = it->second;
- Result result = PreconditionChecks(wallet, wtx, errors);
+ Result result = PreconditionChecks(*locked_chain, wallet, wtx, errors);
if (result != Result::OK) {
return result;
}
@@ -212,13 +215,15 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
}
bool SignTransaction(CWallet* wallet, CMutableTransaction& mtx) {
- LOCK2(cs_main, wallet->cs_wallet);
+ auto locked_chain = wallet->chain().lock();
+ LOCK(wallet->cs_wallet);
return wallet->SignTransaction(mtx);
}
Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<std::string>& errors, uint256& bumped_txid)
{
- LOCK2(cs_main, wallet->cs_wallet);
+ auto locked_chain = wallet->chain().lock();
+ LOCK(wallet->cs_wallet);
if (!errors.empty()) {
return Result::MISC_ERROR;
}
@@ -230,7 +235,7 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
CWalletTx& oldWtx = it->second;
// make sure the transaction still has no descendants and hasn't been mined in the meantime
- Result result = PreconditionChecks(wallet, oldWtx, errors);
+ Result result = PreconditionChecks(*locked_chain, wallet, oldWtx, errors);
if (result != Result::OK) {
return result;
}
diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp
index d620e25f2b..9e2984ff05 100644
--- a/src/wallet/fees.cpp
+++ b/src/wallet/fees.cpp
@@ -7,7 +7,7 @@
#include <policy/policy.h>
#include <txmempool.h>
-#include <util.h>
+#include <util/system.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/wallet.h>
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index b36287ff50..14d811c6cd 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -5,11 +5,12 @@
#include <chainparams.h>
#include <init.h>
+#include <interfaces/chain.h>
#include <net.h>
#include <scheduler.h>
#include <outputtype.h>
-#include <util.h>
-#include <utilmoneystr.h>
+#include <util/system.h>
+#include <util/moneystr.h>
#include <validation.h>
#include <walletinitinterface.h>
#include <wallet/rpcwallet.h>
@@ -19,34 +20,17 @@
class WalletInit : public WalletInitInterface {
public:
+ //! Was the wallet component compiled in.
+ bool HasWalletSupport() const override {return true;}
+
//! Return the wallets help message.
void AddWalletOptions() const override;
//! Wallets parameter interaction
bool ParameterInteraction() const override;
- //! Register wallet RPCs.
- void RegisterRPC(CRPCTable &tableRPC) const override;
-
- //! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
- // This function will perform salvage on the wallet if requested, as long as only one wallet is
- // being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
- bool Verify() const override;
-
- //! Load wallet databases.
- bool Open() const override;
-
- //! Complete startup of wallets.
- void Start(CScheduler& scheduler) const override;
-
- //! Flush all wallets in preparation for shutdown.
- void Flush() const override;
-
- //! Stop all wallets. Wallets will be flushed first.
- void Stop() const override;
-
- //! Close all wallets.
- void Close() const override;
+ //! Add wallets that should be opened to list of init interfaces.
+ void Construct(InitInterfaces& interfaces) const override;
};
const WalletInitInterface& g_wallet_init_interface = WalletInit();
@@ -54,7 +38,7 @@ const WalletInitInterface& g_wallet_init_interface = WalletInit();
void WalletInit::AddWalletOptions() const
{
gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET);
- gArgs.AddArg("-avoidpartialspends", strprintf(_("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)"), DEFAULT_AVOIDPARTIALSPENDS), false, OptionsCategory::WALLET);
+ gArgs.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)", DEFAULT_AVOIDPARTIALSPENDS), false, OptionsCategory::WALLET);
gArgs.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", false, OptionsCategory::WALLET);
gArgs.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", false, OptionsCategory::WALLET);
gArgs.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
@@ -96,7 +80,6 @@ bool WalletInit::ParameterInteraction() const
return true;
}
- gArgs.SoftSetArg("-wallet", "");
const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
@@ -162,38 +145,28 @@ bool WalletInit::ParameterInteraction() const
return true;
}
-void WalletInit::RegisterRPC(CRPCTable &t) const
+bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
{
- if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
- return;
- }
-
- RegisterWalletRPCCommands(t);
-}
-
-bool WalletInit::Verify() const
-{
- if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
- return true;
- }
-
if (gArgs.IsArgSet("-walletdir")) {
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
- if (!fs::exists(wallet_dir)) {
+ boost::system::error_code error;
+ // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
+ fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
+ if (error || !fs::exists(wallet_dir)) {
return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
} else if (!fs::is_directory(wallet_dir)) {
return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
+ // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
} else if (!wallet_dir.is_absolute()) {
return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
}
+ gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
}
LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
uiInterface.InitMessage(_("Verifying wallet(s)..."));
- std::vector<std::string> wallet_files = gArgs.GetArgs("-wallet");
-
// Parameter interaction code should have thrown an error if -salvagewallet
// was enabled with more than wallet file, so the wallet_files size check
// here should have no effect.
@@ -203,15 +176,15 @@ bool WalletInit::Verify() const
std::set<fs::path> wallet_paths;
for (const auto& wallet_file : wallet_files) {
- fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
+ WalletLocation location(wallet_file);
- if (!wallet_paths.insert(wallet_path).second) {
+ if (!wallet_paths.insert(location.GetPath()).second) {
return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
}
std::string error_string;
std::string warning_string;
- bool verify_success = CWallet::Verify(wallet_file, salvage_wallet, error_string, warning_string);
+ bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warning_string);
if (!error_string.empty()) InitError(error_string);
if (!warning_string.empty()) InitWarning(warning_string);
if (!verify_success) return false;
@@ -220,15 +193,20 @@ bool WalletInit::Verify() const
return true;
}
-bool WalletInit::Open() const
+void WalletInit::Construct(InitInterfaces& interfaces) const
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
LogPrintf("Wallet disabled!\n");
- return true;
+ return;
}
+ gArgs.SoftSetArg("-wallet", "");
+ interfaces.chain_clients.emplace_back(interfaces::MakeWalletClient(*interfaces.chain, gArgs.GetArgs("-wallet")));
+}
- for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
- std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir()));
+bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
+{
+ for (const std::string& walletFile : wallet_files) {
+ std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile));
if (!pwallet) {
return false;
}
@@ -238,7 +216,7 @@ bool WalletInit::Open() const
return true;
}
-void WalletInit::Start(CScheduler& scheduler) const
+void StartWallets(CScheduler& scheduler)
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
pwallet->postInitProcess();
@@ -248,21 +226,21 @@ void WalletInit::Start(CScheduler& scheduler) const
scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
}
-void WalletInit::Flush() const
+void FlushWallets()
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
pwallet->Flush(false);
}
}
-void WalletInit::Stop() const
+void StopWallets()
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
pwallet->Flush(true);
}
}
-void WalletInit::Close() const
+void UnloadWallets()
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
RemoveWallet(pwallet);
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index af36d321d5..fa1e209bf2 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -3,21 +3,22 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
+#include <core_io.h>
+#include <interfaces/chain.h>
#include <key_io.h>
+#include <merkleblock.h>
#include <rpc/server.h>
-#include <validation.h>
+#include <rpc/util.h>
#include <script/script.h>
#include <script/standard.h>
#include <sync.h>
-#include <util.h>
-#include <utiltime.h>
+#include <util/system.h>
+#include <util/time.h>
+#include <validation.h>
#include <wallet/wallet.h>
-#include <merkleblock.h>
-#include <core_io.h>
#include <wallet/rpcwallet.h>
-#include <fstream>
#include <stdint.h>
#include <boost/algorithm/string.hpp>
@@ -108,12 +109,18 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "importprivkey \"privkey\" ( \"label\" ) ( rescan )\n"
- "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n"
+ RPCHelpMan{"importprivkey",
+ "\nAdds a private key (as returned by dumpprivkey) to your wallet. Requires a new wallet backup.\n",
+ {
+ {"privkey", RPCArg::Type::STR, false},
+ {"label", RPCArg::Type::STR, true},
+ {"rescan", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"Hint: use importmulti to import more than one private key.\n"
"\nArguments:\n"
"1. \"privkey\" (string, required) The private key (see dumpprivkey)\n"
- "2. \"label\" (string, optional, default=\"\") An optional label\n"
+ "2. \"label\" (string, optional, default=current label if address exists, otherwise \"\") An optional label\n"
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\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 key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
@@ -134,7 +141,8 @@ UniValue importprivkey(const JSONRPCRequest& request)
WalletRescanReserver reserver(pwallet);
bool fRescan = true;
{
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -162,9 +170,14 @@ UniValue importprivkey(const JSONRPCRequest& request)
CKeyID vchAddress = pubkey.GetID();
{
pwallet->MarkDirty();
- // We don't know which corresponding address will be used; label them all
+
+ // We don't know which corresponding address will be used;
+ // label all new addresses, and label existing addresses if a
+ // label was passed.
for (const auto& dest : GetAllDestinationsForKey(pubkey)) {
- pwallet->SetAddressBook(dest, strLabel, "receive");
+ if (!request.params[1].isNull() || pwallet->mapAddressBook.count(dest) == 0) {
+ pwallet->SetAddressBook(dest, strLabel, "receive");
+ }
}
// Don't throw error in case a key is already there
@@ -199,8 +212,9 @@ UniValue abortrescan(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
- "abortrescan\n"
- "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n"
+ RPCHelpMan{"abortrescan",
+ "\nStops current wallet rescan triggered by an RPC call, e.g. by an importprivkey call.\n", {}}
+ .ToString() +
"\nExamples:\n"
"\nImport a private key\n"
+ HelpExampleCli("importprivkey", "\"mykey\"") +
@@ -261,8 +275,15 @@ UniValue importaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw std::runtime_error(
- "importaddress \"address\" ( \"label\" rescan p2sh )\n"
- "\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"
+ 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",
+ {
+ {"address", RPCArg::Type::STR, false},
+ {"label", RPCArg::Type::STR, true},
+ {"rescan", RPCArg::Type::BOOL, true},
+ {"p2sh", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"address\" (string, required) The Bitcoin address (or hex-encoded script)\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
@@ -306,7 +327,8 @@ UniValue importaddress(const JSONRPCRequest& request)
fP2SH = request.params[3].get_bool();
{
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (IsValidDestination(dest)) {
@@ -340,8 +362,13 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
- "importprunedfunds\n"
- "\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"
+ 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, false},
+ {"txoutproof", RPCArg::Type::STR_HEX, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"rawtransaction\" (string, required) A raw transaction in hex funding an already-existing address in wallet\n"
"2. \"txoutproof\" (string, required) The hex output from gettxoutproof that contains the transaction\n"
@@ -363,7 +390,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
unsigned int txnIndex = 0;
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) {
- LOCK(cs_main);
+ auto locked_chain = pwallet->chain().lock();
const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash());
if (!pindex || !chainActive.Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
@@ -383,7 +410,8 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
wtx.nIndex = txnIndex;
wtx.hashBlock = merkleBlock.header.GetHash();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (pwallet->IsMine(*wtx.tx)) {
pwallet->AddToWallet(wtx, false);
@@ -403,8 +431,12 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "removeprunedfunds \"txid\"\n"
- "\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"
+ 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, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txid\" (string, required) The hex-encoded id of the transaction you are deleting\n"
"\nExamples:\n"
@@ -413,10 +445,10 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
+ HelpExampleRpc("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1ce581bebf46446a512166eae7628734ea0a5\"")
);
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
- uint256 hash;
- hash.SetHex(request.params[0].get_str());
+ uint256 hash(ParseHashV(request.params[0], "txid"));
std::vector<uint256> vHash;
vHash.push_back(hash);
std::vector<uint256> vHashOut;
@@ -442,8 +474,14 @@ UniValue importpubkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "importpubkey \"pubkey\" ( \"label\" rescan )\n"
- "\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"
+ 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",
+ {
+ {"pubkey", RPCArg::Type::STR, false},
+ {"label", RPCArg::Type::STR, true},
+ {"rescan", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"pubkey\" (string, required) The hex-encoded public key\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
@@ -485,7 +523,8 @@ UniValue importpubkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
{
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
for (const auto& dest : GetAllDestinationsForKey(pubKey)) {
ImportAddress(pwallet, dest, strLabel);
@@ -513,8 +552,12 @@ UniValue importwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "importwallet \"filename\"\n"
- "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n"
+ RPCHelpMan{"importwallet",
+ "\nImports keys from a wallet dump file (see dumpwallet). Requires a new wallet backup to include imported keys.\n",
+ {
+ {"filename", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"filename\" (string, required) The wallet file\n"
"\nExamples:\n"
@@ -537,12 +580,13 @@ UniValue importwallet(const JSONRPCRequest& request)
int64_t nTimeBegin = 0;
bool fGood = true;
{
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
- std::ifstream file;
- file.open(request.params[0].get_str().c_str(), std::ios::in | std::ios::ate);
+ fsbridge::ifstream file;
+ file.open(request.params[0].get_str(), std::ios::in | std::ios::ate);
if (!file.is_open()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
}
@@ -642,9 +686,13 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "dumpprivkey \"address\"\n"
- "\nReveals the private key corresponding to 'address'.\n"
- "Then the importprivkey can be used with this output\n"
+ RPCHelpMan{"dumpprivkey",
+ "\nReveals the private key corresponding to 'address'.\n"
+ "Then the importprivkey can be used with this output\n",
+ {
+ {"address", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address for the private key\n"
"\nResult:\n"
@@ -655,7 +703,8 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
+ HelpExampleRpc("dumpprivkey", "\"myaddress\"")
);
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -686,11 +735,15 @@ UniValue dumpwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "dumpwallet \"filename\"\n"
- "\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"
- "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n"
+ 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"
+ "only backing up the seed itself, and must be backed up too (e.g. ensure you back up the whole dumpfile).\n",
+ {
+ {"filename", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n"
"\nResult:\n"
@@ -702,7 +755,8 @@ UniValue dumpwallet(const JSONRPCRequest& request)
+ HelpExampleRpc("dumpwallet", "\"test\"")
);
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -718,14 +772,14 @@ UniValue dumpwallet(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are sure this is what you want, move it out of the way first");
}
- std::ofstream file;
- file.open(filepath.string().c_str());
+ fsbridge::ofstream file;
+ file.open(filepath);
if (!file.is_open())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
std::map<CTxDestination, int64_t> mapKeyBirth;
const std::map<CKeyID, int64_t>& mapKeyPool = pwallet->GetAllReserveKeys();
- pwallet->GetKeyBirthTimes(mapKeyBirth);
+ pwallet->GetKeyBirthTimes(*locked_chain, mapKeyBirth);
std::set<CScriptID> scripts = pwallet->GetCScripts();
// TODO: include scripts in GetKeyBirthTimes() output instead of separate
@@ -810,29 +864,24 @@ UniValue dumpwallet(const JSONRPCRequest& request)
static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
try {
- bool success = false;
-
- // Required fields.
+ // First ensure scriptPubKey has either a script or JSON with "address" string
const UniValue& scriptPubKey = data["scriptPubKey"];
-
- // Should have script or JSON with "address".
- if (!(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address")) && !(scriptPubKey.getType() == UniValue::VSTR)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scriptPubKey");
+ bool isScript = scriptPubKey.getType() == UniValue::VSTR;
+ if (!isScript && !(scriptPubKey.getType() == UniValue::VOBJ && scriptPubKey.exists("address"))) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "scriptPubKey must be string with script or JSON with address string");
}
+ const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
// Optional fields.
const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
+ const std::string& witness_script_hex = data.exists("witnessscript") ? data["witnessscript"].get_str() : "";
const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
- const std::string& label = data.exists("label") && !internal ? data["label"].get_str() : "";
+ const std::string& label = data.exists("label") ? data["label"].get_str() : "";
- bool isScript = scriptPubKey.getType() == UniValue::VSTR;
- bool isP2SH = strRedeemScript.length() > 0;
- const std::string& output = isScript ? scriptPubKey.get_str() : scriptPubKey["address"].get_str();
-
- // Parse the output.
+ // Generate the script and destination for the scriptPubKey provided
CScript script;
CTxDestination dest;
@@ -849,44 +898,45 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
std::vector<unsigned char> vData(ParseHex(output));
script = CScript(vData.begin(), vData.end());
+ if (!ExtractDestination(script, dest) && !internal) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set to true for nonstandard scriptPubKey imports.");
+ }
}
// Watchonly and private keys
if (watchOnly && keys.size()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between watchonly and keys");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Watch-only addresses should not include private keys");
}
- // Internal + Label
+ // Internal addresses should not have a label
if (internal && data.exists("label")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between internal and label");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal addresses should not have a label");
}
- // Not having Internal + Script
- if (!internal && isScript) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set for hex scriptPubKey");
+ // Force users to provide the witness script in its field rather than redeemscript
+ if (!strRedeemScript.empty() && script.IsPayToWitnessScriptHash()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "P2WSH addresses have an empty redeemscript. Please provide the witnessscript instead.");
}
- // Keys / PubKeys size check.
- if (!isP2SH && (keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey
- throw JSONRPCError(RPC_INVALID_PARAMETER, "More than private key given for one address");
- }
-
- // Invalid P2SH redeemScript
- if (isP2SH && !IsHex(strRedeemScript)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script");
- }
-
- // Process. //
+ CScript scriptpubkey_script = script;
+ CTxDestination scriptpubkey_dest = dest;
+ bool allow_p2wpkh = true;
// P2SH
- if (isP2SH) {
+ if (!strRedeemScript.empty() && script.IsPayToScriptHash()) {
+ // Check the redeemScript is valid
+ if (!IsHex(strRedeemScript)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid redeem script: must be hex string");
+ }
+
// Import redeem script.
std::vector<unsigned char> vData(ParseHex(strRedeemScript));
CScript redeemScript = CScript(vData.begin(), vData.end());
+ CScriptID redeem_id(redeemScript);
- // Invalid P2SH address
- if (!script.IsPayToScriptHash()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid P2SH address / script");
+ // Check that the redeemScript and scriptPubKey match
+ if (GetScriptForDestination(redeem_id) != script) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The redeemScript does not match the scriptPubKey");
}
pwallet->MarkDirty();
@@ -895,114 +945,83 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
- CScriptID redeem_id(redeemScript);
if (!pwallet->HaveCScript(redeem_id) && !pwallet->AddCScript(redeemScript)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
- CScript redeemDestination = GetScriptForDestination(redeem_id);
+ // Now set script to the redeemScript so we parse the inner script as P2WSH or P2WPKH below
+ script = redeemScript;
+ ExtractDestination(script, dest);
+ }
- if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ // (P2SH-)P2WSH
+ if (!witness_script_hex.empty() && script.IsPayToWitnessScriptHash()) {
+ if (!IsHex(witness_script_hex)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid witness script: must be hex string");
}
- pwallet->MarkDirty();
+ // Generate the scripts
+ std::vector<unsigned char> witness_script_parsed(ParseHex(witness_script_hex));
+ CScript witness_script = CScript(witness_script_parsed.begin(), witness_script_parsed.end());
+ CScriptID witness_id(witness_script);
- if (!pwallet->AddWatchOnly(redeemDestination, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ // Check that the witnessScript and scriptPubKey match
+ if (GetScriptForDestination(WitnessV0ScriptHash(witness_script)) != script) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The witnessScript does not match the scriptPubKey or redeemScript");
}
- // add to address book or update label
- if (IsValidDestination(dest)) {
- pwallet->SetAddressBook(dest, label, "receive");
+ // Add the witness script as watch only only if it is not for P2SH-P2WSH
+ if (!scriptpubkey_script.IsPayToScriptHash() && !pwallet->AddWatchOnly(witness_script, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
- // Import private keys.
- if (keys.size()) {
- for (size_t i = 0; i < keys.size(); i++) {
- const std::string& privkey = keys[i].get_str();
-
- CKey key = DecodeSecret(privkey);
-
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
-
- CPubKey pubkey = key.GetPubKey();
- assert(key.VerifyPubKey(pubkey));
-
- CKeyID vchAddress = pubkey.GetID();
- pwallet->MarkDirty();
- pwallet->SetAddressBook(vchAddress, label, "receive");
-
- if (pwallet->HaveKey(vchAddress)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
- }
-
- pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
+ if (!pwallet->HaveCScript(witness_id) && !pwallet->AddCScript(witness_script)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2wsh witnessScript to wallet");
+ }
- if (!pwallet->AddKeyPubKey(key, pubkey)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
- }
+ // Now set script to the witnessScript so we parse the inner script as P2PK or P2PKH below
+ script = witness_script;
+ ExtractDestination(script, dest);
+ allow_p2wpkh = false; // P2WPKH cannot be embedded in P2WSH
+ }
- pwallet->UpdateTimeFirstKey(timestamp);
- }
+ // (P2SH-)P2PK/P2PKH/P2WPKH
+ if (dest.type() == typeid(CKeyID) || dest.type() == typeid(WitnessV0KeyHash)) {
+ if (!allow_p2wpkh && dest.type() == typeid(WitnessV0KeyHash)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "P2WPKH cannot be embedded in P2WSH");
}
-
- success = true;
- } else {
- // Import public keys.
- if (pubKeys.size() && keys.size() == 0) {
+ if (keys.size() > 1 || pubKeys.size() > 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "More than one key given for one single-key address");
+ }
+ CPubKey pubkey;
+ if (keys.size()) {
+ pubkey = DecodeSecret(keys[0].get_str()).GetPubKey();
+ }
+ if (pubKeys.size()) {
const std::string& strPubKey = pubKeys[0].get_str();
-
if (!IsHex(strPubKey)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
}
-
- std::vector<unsigned char> vData(ParseHex(strPubKey));
- CPubKey pubKey(vData.begin(), vData.end());
-
- if (!pubKey.IsFullyValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
+ std::vector<unsigned char> vData(ParseHex(pubKeys[0].get_str()));
+ CPubKey pubkey_temp(vData.begin(), vData.end());
+ if (pubkey.size() && pubkey_temp != pubkey) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key does not match public key for address");
}
-
- CTxDestination pubkey_dest = pubKey.GetID();
-
- // Consistency check.
- if (!isScript && !(pubkey_dest == dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
- }
-
- // Consistency check.
- if (isScript) {
- CTxDestination destination;
-
- if (ExtractDestination(script, destination)) {
- if (!(destination == pubkey_dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
- }
- }
- }
-
- CScript pubKeyScript = GetScriptForDestination(pubkey_dest);
-
- if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
- }
-
- pwallet->MarkDirty();
-
- if (!pwallet->AddWatchOnly(pubKeyScript, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ pubkey = pubkey_temp;
+ }
+ if (pubkey.size() > 0) {
+ if (!pubkey.IsFullyValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
}
- // add to address book or update label
- if (IsValidDestination(pubkey_dest)) {
- pwallet->SetAddressBook(pubkey_dest, label, "receive");
+ // Check the key corresponds to the destination given
+ std::vector<CTxDestination> destinations = GetAllDestinationsForKey(pubkey);
+ if (std::find(destinations.begin(), destinations.end(), dest) == destinations.end()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Key does not match address destination");
}
- // TODO Is this necessary?
- CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey);
+ // This is necessary to force the wallet to import the pubKey
+ CScript scriptRawPubKey = GetScriptForRawPubKey(pubkey);
if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
@@ -1013,86 +1032,62 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
-
- success = true;
}
+ }
- // Import private keys.
- if (keys.size()) {
- const std::string& strPrivkey = keys[0].get_str();
-
- // Checks.
- CKey key = DecodeSecret(strPrivkey);
-
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
-
- CPubKey pubKey = key.GetPubKey();
- assert(key.VerifyPubKey(pubKey));
-
- CTxDestination pubkey_dest = pubKey.GetID();
-
- // Consistency check.
- if (!isScript && !(pubkey_dest == dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
- }
+ // Import the address
+ if (::IsMine(*pwallet, scriptpubkey_script) == ISMINE_SPENDABLE) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
+ }
- // Consistency check.
- if (isScript) {
- CTxDestination destination;
+ pwallet->MarkDirty();
- if (ExtractDestination(script, destination)) {
- if (!(destination == pubkey_dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed");
- }
- }
- }
-
- CKeyID vchAddress = pubKey.GetID();
- pwallet->MarkDirty();
- pwallet->SetAddressBook(vchAddress, label, "receive");
+ if (!pwallet->AddWatchOnly(scriptpubkey_script, timestamp)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
+ }
- if (pwallet->HaveKey(vchAddress)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
- }
+ if (!watchOnly && !pwallet->HaveCScript(CScriptID(scriptpubkey_script)) && !pwallet->AddCScript(scriptpubkey_script)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding scriptPubKey script to wallet");
+ }
- pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
+ // if not internal add to address book or update label
+ if (!internal) {
+ assert(IsValidDestination(scriptpubkey_dest));
+ pwallet->SetAddressBook(scriptpubkey_dest, label, "receive");
+ }
- if (!pwallet->AddKeyPubKey(key, pubKey)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
- }
+ // Import private keys.
+ for (size_t i = 0; i < keys.size(); i++) {
+ const std::string& strPrivkey = keys[i].get_str();
- pwallet->UpdateTimeFirstKey(timestamp);
+ // Checks.
+ CKey key = DecodeSecret(strPrivkey);
- success = true;
+ if (!key.IsValid()) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
}
- // Import scriptPubKey only.
- if (pubKeys.size() == 0 && keys.size() == 0) {
- if (::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script");
- }
+ CPubKey pubKey = key.GetPubKey();
+ assert(key.VerifyPubKey(pubKey));
- pwallet->MarkDirty();
+ CKeyID vchAddress = pubKey.GetID();
+ pwallet->MarkDirty();
- if (!pwallet->AddWatchOnly(script, timestamp)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
- }
+ if (pwallet->HaveKey(vchAddress)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key");
+ }
- if (scriptPubKey.getType() == UniValue::VOBJ) {
- // add to address book or update label
- if (IsValidDestination(dest)) {
- pwallet->SetAddressBook(dest, label, "receive");
- }
- }
+ pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
- success = true;
+ if (!pwallet->AddKeyPubKey(key, pubKey)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
}
+
+ pwallet->UpdateTimeFirstKey(timestamp);
}
UniValue result = UniValue(UniValue::VOBJ);
- result.pushKV("success", UniValue(success));
+ result.pushKV("success", UniValue(true));
return result;
} catch (const UniValue& e) {
UniValue result = UniValue(UniValue::VOBJ);
@@ -1129,11 +1124,35 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
return NullUniValue;
}
- // clang-format off
if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
throw std::runtime_error(
- "importmulti \"requests\" ( \"options\" )\n\n"
- "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n"
+ RPCHelpMan{"importmulti",
+ "\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n",
+ {
+ {"requests", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {
+ {"scriptPubKey", RPCArg::Type::STR, false},
+ {"timestamp", RPCArg::Type::NUM, false},
+ {"redeemscript", RPCArg::Type::STR, true},
+ {"witnessscript", RPCArg::Type::STR, true},
+ {"internal", RPCArg::Type::BOOL, true},
+ {"watchonly", RPCArg::Type::BOOL, true},
+ {"label", RPCArg::Type::STR, true},
+ },
+ },
+ false},
+ },
+ false, "\"requests\""},
+ {"options", RPCArg::Type::OBJ,
+ {
+ {"rescan", RPCArg::Type::BOOL, true},
+ },
+ true, "\"options\""},
+ }}
+ .ToString() +
"Arguments:\n"
"1. requests (array, required) Data to be imported\n"
" [ (array of json objects)\n"
@@ -1145,10 +1164,11 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
" \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
" 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
" creation time of all keys being imported by the importmulti call will be scanned.\n"
- " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
+ " \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey\n"
+ " \"witnessscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey\n"
" \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
" \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
- " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n"
+ " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments aka change\n"
" \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n"
" \"label\": <label> , (string, optional, default: '') Label to assign to the address, only allowed with internal=false\n"
" }\n"
@@ -1168,7 +1188,6 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"\nResponse is an array with the same size as the input that has the execution result :\n"
" [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n");
- // clang-format on
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
@@ -1195,7 +1214,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
int64_t nLowestTimestamp = 0;
UniValue response(UniValue::VARR);
{
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
// Verify all timestamps are present before importing any keys.
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index e419f8d164..b4c21631ab 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -8,6 +8,8 @@
#include <consensus/validation.h>
#include <core_io.h>
#include <httpserver.h>
+#include <init.h>
+#include <interfaces/chain.h>
#include <validation.h>
#include <key_io.h>
#include <net.h>
@@ -20,11 +22,12 @@
#include <rpc/rawtransaction.h>
#include <rpc/server.h>
#include <rpc/util.h>
+#include <script/descriptor.h>
#include <script/sign.h>
#include <shutdown.h>
#include <timedata.h>
-#include <util.h>
-#include <utilmoneystr.h>
+#include <util/system.h>
+#include <util/moneystr.h>
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
#include <wallet/rpcwallet.h>
@@ -89,9 +92,9 @@ void EnsureWalletIsUnlocked(CWallet * const pwallet)
}
}
-static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx, UniValue& entry)
{
- int confirms = wtx.GetDepthInMainChain();
+ int confirms = wtx.GetDepthInMainChain(locked_chain);
entry.pushKV("confirmations", confirms);
if (wtx.IsCoinBase())
entry.pushKV("generated", true);
@@ -101,7 +104,7 @@ static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) EXCLUSIVE_LOCK
entry.pushKV("blockindex", wtx.nIndex);
entry.pushKV("blocktime", LookupBlockIndex(wtx.hashBlock)->GetBlockTime());
} else {
- entry.pushKV("trusted", wtx.IsTrusted());
+ entry.pushKV("trusted", wtx.IsTrusted(locked_chain));
}
uint256 hash = wtx.GetHash();
entry.pushKV("txid", hash.GetHex());
@@ -147,10 +150,15 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
- "getnewaddress ( \"label\" \"address_type\" )\n"
- "\nReturns a new Bitcoin address for receiving payments.\n"
- "If 'label' is specified, it is added to the address book \n"
- "so payments received with the address will be associated with 'label'.\n"
+ RPCHelpMan{"getnewaddress",
+ "\nReturns a new Bitcoin address for receiving payments.\n"
+ "If 'label' is specified, it is added to the address book \n"
+ "so payments received with the address will be associated with 'label'.\n",
+ {
+ {"label", RPCArg::Type::STR, true},
+ {"address_type", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"label\" (string, optional) The label name for the address to be linked to. If not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name.\n"
"2. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -addresstype.\n"
@@ -207,9 +215,13 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getrawchangeaddress ( \"address_type\" )\n"
- "\nReturns a new Bitcoin address, for receiving change.\n"
- "This is for use with raw transactions, NOT normal use.\n"
+ RPCHelpMan{"getrawchangeaddress",
+ "\nReturns a new Bitcoin address, for receiving change.\n"
+ "This is for use with raw transactions, NOT normal use.\n",
+ {
+ {"address_type", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
"\nResult:\n"
@@ -261,8 +273,13 @@ static UniValue setlabel(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
- "setlabel \"address\" \"label\"\n"
- "\nSets the label associated with the given address.\n"
+ RPCHelpMan{"setlabel",
+ "\nSets the label associated with the given address.\n",
+ {
+ {"address", RPCArg::Type::STR, false},
+ {"label", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to be associated with a label.\n"
"2. \"label\" (string, required) The label to assign to the address.\n"
@@ -290,7 +307,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
}
-static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
+static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
{
CAmount curBalance = pwallet->GetBalance();
@@ -317,7 +334,7 @@ static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
CTransactionRef tx;
- if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
+ if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
@@ -341,9 +358,20 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
throw std::runtime_error(
- "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount replaceable conf_target \"estimate_mode\")\n"
- "\nSend an amount to a given address.\n"
- + HelpRequiringPassphrase(pwallet) +
+ RPCHelpMan{"sendtoaddress",
+ "\nSend an amount to a given address.\n",
+ {
+ {"address", RPCArg::Type::STR, false},
+ {"amount", RPCArg::Type::AMOUNT, false},
+ {"comment", RPCArg::Type::STR, true},
+ {"comment_to", RPCArg::Type::STR, true},
+ {"subtractfeefromamount", RPCArg::Type::BOOL, true},
+ {"replaceable", RPCArg::Type::BOOL, true},
+ {"conf_target", RPCArg::Type::NUM, true},
+ {"estimate_mode", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
+ HelpRequiringPassphrase(pwallet) +
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to send to.\n"
"2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n"
@@ -373,7 +401,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (!IsValidDestination(dest)) {
@@ -415,7 +444,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
+ CTransactionRef tx = SendMoney(*locked_chain, pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
return tx->GetHash().GetHex();
}
@@ -430,10 +459,12 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "listaddressgroupings\n"
- "\nLists groups of addresses which have had their common ownership\n"
- "made public by common use as inputs or as the resulting change\n"
- "in past transactions\n"
+ RPCHelpMan{"listaddressgroupings",
+ "\nLists groups of addresses which have had their common ownership\n"
+ "made public by common use as inputs or as the resulting change\n"
+ "in past transactions\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"[\n"
" [\n"
@@ -455,10 +486,11 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR);
- std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances();
+ std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances(*locked_chain);
for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) {
UniValue jsonGrouping(UniValue::VARR);
for (const CTxDestination& address : grouping)
@@ -489,9 +521,14 @@ static UniValue signmessage(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
- "signmessage \"address\" \"message\"\n"
- "\nSign a message with the private key of an address"
- + HelpRequiringPassphrase(pwallet) + "\n"
+ RPCHelpMan{"signmessage",
+ "\nSign a message with the private key of an address",
+ {
+ {"address", RPCArg::Type::STR, false},
+ {"message", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
+ HelpRequiringPassphrase(pwallet) + "\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to use for the private key.\n"
"2. \"message\" (string, required) The message to create a signature of.\n"
@@ -504,11 +541,12 @@ static UniValue signmessage(const JSONRPCRequest& request)
+ HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
"\nVerify the signature\n"
+ HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
- "\nAs json rpc\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")
);
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -552,8 +590,13 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "getreceivedbyaddress \"address\" ( minconf )\n"
- "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n"
+ RPCHelpMan{"getreceivedbyaddress",
+ "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
+ {
+ {"address", RPCArg::Type::STR, false},
+ {"minconf", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address for transactions.\n"
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
@@ -566,7 +609,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") +
"\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")
);
@@ -574,7 +617,9 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
// Bitcoin address
CTxDestination dest = DecodeDestination(request.params[0].get_str());
@@ -600,7 +645,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
for (const CTxOut& txout : wtx.tx->vout)
if (txout.scriptPubKey == scriptPubKey)
- if (wtx.GetDepthInMainChain() >= nMinDepth)
+ if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth)
nAmount += txout.nValue;
}
@@ -619,8 +664,13 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "getreceivedbylabel \"label\" ( minconf )\n"
- "\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n"
+ RPCHelpMan{"getreceivedbylabel",
+ "\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
+ {
+ {"label", RPCArg::Type::STR, false},
+ {"minconf", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"label\" (string, required) The selected label, may be the default label using \"\".\n"
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
@@ -633,7 +683,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
"\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
);
@@ -641,7 +691,9 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
// Minimum confirmations
int nMinDepth = 1;
@@ -663,7 +715,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) {
- if (wtx.GetDepthInMainChain() >= nMinDepth)
+ if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth)
nAmount += txout.nValue;
}
}
@@ -684,10 +736,16 @@ static UniValue getbalance(const JSONRPCRequest& request)
if (request.fHelp || (request.params.size() > 3 ))
throw std::runtime_error(
- "getbalance ( \"(dummy)\" minconf include_watchonly )\n"
- "\nReturns the total available balance.\n"
- "The available balance is what the wallet considers currently spendable, and is\n"
- "thus affected by options which limit spendability such as -spendzeroconfchange.\n"
+ RPCHelpMan{"getbalance",
+ "\nReturns the total available balance.\n"
+ "The available balance is what the wallet considers currently spendable, and is\n"
+ "thus affected by options which limit spendability such as -spendzeroconfchange.\n",
+ {
+ {"dummy", RPCArg::Type::STR, true},
+ {"minconf", RPCArg::Type::NUM, true},
+ {"include_watchonly", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\".\n"
"2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n"
@@ -699,7 +757,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
+ HelpExampleCli("getbalance", "") +
"\nThe total amount in the wallet at least 6 blocks confirmed\n"
+ HelpExampleCli("getbalance", "\"*\" 6") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getbalance", "\"*\", 6")
);
@@ -707,7 +765,8 @@ static UniValue getbalance(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
const UniValue& dummy_value = request.params[0];
if (!dummy_value.isNull() && dummy_value.get_str() != "*") {
@@ -738,14 +797,16 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
- "getunconfirmedbalance\n"
- "Returns the server's total unconfirmed balance\n");
+ RPCHelpMan{"getunconfirmedbalance",
+ "Returns the server's total unconfirmed balance\n", {}}
+ .ToString());
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
return ValueFromAmount(pwallet->GetUnconfirmedBalance());
}
@@ -762,9 +823,28 @@ static UniValue sendmany(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
throw std::runtime_error(
- "sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
- "\nSend multiple times. Amounts are double-precision floating point numbers.\n"
- + HelpRequiringPassphrase(pwallet) + "\n"
+ RPCHelpMan{"sendmany",
+ "\nSend multiple times. Amounts are double-precision floating point numbers.\n",
+ {
+ {"dummy", RPCArg::Type::STR, false, "\"\""},
+ {"amounts", RPCArg::Type::OBJ,
+ {
+ {"address", RPCArg::Type::AMOUNT, false},
+ },
+ false},
+ {"minconf", RPCArg::Type::NUM, true},
+ {"comment", RPCArg::Type::STR, true},
+ {"subtractfeefrom", RPCArg::Type::ARR,
+ {
+ {"address", RPCArg::Type::STR, true},
+ },
+ true},
+ {"replaceable", RPCArg::Type::BOOL, true},
+ {"conf_target", RPCArg::Type::NUM, true},
+ {"estimate_mode", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
+ HelpRequiringPassphrase(pwallet) + "\n"
"\nArguments:\n"
"1. \"dummy\" (string, required) Must be set to \"\" for backwards compatibility.\n"
"2. \"amounts\" (string, required) A json object with addresses and amounts\n"
@@ -798,7 +878,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
+ HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") +
"\nSend two amounts to two different addresses, subtract fee from amount:\n"
+ HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
);
@@ -806,7 +886,8 @@ static UniValue sendmany(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (pwallet->GetBroadcastTransactions() && !g_connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -892,7 +973,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
int nChangePosRet = -1;
std::string strFailReason;
CTransactionRef tx;
- bool fCreated = pwallet->CreateTransaction(vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control);
+ bool fCreated = pwallet->CreateTransaction(*locked_chain, vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control);
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
CValidationState state;
@@ -939,13 +1020,14 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
"\nExamples:\n"
"\nAdd a multisig address from 2 addresses\n"
+ HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"")
;
throw std::runtime_error(msg);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
std::string label;
if (!request.params[2].isNull())
@@ -982,131 +1064,6 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
return result;
}
-class Witnessifier : public boost::static_visitor<bool>
-{
-public:
- CWallet * const pwallet;
- CTxDestination result;
- bool already_witness;
-
- explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {}
-
- bool operator()(const CKeyID &keyID) {
- if (pwallet) {
- CScript basescript = GetScriptForDestination(keyID);
- CScript witscript = GetScriptForWitness(basescript);
- if (!IsSolvable(*pwallet, witscript)) {
- return false;
- }
- return ExtractDestination(witscript, result);
- }
- return false;
- }
-
- bool operator()(const CScriptID &scriptID) {
- CScript subscript;
- if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
- int witnessversion;
- std::vector<unsigned char> witprog;
- if (subscript.IsWitnessProgram(witnessversion, witprog)) {
- ExtractDestination(subscript, result);
- already_witness = true;
- return true;
- }
- CScript witscript = GetScriptForWitness(subscript);
- if (!IsSolvable(*pwallet, witscript)) {
- return false;
- }
- return ExtractDestination(witscript, result);
- }
- return false;
- }
-
- bool operator()(const WitnessV0KeyHash& id)
- {
- already_witness = true;
- result = id;
- return true;
- }
-
- bool operator()(const WitnessV0ScriptHash& id)
- {
- already_witness = true;
- result = id;
- return true;
- }
-
- template<typename T>
- bool operator()(const T& dest) { return false; }
-};
-
-static UniValue addwitnessaddress(const JSONRPCRequest& request)
-{
- std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
- CWallet* const pwallet = wallet.get();
-
- if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
- return NullUniValue;
- }
-
- if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
- {
- std::string msg = "addwitnessaddress \"address\" ( p2sh )\n"
- "\nDEPRECATED: set the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n"
- "Add a witness address for a script (with pubkey or redeemscript known). Requires a new wallet backup.\n"
- "It returns the witness script.\n"
-
- "\nArguments:\n"
- "1. \"address\" (string, required) An address known to the wallet\n"
- "2. p2sh (bool, optional, default=true) Embed inside P2SH\n"
-
- "\nResult:\n"
- "\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n"
- "}\n"
- ;
- throw std::runtime_error(msg);
- }
-
- if (!IsDeprecatedRPCEnabled("addwitnessaddress")) {
- throw JSONRPCError(RPC_METHOD_DEPRECATED, "addwitnessaddress is deprecated and will be fully removed in v0.17. "
- "To use addwitnessaddress in v0.16, restart bitcoind with -deprecatedrpc=addwitnessaddress.\n"
- "Projects should transition to using the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n");
- }
-
- CTxDestination dest = DecodeDestination(request.params[0].get_str());
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
- }
-
- bool p2sh = true;
- if (!request.params[1].isNull()) {
- p2sh = request.params[1].get_bool();
- }
-
- Witnessifier w(pwallet);
- bool ret = boost::apply_visitor(w, dest);
- if (!ret) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
- }
-
- CScript witprogram = GetScriptForDestination(w.result);
-
- if (p2sh) {
- w.result = CScriptID(witprogram);
- }
-
- if (w.already_witness) {
- if (!(dest == w.result)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types");
- }
- } else {
- pwallet->AddCScript(witprogram); // Implicit for single-key now, but necessary for multisig and for compatibility with older software
- pwallet->SetAddressBook(w.result, "", "receive");
- }
-
- return EncodeDestination(w.result);
-}
-
struct tallyitem
{
CAmount nAmount;
@@ -1121,8 +1078,10 @@ struct tallyitem
}
};
-static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
+ LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
+
// Minimum confirmations
int nMinDepth = 1;
if (!params[0].isNull())
@@ -1156,7 +1115,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue;
- int nDepth = wtx.GetDepthInMainChain();
+ int nDepth = wtx.GetDepthInMainChain(locked_chain);
if (nDepth < nMinDepth)
continue;
@@ -1276,8 +1235,15 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 4)
throw std::runtime_error(
- "listreceivedbyaddress ( minconf include_empty include_watchonly address_filter )\n"
- "\nList balances by receiving address.\n"
+ RPCHelpMan{"listreceivedbyaddress",
+ "\nList balances by receiving address.\n",
+ {
+ {"minconf", RPCArg::Type::NUM, true},
+ {"include_empty", RPCArg::Type::BOOL, true},
+ {"include_watchonly", RPCArg::Type::BOOL, true},
+ {"address_filter", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
"2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n"
@@ -1310,9 +1276,10 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
- return ListReceived(pwallet, request.params, false);
+ return ListReceived(*locked_chain, pwallet, request.params, false);
}
static UniValue listreceivedbylabel(const JSONRPCRequest& request)
@@ -1326,8 +1293,14 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 3)
throw std::runtime_error(
- "listreceivedbylabel ( minconf include_empty include_watchonly)\n"
- "\nList received transactions by label.\n"
+ RPCHelpMan{"listreceivedbylabel",
+ "\nList received transactions by label.\n",
+ {
+ {"minconf", RPCArg::Type::NUM, true},
+ {"include_empty", RPCArg::Type::BOOL, true},
+ {"include_watchonly", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
"2. include_empty (bool, optional, default=false) Whether to include labels that haven't received any payments.\n"
@@ -1354,9 +1327,10 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
- return ListReceived(pwallet, request.params, true);
+ return ListReceived(*locked_chain, pwallet, request.params, true);
}
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
@@ -1369,25 +1343,26 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
/**
* List transactions based on the given criteria.
*
- * @param pwallet The wallet.
- * @param wtx The wallet transaction.
- * @param nMinDepth The minimum confirmation depth.
- * @param fLong Whether to include the JSON version of the transaction.
- * @param ret The UniValue into which the result is stored.
- * @param filter The "is mine" filter bool.
+ * @param pwallet The wallet.
+ * @param wtx The wallet transaction.
+ * @param nMinDepth The minimum confirmation depth.
+ * @param fLong Whether to include the JSON version of the transaction.
+ * @param ret The UniValue into which the result is stored.
+ * @param filter_ismine The "is mine" filter flags.
+ * @param filter_label Optional label string to filter incoming transactions.
*/
-static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label)
{
CAmount nFee;
std::list<COutputEntry> listReceived;
std::list<COutputEntry> listSent;
- wtx.GetAmounts(listReceived, listSent, nFee, filter);
+ wtx.GetAmounts(listReceived, listSent, nFee, filter_ismine);
bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
// Sent
- if ((!listSent.empty() || nFee != 0))
+ if (!filter_label)
{
for (const COutputEntry& s : listSent)
{
@@ -1404,14 +1379,14 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee));
if (fLong)
- WalletTxToJSON(wtx, entry);
+ WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
entry.pushKV("abandoned", wtx.isAbandoned());
ret.push_back(entry);
}
}
// Received
- if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
+ if (listReceived.size() > 0 && wtx.GetDepthInMainChain(locked_chain) >= nMinDepth)
{
for (const COutputEntry& r : listReceived)
{
@@ -1419,6 +1394,9 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
if (pwallet->mapAddressBook.count(r.destination)) {
label = pwallet->mapAddressBook[r.destination].name;
}
+ if (filter_label && label != *filter_label) {
+ continue;
+ }
UniValue entry(UniValue::VOBJ);
if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true);
@@ -1426,9 +1404,9 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase())
{
- if (wtx.GetDepthInMainChain() < 1)
+ if (wtx.GetDepthInMainChain(locked_chain) < 1)
entry.pushKV("category", "orphan");
- else if (wtx.IsImmatureCoinBase())
+ else if (wtx.IsImmatureCoinBase(locked_chain))
entry.pushKV("category", "immature");
else
entry.pushKV("category", "generate");
@@ -1443,7 +1421,7 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
}
entry.pushKV("vout", r.vout);
if (fLong)
- WalletTxToJSON(wtx, entry);
+ WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
ret.push_back(entry);
}
}
@@ -1460,10 +1438,19 @@ UniValue listtransactions(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 4)
throw std::runtime_error(
- "listtransactions (dummy count skip include_watchonly)\n"
- "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n"
+ RPCHelpMan{"listtransactions",
+ "\nIf a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n"
+ "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n",
+ {
+ {"label", RPCArg::Type::STR, true},
+ {"count", RPCArg::Type::NUM, true},
+ {"skip", RPCArg::Type::NUM, true},
+ {"include_watchonly", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
- "1. \"dummy\" (string, optional) If set, should be \"*\" for backwards compatibility.\n"
+ "1. \"label\" (string, optional) If set, should be a valid label name to return only incoming transactions\n"
+ " with the specified label, or \"*\" to disable filtering and return all transactions.\n"
"2. count (numeric, optional, default=10) The number of transactions to return\n"
"3. skip (numeric, optional, default=0) The number of transactions to skip\n"
"4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n"
@@ -1500,7 +1487,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
+ HelpExampleCli("listtransactions", "") +
"\nList transactions 100 to 120\n"
+ HelpExampleCli("listtransactions", "\"*\" 20 100") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listtransactions", "\"*\", 20, 100")
);
@@ -1508,8 +1495,12 @@ UniValue listtransactions(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
+ const std::string* filter_label = nullptr;
if (!request.params[0].isNull() && request.params[0].get_str() != "*") {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"*\"");
+ filter_label = &request.params[0].get_str();
+ if (filter_label->empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Label argument must be a valid label name or \"*\".");
+ }
}
int nCount = 10;
if (!request.params[1].isNull())
@@ -1530,7 +1521,8 @@ UniValue listtransactions(const JSONRPCRequest& request)
UniValue ret(UniValue::VARR);
{
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
const CWallet::TxItems & txOrdered = pwallet->wtxOrdered;
@@ -1538,7 +1530,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second;
- ListTransactions(pwallet, *pwtx, 0, true, ret, filter);
+ ListTransactions(*locked_chain, pwallet, *pwtx, 0, true, ret, filter, filter_label);
if ((int)ret.size() >= (nCount+nFrom)) break;
}
}
@@ -1580,10 +1572,17 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 4)
throw std::runtime_error(
- "listsinceblock ( \"blockhash\" target_confirmations include_watchonly include_removed )\n"
- "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
- "If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
- "Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n"
+ RPCHelpMan{"listsinceblock",
+ "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted.\n"
+ "If \"blockhash\" is no longer a part of the main chain, transactions from the fork point onward are included.\n"
+ "Additionally, if include_removed is set, transactions affecting the wallet which were removed are returned in the \"removed\" array.\n",
+ {
+ {"blockhash", RPCArg::Type::STR, true},
+ {"target_confirmations", RPCArg::Type::NUM, true},
+ {"include_watchonly", RPCArg::Type::BOOL, true},
+ {"include_removed", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
"2. target_confirmations: (numeric, optional, default=1) Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value\n"
@@ -1630,7 +1629,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain.
const CBlockIndex* paltindex = nullptr; // Block index of the specified block, even if it's in a deactivated chain.
@@ -1638,9 +1638,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
isminefilter filter = ISMINE_SPENDABLE;
if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
- uint256 blockId;
+ uint256 blockId(ParseHashV(request.params[0], "blockhash"));
- blockId.SetHex(request.params[0].get_str());
paltindex = pindex = LookupBlockIndex(blockId);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
@@ -1674,8 +1673,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
CWalletTx tx = pairWtx.second;
- if (depth == -1 || tx.GetDepthInMainChain() < depth) {
- ListTransactions(pwallet, tx, 0, true, transactions, filter);
+ if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) {
+ ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
}
}
@@ -1692,7 +1691,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
if (it != pwallet->mapWallet.end()) {
// We want all transactions regardless of confirmation count to appear here,
// even negative confirmation ones, hence the big negative.
- ListTransactions(pwallet, it->second, -100000000, true, removed, filter);
+ ListTransactions(*locked_chain, pwallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
}
}
paltindex = paltindex->pprev;
@@ -1720,8 +1719,13 @@ static UniValue gettransaction(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "gettransaction \"txid\" ( include_watchonly )\n"
- "\nGet detailed information about in-wallet transaction <txid>\n"
+ RPCHelpMan{"gettransaction",
+ "\nGet detailed information about in-wallet transaction <txid>\n",
+ {
+ {"txid", RPCArg::Type::STR, false},
+ {"include_watchonly", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. \"include_watchonly\" (bool, optional, default=false) Whether to include watch-only addresses in balance calculation and details[]\n"
@@ -1766,10 +1770,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
- uint256 hash;
- hash.SetHex(request.params[0].get_str());
+ uint256 hash(ParseHashV(request.params[0], "txid"));
isminefilter filter = ISMINE_SPENDABLE;
if(!request.params[1].isNull())
@@ -1783,7 +1787,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
}
const CWalletTx& wtx = it->second;
- CAmount nCredit = wtx.GetCredit(filter);
+ CAmount nCredit = wtx.GetCredit(*locked_chain, filter);
CAmount nDebit = wtx.GetDebit(filter);
CAmount nNet = nCredit - nDebit;
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
@@ -1792,10 +1796,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
if (wtx.IsFromMe(filter))
entry.pushKV("fee", ValueFromAmount(nFee));
- WalletTxToJSON(wtx, entry);
+ WalletTxToJSON(pwallet->chain(), *locked_chain, wtx, entry);
UniValue details(UniValue::VARR);
- ListTransactions(pwallet, wtx, 0, false, details, filter);
+ ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
@@ -1815,12 +1819,16 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "abandontransaction \"txid\"\n"
- "\nMark in-wallet transaction <txid> as abandoned\n"
- "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
- "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
- "It only works on transactions which are not included in a block and are not currently in the mempool.\n"
- "It has no effect on transactions which are already abandoned.\n"
+ RPCHelpMan{"abandontransaction",
+ "\nMark in-wallet transaction <txid> as abandoned\n"
+ "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
+ "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
+ "It only works on transactions which are not included in a block and are not currently in the mempool.\n"
+ "It has no effect on transactions which are already abandoned.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"\nResult:\n"
@@ -1834,15 +1842,15 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
- uint256 hash;
- hash.SetHex(request.params[0].get_str());
+ uint256 hash(ParseHashV(request.params[0], "txid"));
if (!pwallet->mapWallet.count(hash)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
}
- if (!pwallet->AbandonTransaction(hash)) {
+ if (!pwallet->AbandonTransaction(*locked_chain, hash)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment");
}
@@ -1861,8 +1869,12 @@ static UniValue backupwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "backupwallet \"destination\"\n"
- "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n"
+ RPCHelpMan{"backupwallet",
+ "\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
+ {
+ {"destination", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"destination\" (string) The destination directory or file\n"
"\nExamples:\n"
@@ -1874,7 +1886,8 @@ static UniValue backupwallet(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
std::string strDest = request.params[0].get_str();
if (!pwallet->BackupWallet(strDest)) {
@@ -1896,9 +1909,13 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "keypoolrefill ( newsize )\n"
- "\nFills the keypool."
- + HelpRequiringPassphrase(pwallet) + "\n"
+ RPCHelpMan{"keypoolrefill",
+ "\nFills the keypool.",
+ {
+ {"newsize", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
+ HelpRequiringPassphrase(pwallet) + "\n"
"\nArguments\n"
"1. newsize (numeric, optional, default=100) The new keypool size\n"
"\nExamples:\n"
@@ -1910,7 +1927,8 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool
unsigned int kpSize = 0;
@@ -1931,13 +1949,6 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
}
-static void LockWallet(CWallet* pWallet)
-{
- LOCK(pWallet->cs_wallet);
- pWallet->nRelockTime = 0;
- pWallet->Lock();
-}
-
static UniValue walletpassphrase(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -1949,9 +1960,14 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
- "walletpassphrase \"passphrase\" timeout\n"
- "\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
- "This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
+ RPCHelpMan{"walletpassphrase",
+ "\nStores the wallet decryption key in memory for 'timeout' seconds.\n"
+ "This is needed prior to performing transactions related to private keys such as sending bitcoins\n",
+ {
+ {"passphrase", RPCArg::Type::STR, false},
+ {"timeout", RPCArg::Type::NUM, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"passphrase\" (string, required) The wallet passphrase\n"
"2. timeout (numeric, required) The time to keep the decryption key in seconds; capped at 100000000 (~3 years).\n"
@@ -1963,12 +1979,13 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
"\nLock the wallet again (before 60 seconds)\n"
+ HelpExampleCli("walletlock", "") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
@@ -1993,21 +2010,29 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
nSleepTime = MAX_SLEEP_TIME;
}
- if (strWalletPass.length() > 0)
- {
- if (!pwallet->Unlock(strWalletPass)) {
- throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
- }
+ if (strWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
+
+ if (!pwallet->Unlock(strWalletPass)) {
+ throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
}
- else
- throw std::runtime_error(
- "walletpassphrase <passphrase> <timeout>\n"
- "Stores the wallet decryption key in memory for <timeout> seconds.");
pwallet->TopUpKeyPool();
pwallet->nRelockTime = GetTime() + nSleepTime;
- RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), std::bind(LockWallet, pwallet), nSleepTime);
+
+ // Keep a weak pointer to the wallet so that it is possible to unload the
+ // wallet before the following callback is called. If a valid shared pointer
+ // is acquired in the callback then the wallet is still loaded.
+ std::weak_ptr<CWallet> weak_wallet = wallet;
+ RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] {
+ if (auto shared_wallet = weak_wallet.lock()) {
+ LOCK(shared_wallet->cs_wallet);
+ shared_wallet->Lock();
+ shared_wallet->nRelockTime = 0;
+ }
+ }, nSleepTime);
return NullUniValue;
}
@@ -2024,8 +2049,13 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
- "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n"
- "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n"
+ RPCHelpMan{"walletpassphrasechange",
+ "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
+ {
+ {"oldpassphrase", RPCArg::Type::STR, false},
+ {"newpassphrase", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"oldpassphrase\" (string) The current passphrase\n"
"2. \"newpassphrase\" (string) The new passphrase\n"
@@ -2035,7 +2065,8 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
@@ -2051,10 +2082,9 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
strNewWalletPass.reserve(100);
strNewWalletPass = request.params[1].get_str().c_str();
- if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
- throw std::runtime_error(
- "walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
- "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
+ if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
@@ -2075,10 +2105,12 @@ static UniValue walletlock(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
- "walletlock\n"
- "\nRemoves the wallet encryption key from memory, locking the wallet.\n"
- "After calling this method, you will need to call walletpassphrase again\n"
- "before being able to call any methods which require the wallet to be unlocked.\n"
+ RPCHelpMan{"walletlock",
+ "\nRemoves the wallet encryption key from memory, locking the wallet.\n"
+ "After calling this method, you will need to call walletpassphrase again\n"
+ "before being able to call any methods which require the wallet to be unlocked.\n",
+ {}}
+ .ToString() +
"\nExamples:\n"
"\nSet the passphrase for 2 minutes to perform a transaction\n"
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
@@ -2086,12 +2118,13 @@ static UniValue walletlock(const JSONRPCRequest& request)
+ HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") +
"\nClear the passphrase since we are done before 2 minutes is up\n"
+ HelpExampleCli("walletlock", "") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletlock", "")
);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
@@ -2115,13 +2148,16 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "encryptwallet \"passphrase\"\n"
- "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
- "After this, any calls that interact with private keys such as sending or signing \n"
- "will require the passphrase to be set prior the making these calls.\n"
- "Use the walletpassphrase call for this, and then walletlock call.\n"
- "If the wallet is already encrypted, use the walletpassphrasechange call.\n"
- "Note that this will shutdown the server.\n"
+ RPCHelpMan{"encryptwallet",
+ "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n"
+ "After this, any calls that interact with private keys such as sending or signing \n"
+ "will require the passphrase to be set prior the making these calls.\n"
+ "Use the walletpassphrase call for this, and then walletlock call.\n"
+ "If the wallet is already encrypted, use the walletpassphrasechange call.\n",
+ {
+ {"passphrase", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n"
"\nExamples:\n"
@@ -2133,12 +2169,13 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
+ HelpExampleCli("signmessage", "\"address\" \"test message\"") +
"\nNow lock the wallet again by removing the passphrase\n"
+ HelpExampleCli("walletlock", "") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
@@ -2150,20 +2187,15 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
strWalletPass.reserve(100);
strWalletPass = request.params[0].get_str().c_str();
- if (strWalletPass.length() < 1)
- throw std::runtime_error(
- "encryptwallet <passphrase>\n"
- "Encrypts the wallet with <passphrase>.");
+ if (strWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
if (!pwallet->EncryptWallet(strWalletPass)) {
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
}
- // BDB seems to have a bad habit of writing old data into
- // slack space in .dat files; that is bad if the old data is
- // unencrypted private keys. So:
- StartShutdown();
- return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
+ return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
}
static UniValue lockunspent(const JSONRPCRequest& request)
@@ -2177,14 +2209,28 @@ static UniValue lockunspent(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "lockunspent unlock ([{\"txid\":\"txid\",\"vout\":n},...])\n"
- "\nUpdates list of temporarily unspendable outputs.\n"
- "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"
- "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"
+ RPCHelpMan{"lockunspent",
+ "\nUpdates list of temporarily unspendable outputs.\n"
+ "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"
+ "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",
+ {
+ {"unlock", RPCArg::Type::BOOL, false},
+ {"transactions", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"vout", RPCArg::Type::NUM, false},
+ },
+ true},
+ },
+ true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. unlock (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n"
"2. \"transactions\" (string, optional) A json array of objects. Each object the txid (string) vout (numeric)\n"
@@ -2208,7 +2254,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
);
@@ -2216,7 +2262,8 @@ static UniValue lockunspent(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
@@ -2246,17 +2293,13 @@ static UniValue lockunspent(const JSONRPCRequest& request)
{"vout", UniValueType(UniValue::VNUM)},
});
- const std::string& txid = find_value(o, "txid").get_str();
- if (!IsHex(txid)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
- }
-
+ const uint256 txid(ParseHashO(o, "txid"));
const int nOutput = find_value(o, "vout").get_int();
if (nOutput < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
}
- const COutPoint outpt(uint256S(txid), nOutput);
+ const COutPoint outpt(txid, nOutput);
const auto it = pwallet->mapWallet.find(outpt.hash);
if (it == pwallet->mapWallet.end()) {
@@ -2269,7 +2312,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
}
- if (pwallet->IsSpent(outpt.hash, outpt.n)) {
+ if (pwallet->IsSpent(*locked_chain, outpt.hash, outpt.n)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
}
@@ -2306,9 +2349,11 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 0)
throw std::runtime_error(
- "listlockunspent\n"
- "\nReturns list of temporarily unspendable outputs.\n"
- "See the lockunspent call to lock and unlock transactions for spending.\n"
+ RPCHelpMan{"listlockunspent",
+ "\nReturns list of temporarily unspendable outputs.\n"
+ "See the lockunspent call to lock and unlock transactions for spending.\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"[\n"
" {\n"
@@ -2326,11 +2371,12 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlockunspent", "")
);
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
std::vector<COutPoint> vOutpts;
pwallet->ListLockedCoins(vOutpts);
@@ -2359,8 +2405,12 @@ static UniValue settxfee(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) {
throw std::runtime_error(
- "settxfee amount\n"
- "\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n"
+ RPCHelpMan{"settxfee",
+ "\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n",
+ {
+ {"amount", RPCArg::Type::NUM, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. amount (numeric or string, required) The transaction fee in " + CURRENCY_UNIT + "/kB\n"
"\nResult\n"
@@ -2371,7 +2421,8 @@ static UniValue settxfee(const JSONRPCRequest& request)
);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
CAmount nAmount = AmountFromValue(request.params[0]);
CFeeRate tx_fee_rate(nAmount, 1000);
@@ -2398,8 +2449,9 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "getwalletinfo\n"
- "Returns an object containing various wallet state info.\n"
+ RPCHelpMan{"getwalletinfo",
+ "Returns an object containing various wallet state info.\n", {}}
+ .ToString() +
"\nResult:\n"
"{\n"
" \"walletname\": xxxxx, (string) the wallet name\n"
@@ -2426,7 +2478,8 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
UniValue obj(UniValue::VOBJ);
@@ -2455,13 +2508,48 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
return obj;
}
+static UniValue listwalletdir(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0) {
+ throw std::runtime_error(
+ RPCHelpMan{"listwalletdir",
+ "Returns a list of wallets in the wallet directory.\n", {}}
+ .ToString() +
+ "{\n"
+ " \"wallets\" : [ (json array of objects)\n"
+ " {\n"
+ " \"name\" : \"name\" (string) The wallet name\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("listwalletdir", "")
+ + HelpExampleRpc("listwalletdir", "")
+ );
+ }
+
+ UniValue wallets(UniValue::VARR);
+ for (const auto& path : ListWalletDir()) {
+ UniValue wallet(UniValue::VOBJ);
+ wallet.pushKV("name", path.string());
+ wallets.push_back(wallet);
+ }
+
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("wallets", wallets);
+ return result;
+}
+
static UniValue listwallets(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "listwallets\n"
- "Returns a list of currently loaded wallets.\n"
- "For full information on the wallet, use \"getwalletinfo\"\n"
+ RPCHelpMan{"listwallets",
+ "Returns a list of currently loaded wallets.\n"
+ "For full information on the wallet, use \"getwalletinfo\"\n",
+ {}}
+ .ToString() +
"\nResult:\n"
"[ (json array of strings)\n"
" \"walletname\" (string) the wallet name\n"
@@ -2491,10 +2579,14 @@ static UniValue loadwallet(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "loadwallet \"filename\"\n"
- "\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, upgradewallet, rescan, etc).\n"
+ 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, upgradewallet, rescan, etc).\n",
+ {
+ {"filename", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"filename\" (string, required) The wallet directory or .dat file.\n"
"\nResult:\n"
@@ -2506,26 +2598,26 @@ static UniValue loadwallet(const JSONRPCRequest& request)
+ HelpExampleCli("loadwallet", "\"test.dat\"")
+ HelpExampleRpc("loadwallet", "\"test.dat\"")
);
- std::string wallet_file = request.params[0].get_str();
+
+ WalletLocation location(request.params[0].get_str());
std::string error;
- fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
- if (fs::symlink_status(wallet_path).type() == fs::file_not_found) {
- throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + wallet_file + " not found.");
- } else if (fs::is_directory(wallet_path)) {
+ 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 = wallet_path / "wallet.dat";
+ 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 " + wallet_file + " does not contain a wallet.dat file.");
+ throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + location.GetName() + " does not contain a wallet.dat file.");
}
}
std::string warning;
- if (!CWallet::Verify(wallet_file, false, error, warning)) {
+ if (!CWallet::Verify(*g_rpc_interfaces->chain, location, false, error, warning)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
}
- std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_file, fs::absolute(wallet_file, GetWalletDir()));
+ std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location);
if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed.");
}
@@ -2544,8 +2636,13 @@ static UniValue createwallet(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "createwallet \"wallet_name\" ( disable_private_keys )\n"
- "\nCreates and loads a new wallet.\n"
+ RPCHelpMan{"createwallet",
+ "\nCreates and loads a new wallet.\n",
+ {
+ {"wallet_name", RPCArg::Type::STR, false},
+ {"disable_private_keys", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"wallet_name\" (string, required) The name for the new wallet. If this is a path, the wallet will be created at the path location.\n"
"2. disable_private_keys (boolean, optional, default: false) Disable the possibility of private keys (only watchonlys are possible in this mode).\n"
@@ -2559,7 +2656,6 @@ static UniValue createwallet(const JSONRPCRequest& request)
+ HelpExampleRpc("createwallet", "\"testwallet\"")
);
}
- std::string wallet_name = request.params[0].get_str();
std::string error;
std::string warning;
@@ -2568,17 +2664,17 @@ static UniValue createwallet(const JSONRPCRequest& request)
disable_privatekeys = request.params[1].get_bool();
}
- fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir());
- if (fs::symlink_status(wallet_path).type() != fs::file_not_found) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + wallet_name + " already exists.");
+ WalletLocation location(request.params[0].get_str());
+ if (location.Exists()) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + location.GetName() + " already exists.");
}
// Wallet::Verify will check if we're trying to create a wallet with a duplication name.
- if (!CWallet::Verify(wallet_name, false, error, warning)) {
+ if (!CWallet::Verify(*g_rpc_interfaces->chain, location, false, error, warning)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
}
- std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir()), (disable_privatekeys ? (uint64_t)WALLET_FLAG_DISABLE_PRIVATE_KEYS : 0));
+ std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location, (disable_privatekeys ? (uint64_t)WALLET_FLAG_DISABLE_PRIVATE_KEYS : 0));
if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
}
@@ -2597,9 +2693,13 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
- "unloadwallet ( \"wallet_name\" )\n"
- "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
- "Specifying the wallet name on a wallet endpoint is invalid."
+ RPCHelpMan{"unloadwallet",
+ "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
+ "Specifying the wallet name on a wallet endpoint is invalid.",
+ {
+ {"wallet_name", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"wallet_name\" (string, optional) The name of the wallet to unload.\n"
"\nExamples:\n"
@@ -2653,9 +2753,11 @@ static UniValue resendwallettransactions(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
- "resendwallettransactions\n"
- "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n"
- "Intended only for testing; the wallet code periodically re-broadcasts\n"
+ RPCHelpMan{"resendwallettransactions",
+ "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n"
+ "Intended only for testing; the wallet code periodically re-broadcasts\n",
+ {}}
+ .ToString() +
"automatically.\n"
"Returns an RPC error if -walletbroadcast is set to false.\n"
"Returns array of transaction ids that were re-broadcast.\n"
@@ -2664,13 +2766,14 @@ static UniValue resendwallettransactions(const JSONRPCRequest& request)
if (!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (!pwallet->GetBroadcastTransactions()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast");
}
- std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(GetTime(), g_connman.get());
+ std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(*locked_chain, GetTime(), g_connman.get());
UniValue result(UniValue::VARR);
for (const uint256& txid : txids)
{
@@ -2690,10 +2793,29 @@ static UniValue listunspent(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 5)
throw std::runtime_error(
- "listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] [query_options])\n"
- "\nReturns array of unspent transaction outputs\n"
- "with between minconf and maxconf (inclusive) confirmations.\n"
- "Optionally filter to only include txouts paid to specified addresses.\n"
+ RPCHelpMan{"listunspent",
+ "\nReturns array of unspent transaction outputs\n"
+ "with between minconf and maxconf (inclusive) confirmations.\n"
+ "Optionally filter to only include txouts paid to specified addresses.\n",
+ {
+ {"minconf", RPCArg::Type::NUM, true},
+ {"maxconf", RPCArg::Type::NUM, true},
+ {"addresses", RPCArg::Type::ARR,
+ {
+ {"address", RPCArg::Type::STR, true},
+ },
+ true},
+ {"include_unsafe", RPCArg::Type::BOOL, true},
+ {"query_options", RPCArg::Type::OBJ,
+ {
+ {"minimumAmount", RPCArg::Type::AMOUNT, true},
+ {"maximumAmount", RPCArg::Type::AMOUNT, true},
+ {"maximumCount", RPCArg::Type::NUM, true},
+ {"minimumSumAmount", RPCArg::Type::AMOUNT, true},
+ },
+ true, "query_options"},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n"
"2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n"
@@ -2724,6 +2846,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
" \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n"
" \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n"
" \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n"
+ " \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n"
" \"safe\" : xxx (bool) Whether this output is considered safe to spend. Unconfirmed transactions\n"
" from outside keys and unconfirmed replacement transactions are considered unsafe\n"
" and are not eligible for spending by fundrawtransaction and sendtoaddress.\n"
@@ -2801,8 +2924,9 @@ static UniValue listunspent(const JSONRPCRequest& request)
UniValue results(UniValue::VARR);
std::vector<COutput> vecOutputs;
{
- LOCK2(cs_main, pwallet->cs_wallet);
- pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
+ pwallet->AvailableCoins(*locked_chain, vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
}
LOCK(pwallet->cs_wallet);
@@ -2841,6 +2965,10 @@ static UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("confirmations", out.nDepth);
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
+ if (out.fSolvable) {
+ auto descriptor = InferDescriptor(scriptPubKey, *pwallet);
+ entry.pushKV("desc", descriptor->ToString());
+ }
entry.pushKV("safe", out.fSafe);
results.push_back(entry);
}
@@ -2975,17 +3103,40 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "fundrawtransaction \"hexstring\" ( options iswitness )\n"
- "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
- "This will not modify existing inputs, and will add at most one change output to the outputs.\n"
- "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
- "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
- "The inputs added will not be signed, use signrawtransaction for that.\n"
- "Note that all existing inputs must have their previous output transaction be in the wallet.\n"
- "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
- "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
- "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
- "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
+ RPCHelpMan{"fundrawtransaction",
+ "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
+ "This will not modify existing inputs, and will add at most one change output to the outputs.\n"
+ "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
+ "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
+ "The inputs added will not be signed, use signrawtransaction for that.\n"
+ "Note that all existing inputs must have their previous output transaction be in the wallet.\n"
+ "Note that all inputs selected must be of standard form and P2SH scripts must be\n"
+ "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
+ "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
+ "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n",
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, false},
+ {"options", RPCArg::Type::OBJ,
+ {
+ {"changeAddress", RPCArg::Type::STR, true},
+ {"changePosition", RPCArg::Type::NUM, true},
+ {"change_type", RPCArg::Type::STR, true},
+ {"includeWatching", RPCArg::Type::BOOL, true},
+ {"lockUnspents", RPCArg::Type::BOOL, true},
+ {"feeRate", RPCArg::Type::AMOUNT, true},
+ {"subtractFeeFromOutputs", RPCArg::Type::ARR,
+ {
+ {"vout_index", RPCArg::Type::NUM, true},
+ },
+ true},
+ {"replaceable", RPCArg::Type::BOOL, true},
+ {"conf_target", RPCArg::Type::NUM, true},
+ {"estimate_mode", RPCArg::Type::STR, true},
+ },
+ true, "options"},
+ {"iswitness", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
"2. options (object, optional)\n"
@@ -3064,11 +3215,29 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "signrawtransactionwithwallet \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n"
- "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
- "The second optional argument (may be null) is an array of previous transaction outputs that\n"
- "this transaction depends on but may not yet be in the block chain.\n"
- + HelpRequiringPassphrase(pwallet) + "\n"
+ RPCHelpMan{"signrawtransactionwithwallet",
+ "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
+ "The second optional argument (may be null) is an array of previous transaction outputs that\n"
+ "this transaction depends on but may not yet be in the block chain.\n",
+ {
+ {"hexstring", RPCArg::Type::STR, false},
+ {"prevtxs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"vout", RPCArg::Type::NUM, false},
+ {"scriptPubKey", RPCArg::Type::STR_HEX, false},
+ {"redeemScript", RPCArg::Type::STR_HEX, false},
+ {"amount", RPCArg::Type::AMOUNT, false},
+ },
+ false},
+ },
+ true},
+ {"sighashtype", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
+ HelpRequiringPassphrase(pwallet) + "\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The transaction hex string\n"
@@ -3120,8 +3289,11 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
}
// Sign the transaction
- LOCK2(cs_main, pwallet->cs_wallet);
- return SignTransaction(mtx, request.params[1], pwallet, false, request.params[2]);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
+ EnsureWalletIsUnlocked(pwallet);
+
+ return SignTransaction(pwallet->chain(), mtx, request.params[1], pwallet, false, request.params[2]);
}
static UniValue bumpfee(const JSONRPCRequest& request)
@@ -3135,18 +3307,30 @@ static UniValue bumpfee(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "bumpfee \"txid\" ( options ) \n"
- "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
- "An opt-in RBF transaction with the given txid must be in the wallet.\n"
- "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n"
- "If the change output is not big enough to cover the increased fee, the command will currently fail\n"
- "instead of adding new inputs to compensate. (A future implementation could improve this.)\n"
- "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
- "By default, the new fee will be calculated automatically using estimatesmartfee.\n"
- "The user can specify a confirmation target for estimatesmartfee.\n"
- "Alternatively, the user can specify totalFee, or use RPC settxfee to set a higher fee rate.\n"
- "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
- "returned by getnetworkinfo) to enter the node's mempool.\n"
+ RPCHelpMan{"bumpfee",
+ "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
+ "An opt-in RBF transaction with the given txid must be in the wallet.\n"
+ "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n"
+ "If the change output is not big enough to cover the increased fee, the command will currently fail\n"
+ "instead of adding new inputs to compensate. (A future implementation could improve this.)\n"
+ "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
+ "By default, the new fee will be calculated automatically using estimatesmartfee.\n"
+ "The user can specify a confirmation target for estimatesmartfee.\n"
+ "Alternatively, the user can specify totalFee, or use RPC settxfee to set a higher fee rate.\n"
+ "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
+ "returned by getnetworkinfo) to enter the node's mempool.\n",
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"options", RPCArg::Type::OBJ,
+ {
+ {"confTarget", RPCArg::Type::NUM, true},
+ {"totalFee", RPCArg::Type::AMOUNT, true},
+ {"replaceable", RPCArg::Type::BOOL, true},
+ {"estimate_mode", RPCArg::Type::STR, true},
+ },
+ true, "options"},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. txid (string, required) The txid to be bumped\n"
"2. options (object, optional)\n"
@@ -3181,8 +3365,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
}
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
- uint256 hash;
- hash.SetHex(request.params[0].get_str());
+ uint256 hash(ParseHashV(request.params[0], "txid"));
// optional parameters
CAmount totalFee = 0;
@@ -3224,7 +3407,8 @@ static UniValue bumpfee(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -3287,8 +3471,13 @@ UniValue generate(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
throw std::runtime_error(
- "generate nblocks ( maxtries )\n"
- "\nMine up to nblocks blocks immediately (before the RPC call returns) to an address in the wallet.\n"
+ RPCHelpMan{"generate",
+ "\nMine up to nblocks blocks immediately (before the RPC call returns) to an address in the wallet.\n",
+ {
+ {"nblocks", RPCArg::Type::NUM, false},
+ {"maxtries", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. nblocks (numeric, required) How many blocks are generated immediately.\n"
"2. maxtries (numeric, optional) How many iterations to try (default = 1000000).\n"
@@ -3300,6 +3489,12 @@ UniValue generate(const JSONRPCRequest& request)
);
}
+ if (!IsDeprecatedRPCEnabled("generate")) {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "The wallet generate rpc method is deprecated and will be fully removed in v0.19. "
+ "To use generate in v0.18, restart bitcoind with -deprecatedrpc=generate.\n"
+ "Clients should transition to using the node rpc method generatetoaddress\n");
+ }
+
int num_generate = request.params[0].get_int();
uint64_t max_tries = 1000000;
if (!request.params[1].isNull()) {
@@ -3333,8 +3528,13 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
- "rescanblockchain (\"start_height\") (\"stop_height\")\n"
- "\nRescan the local blockchain for wallet related transactions.\n"
+ RPCHelpMan{"rescanblockchain",
+ "\nRescan the local blockchain for wallet related transactions.\n",
+ {
+ {"start_height", RPCArg::Type::NUM, true},
+ {"stop_height", RPCArg::Type::NUM, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"start_height\" (numeric, optional) block height where the rescan should start\n"
"2. \"stop_height\" (numeric, optional) the last block height that should be scanned\n"
@@ -3358,7 +3558,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
CBlockIndex *pindexStop = nullptr;
CBlockIndex *pChainTip = nullptr;
{
- LOCK(cs_main);
+ auto locked_chain = pwallet->chain().lock();
pindexStart = chainActive.Genesis();
pChainTip = chainActive.Tip();
@@ -3382,7 +3582,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
// We can't rescan beyond non-pruned blocks, stop and throw an error
if (fPruneMode) {
- LOCK(cs_main);
+ auto locked_chain = pwallet->chain().lock();
CBlockIndex *block = pindexStop ? pindexStop : pChainTip;
while (block && block->nHeight >= pindexStart->nHeight) {
if (!(block->nStatus & BLOCK_HAVE_DATA)) {
@@ -3539,18 +3739,26 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "getaddressinfo \"address\"\n"
- "\nReturn information about the given bitcoin address. Some information requires the address\n"
- "to be in the wallet.\n"
+ RPCHelpMan{"getaddressinfo",
+ "\nReturn information about the given bitcoin address. Some information requires the address\n"
+ "to be in the wallet.\n",
+ {
+ {"address", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to get the information of.\n"
"\nResult:\n"
"{\n"
" \"address\" : \"address\", (string) The bitcoin address validated\n"
- " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n"
+ " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n"
" \"ismine\" : true|false, (boolean) If the address is yours or not\n"
+ " \"solvable\" : true|false, (boolean) If the address is solvable by the wallet\n"
" \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
+ " \"solvable\" : true|false, (boolean) Whether we know how to spend coins sent to this address, ignoring the possible lack of private keys\n"
+ " \"desc\" : \"desc\", (string, optional) A descriptor for spending coins sent to this address (only when solvable)\n"
" \"isscript\" : true|false, (boolean) If the key is a script\n"
+ " \"ischange\" : true|false, (boolean) If the address was used for change output\n"
" \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
" \"witness_version\" : version (numeric, optional) The version number of the witness program\n"
" \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n"
@@ -3602,12 +3810,19 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
isminetype mine = IsMine(*pwallet, dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
+ bool solvable = IsSolvable(*pwallet, scriptPubKey);
+ ret.pushKV("solvable", solvable);
+ if (solvable) {
+ ret.pushKV("desc", InferDescriptor(scriptPubKey, *pwallet)->ToString());
+ }
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
+ ret.pushKV("solvable", IsSolvable(*pwallet, scriptPubKey));
UniValue detail = DescribeWalletAddress(pwallet, dest);
ret.pushKVs(detail);
if (pwallet->mapAddressBook.count(dest)) {
ret.pushKV("label", pwallet->mapAddressBook[dest].name);
}
+ ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
const CKeyMetadata* meta = nullptr;
CKeyID key_id = GetKeyForDestination(*pwallet, dest);
if (!key_id.IsNull()) {
@@ -3655,8 +3870,12 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "getaddressesbylabel \"label\"\n"
- "\nReturns the list of addresses assigned the specified label.\n"
+ RPCHelpMan{"getaddressesbylabel",
+ "\nReturns the list of addresses assigned the specified label.\n",
+ {
+ {"label", RPCArg::Type::STR, false},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"label\" (string, required) The label.\n"
"\nResult:\n"
@@ -3700,8 +3919,12 @@ static UniValue listlabels(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "listlabels ( \"purpose\" )\n"
- "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n"
+ RPCHelpMan{"listlabels",
+ "\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
+ {
+ {"purpose", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"purpose\" (string, optional) Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument.\n"
"\nResult:\n"
@@ -3716,7 +3939,7 @@ static UniValue listlabels(const JSONRPCRequest& request)
+ HelpExampleCli("listlabels", "receive") +
"\nList labels that have sending addresses\n"
+ HelpExampleCli("listlabels", "send") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlabels", "receive")
);
@@ -3754,10 +3977,15 @@ UniValue sethdseed(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
- "sethdseed ( \"newkeypool\" \"seed\" )\n"
- "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
- "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
- "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n"
+ RPCHelpMan{"sethdseed",
+ "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
+ "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
+ "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n",
+ {
+ {"newkeypool", RPCArg::Type::BOOL, true},
+ {"seed", RPCArg::Type::STR, true},
+ }}
+ .ToString()
+ HelpRequiringPassphrase(pwallet) +
"\nArguments:\n"
"1. \"newkeypool\" (boolean, optional, default=true) Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
@@ -3778,7 +4006,8 @@ UniValue sethdseed(const JSONRPCRequest& request)
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
// Do not do anything to non-HD wallets
if (!pwallet->IsHDEnabled()) {
@@ -3827,24 +4056,34 @@ void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubK
hd_keypaths.emplace(vchPubKey, std::move(info));
}
-bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const CTransaction* txConst, int sighash_type, bool sign, bool bip32derivs)
+bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs)
{
LOCK(pwallet->cs_wallet);
// Get all of the previous transactions
bool complete = true;
- for (unsigned int i = 0; i < txConst->vin.size(); ++i) {
- const CTxIn& txin = txConst->vin[i];
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i);
- // If we don't know about this input, skip it and let someone else deal with it
- const uint256& txhash = txin.prevout.hash;
- const auto it = pwallet->mapWallet.find(txhash);
- if (it != pwallet->mapWallet.end()) {
- const CWalletTx& wtx = it->second;
- CTxOut utxo = wtx.tx->vout[txin.prevout.n];
- // Update both UTXOs from the wallet.
- input.non_witness_utxo = wtx.tx;
- input.witness_utxo = utxo;
+ if (PSBTInputSigned(input)) {
+ continue;
+ }
+
+ // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
+ if (!input.IsSane()) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "PSBT input is not sane.");
+ }
+
+ // If we have no utxo, grab it from the wallet.
+ if (!input.non_witness_utxo && input.witness_utxo.IsNull()) {
+ const uint256& txhash = txin.prevout.hash;
+ const auto it = pwallet->mapWallet.find(txhash);
+ if (it != pwallet->mapWallet.end()) {
+ const CWalletTx& wtx = it->second;
+ // We only need the non_witness_utxo, which is a superset of the witness_utxo.
+ // The signing code will switch to the smaller witness_utxo if this is ok.
+ input.non_witness_utxo = wtx.tx;
+ }
}
// Get the Sighash type
@@ -3852,12 +4091,12 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Specified Sighash and sighash in PSBT do not match.");
}
- complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), *psbtx.tx, input, i, sighash_type);
+ complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), psbtx, i, sighash_type);
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
- for (unsigned int i = 0; i < txConst->vout.size(); ++i) {
- const CTxOut& out = txConst->vout.at(i);
+ for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
+ const CTxOut& out = psbtx.tx->vout.at(i);
PSBTOutput& psbt_out = psbtx.outputs.at(i);
// Fill a SignatureData with output info
@@ -3882,10 +4121,17 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 4)
throw std::runtime_error(
- "walletprocesspsbt \"psbt\" ( sign \"sighashtype\" bip32derivs )\n"
- "\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
- "that we can sign for.\n"
- + HelpRequiringPassphrase(pwallet) + "\n"
+ RPCHelpMan{"walletprocesspsbt",
+ "\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
+ "that we can sign for.\n",
+ {
+ {"psbt", RPCArg::Type::STR, false},
+ {"sign", RPCArg::Type::BOOL, true},
+ {"sighashtype", RPCArg::Type::STR, true},
+ {"bip32derivs", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
+ HelpRequiringPassphrase(pwallet) + "\n"
"\nArguments:\n"
"1. \"psbt\" (string, required) The transaction base64 string\n"
@@ -3922,19 +4168,15 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
// Get the sighash type
int nHashType = ParseSighashString(request.params[2]);
- // Use CTransaction for the constant parts of the
- // transaction to avoid rehashing.
- const CTransaction txConst(*psbtx.tx);
-
// Fill transaction with our data and also sign
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
bool bip32derivs = request.params[3].isNull() ? false : request.params[3].get_bool();
- bool complete = FillPSBT(pwallet, psbtx, &txConst, nHashType, sign, bip32derivs);
+ bool complete = FillPSBT(pwallet, psbtx, nHashType, sign, bip32derivs);
UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- result.pushKV("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()));
+ result.pushKV("psbt", EncodeBase64(ssTx.str()));
result.pushKV("complete", complete);
return result;
@@ -3951,9 +4193,57 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
throw std::runtime_error(
- "walletcreatefundedpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable ) ( options bip32derivs )\n"
- "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
- "Implements the Creator and Updater roles.\n"
+ RPCHelpMan{"walletcreatefundedpsbt",
+ "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
+ "Implements the Creator and Updater roles.\n",
+ {
+ {"inputs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"vout", RPCArg::Type::NUM, false},
+ {"sequence", RPCArg::Type::NUM, false},
+ },
+ false},
+ },
+ false},
+ {"outputs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"address", RPCArg::Type::AMOUNT, true},
+ },
+ true},
+ {"", RPCArg::Type::OBJ,
+ {
+ {"data", RPCArg::Type::STR_HEX, true},
+ },
+ true},
+ },
+ false},
+ {"locktime", RPCArg::Type::NUM, true},
+ {"options", RPCArg::Type::OBJ,
+ {
+ {"changeAddress", RPCArg::Type::STR_HEX, true},
+ {"changePosition", RPCArg::Type::NUM, true},
+ {"change_type", RPCArg::Type::STR, true},
+ {"includeWatching", RPCArg::Type::BOOL, true},
+ {"lockUnspents", RPCArg::Type::BOOL, true},
+ {"feeRate", RPCArg::Type::AMOUNT, true},
+ {"subtractFeeFromOutputs", RPCArg::Type::ARR,
+ {
+ {"int", RPCArg::Type::NUM, true},
+ },
+ true},
+ {"replaceable", RPCArg::Type::BOOL, true},
+ {"conf_target", RPCArg::Type::NUM, true},
+ {"estimate_mode", RPCArg::Type::STR, true},
+ },
+ true, "options"},
+ {"bip32derivs", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nArguments:\n"
"1. \"inputs\" (array, required) A json array of json objects\n"
" [\n"
@@ -3970,7 +4260,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
" \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
" },\n"
" {\n"
- " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex-encoded data\n"
" }\n"
" ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.\n"
@@ -4026,29 +4316,18 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
// Make a blank psbt
- PartiallySignedTransaction psbtx;
- psbtx.tx = rawTx;
- for (unsigned int i = 0; i < rawTx.vin.size(); ++i) {
- psbtx.inputs.push_back(PSBTInput());
- }
- for (unsigned int i = 0; i < rawTx.vout.size(); ++i) {
- psbtx.outputs.push_back(PSBTOutput());
- }
-
- // Use CTransaction for the constant parts of the
- // transaction to avoid rehashing.
- const CTransaction txConst(*psbtx.tx);
+ PartiallySignedTransaction psbtx(rawTx);
// Fill transaction with out data but don't sign
bool bip32derivs = request.params[4].isNull() ? false : request.params[4].get_bool();
- FillPSBT(pwallet, psbtx, &txConst, 1, false, bip32derivs);
+ FillPSBT(pwallet, psbtx, 1, false, bip32derivs);
// Serialize the PSBT
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
UniValue result(UniValue::VOBJ);
- result.pushKV("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()));
+ result.pushKV("psbt", EncodeBase64(ssTx.str()));
result.pushKV("fee", ValueFromAmount(fee));
result.pushKV("changepos", change_position);
return result;
@@ -4065,11 +4344,11 @@ UniValue importprunedfunds(const JSONRPCRequest& request);
UniValue removeprunedfunds(const JSONRPCRequest& request);
UniValue importmulti(const JSONRPCRequest& request);
+// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
- { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
{ "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
@@ -4104,8 +4383,9 @@ static const CRPCCommand commands[] =
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} },
{ "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} },
{ "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
- { "wallet", "listtransactions", &listtransactions, {"dummy","count","skip","include_watchonly"} },
+ { "wallet", "listtransactions", &listtransactions, {"label|dummy","count","skip","include_watchonly"} },
{ "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
+ { "wallet", "listwalletdir", &listwalletdir, {} },
{ "wallet", "listwallets", &listwallets, {} },
{ "wallet", "loadwallet", &loadwallet, {"filename"} },
{ "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} },
@@ -4125,6 +4405,7 @@ static const CRPCCommand commands[] =
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
{ "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} },
};
+// clang-format on
void RegisterWalletRPCCommands(CRPCTable &t)
{
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index 9b9a159b86..abd7750874 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -30,5 +30,5 @@ bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
UniValue getaddressinfo(const JSONRPCRequest& request);
UniValue signrawtransactionwithwallet(const JSONRPCRequest& request);
-bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const CTransaction* txConst, int sighash_type = 1, bool sign = true, bool bip32derivs = false);
+bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false);
#endif //BITCOIN_WALLET_RPCWALLET_H
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index a24dc1b5f9..5c65acf601 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -28,7 +28,8 @@ std::vector<std::unique_ptr<CWalletTx>> wtxn;
typedef std::set<CInputCoin> CoinSet;
static std::vector<COutput> vCoins;
-static CWallet testWallet("dummy", WalletDatabase::CreateDummy());
+static auto testChain = interfaces::MakeChain();
+static CWallet testWallet(*testChain, WalletLocation(), WalletDatabase::CreateDummy());
static CAmount balance = 0;
CoinEligibilityFilter filter_standard(1, 6, 0);
@@ -65,7 +66,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
tx.vin.resize(1);
}
- std::unique_ptr<CWalletTx> wtx(new CWalletTx(&testWallet, MakeTransactionRef(std::move(tx))));
+ std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
if (fIsFromMe)
{
wtx->fDebitCached = true;
@@ -462,14 +463,19 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
+ }
- // test with many inputs
- for (CAmount amt=1500; amt < COIN; amt*=10) {
- empty_wallet();
- // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
- for (uint16_t j = 0; j < 676; j++)
- add_coin(amt);
+ // test with many inputs
+ for (CAmount amt=1500; amt < COIN; amt*=10) {
+ empty_wallet();
+ // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
+ for (uint16_t j = 0; j < 676; j++)
+ add_coin(amt);
+
+ // We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
+ for (int i = 0; i < RUN_TESTS; i++) {
BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+
if (amt - 2000 < MIN_CHANGE) {
// needs more than one input:
uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
@@ -481,14 +487,17 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
BOOST_CHECK_EQUAL(nValueRet, amt);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
}
- }
+ }
+ }
- // test randomness
- {
- empty_wallet();
- for (int i2 = 0; i2 < 100; i2++)
- add_coin(COIN);
+ // test randomness
+ {
+ empty_wallet();
+ for (int i2 = 0; i2 < 100; i2++)
+ add_coin(COIN);
+ // Again, we only create the wallet once to save time, but we still run the coin selection RUN_TESTS times.
+ for (int i = 0; i < RUN_TESTS; i++) {
// picking 50 from 100 coins doesn't depend on the shuffle,
// but does depend on randomness in the stochastic approximation code
BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, GroupCoins(vCoins), setCoinsRet , nValueRet, coin_selection_params, bnb_used));
@@ -506,17 +515,19 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
fails++;
}
BOOST_CHECK_NE(fails, RANDOM_REPEATS);
-
- // add 75 cents in small change. not enough to make 90 cents,
- // then try making 90 cents. there are multiple competing "smallest bigger" coins,
- // one of which should be picked at random
- add_coin(5 * CENT);
- add_coin(10 * CENT);
- add_coin(15 * CENT);
- add_coin(20 * CENT);
- add_coin(25 * CENT);
-
- fails = 0;
+ }
+
+ // add 75 cents in small change. not enough to make 90 cents,
+ // then try making 90 cents. there are multiple competing "smallest bigger" coins,
+ // one of which should be picked at random
+ add_coin(5 * CENT);
+ add_coin(10 * CENT);
+ add_coin(15 * CENT);
+ add_coin(20 * CENT);
+ add_coin(25 * CENT);
+
+ for (int i = 0; i < RUN_TESTS; i++) {
+ int fails = 0;
for (int j = 0; j < RANDOM_REPEATS; j++)
{
// selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
@@ -527,8 +538,9 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test)
fails++;
}
BOOST_CHECK_NE(fails, RANDOM_REPEATS);
- }
- }
+ }
+ }
+
empty_wallet();
}
diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp
new file mode 100644
index 0000000000..3b828d57f9
--- /dev/null
+++ b/src/wallet/test/init_test_fixture.cpp
@@ -0,0 +1,44 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <fs.h>
+
+#include <wallet/test/init_test_fixture.h>
+
+InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName): BasicTestingSetup(chainName)
+{
+ m_chain_client = MakeWalletClient(*m_chain, {});
+
+ std::string sep;
+ sep += fs::path::preferred_separator;
+
+ m_datadir = SetDataDir("tempdir");
+ m_cwd = fs::current_path();
+
+ m_walletdir_path_cases["default"] = m_datadir / "wallets";
+ m_walletdir_path_cases["custom"] = m_datadir / "my_wallets";
+ m_walletdir_path_cases["nonexistent"] = m_datadir / "path_does_not_exist";
+ m_walletdir_path_cases["file"] = m_datadir / "not_a_directory.dat";
+ m_walletdir_path_cases["trailing"] = m_datadir / "wallets" / sep;
+ m_walletdir_path_cases["trailing2"] = m_datadir / "wallets" / sep / sep;
+
+ fs::current_path(m_datadir);
+ m_walletdir_path_cases["relative"] = "wallets";
+
+ fs::create_directories(m_walletdir_path_cases["default"]);
+ fs::create_directories(m_walletdir_path_cases["custom"]);
+ fs::create_directories(m_walletdir_path_cases["relative"]);
+ std::ofstream f(m_walletdir_path_cases["file"].BOOST_FILESYSTEM_C_STR);
+ f.close();
+}
+
+InitWalletDirTestingSetup::~InitWalletDirTestingSetup()
+{
+ fs::current_path(m_cwd);
+}
+
+void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path)
+{
+ gArgs.ForceSetArg("-walletdir", walletdir_path.string());
+} \ No newline at end of file
diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h
new file mode 100644
index 0000000000..cd47b31da1
--- /dev/null
+++ b/src/wallet/test/init_test_fixture.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
+#define BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H
+
+#include <interfaces/chain.h>
+#include <test/test_bitcoin.h>
+
+
+struct InitWalletDirTestingSetup: public BasicTestingSetup {
+ explicit InitWalletDirTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
+ ~InitWalletDirTestingSetup();
+ void SetWalletDir(const fs::path& walletdir_path);
+
+ fs::path m_datadir;
+ fs::path m_cwd;
+ std::map<std::string, fs::path> m_walletdir_path_cases;
+ std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
+ std::unique_ptr<interfaces::ChainClient> m_chain_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
new file mode 100644
index 0000000000..5852d3ef84
--- /dev/null
+++ b/src/wallet/test/init_tests.cpp
@@ -0,0 +1,78 @@
+// Copyright (c) 2018 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <boost/test/unit_test.hpp>
+
+#include <test/test_bitcoin.h>
+#include <wallet/test/init_test_fixture.h>
+
+#include <init.h>
+#include <walletinitinterface.h>
+#include <wallet/wallet.h>
+
+
+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();
+ BOOST_CHECK(result == true);
+ fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
+ BOOST_CHECK(walletdir == expected_path);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
+{
+ SetWalletDir(m_walletdir_path_cases["custom"]);
+ bool result = m_chain_client->verify();
+ BOOST_CHECK(result == true);
+ fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]);
+ BOOST_CHECK(walletdir == expected_path);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist)
+{
+ SetWalletDir(m_walletdir_path_cases["nonexistent"]);
+ bool result = m_chain_client->verify();
+ BOOST_CHECK(result == false);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory)
+{
+ SetWalletDir(m_walletdir_path_cases["file"]);
+ bool result = m_chain_client->verify();
+ BOOST_CHECK(result == false);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative)
+{
+ SetWalletDir(m_walletdir_path_cases["relative"]);
+ bool result = m_chain_client->verify();
+ BOOST_CHECK(result == false);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
+{
+ SetWalletDir(m_walletdir_path_cases["trailing"]);
+ bool result = m_chain_client->verify();
+ BOOST_CHECK(result == true);
+ fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
+ BOOST_CHECK(walletdir == expected_path);
+}
+
+BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
+{
+ SetWalletDir(m_walletdir_path_cases["trailing2"]);
+ bool result = m_chain_client->verify();
+ BOOST_CHECK(result == true);
+ fs::path walletdir = gArgs.GetArg("-walletdir", "");
+ fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
+ BOOST_CHECK(walletdir == expected_path);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index 526f2d983f..9918eeb89f 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -4,7 +4,7 @@
#include <key_io.h>
#include <script/sign.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <univalue.h>
@@ -17,6 +17,8 @@ BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup)
BOOST_AUTO_TEST_CASE(psbt_updater_test)
{
+ LOCK(m_wallet.cs_wallet);
+
// Create prevtxs and add to wallet
CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION);
CTransactionRef prev_tx1;
@@ -57,12 +59,8 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
CDataStream ssData(ParseHex("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000000000000000000"), SER_NETWORK, PROTOCOL_VERSION);
ssData >> psbtx;
- // Use CTransaction for the constant parts of the
- // transaction to avoid rehashing.
- const CTransaction txConst(*psbtx.tx);
-
// Fill transaction with our data
- FillPSBT(&m_wallet, psbtx, &txConst, 1, false, true);
+ FillPSBT(&m_wallet, psbtx, SIGHASH_ALL, false, true);
// Get the final tx
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp
index f193d5c41d..ae7092fa89 100644
--- a/src/wallet/test/wallet_crypto_tests.cpp
+++ b/src/wallet/test/wallet_crypto_tests.cpp
@@ -3,7 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/test_bitcoin.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <wallet/crypter.h>
#include <vector>
diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp
index de59b60349..a5fb1db86c 100644
--- a/src/wallet/test/wallet_test_fixture.cpp
+++ b/src/wallet/test/wallet_test_fixture.cpp
@@ -6,9 +6,10 @@
#include <rpc/server.h>
#include <wallet/db.h>
+#include <wallet/rpcwallet.h>
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
- TestingSetup(chainName), m_wallet("mock", WalletDatabase::CreateMock())
+ TestingSetup(chainName), m_wallet(*m_chain, WalletLocation(), WalletDatabase::CreateMock())
{
bool fFirstRun;
m_wallet.LoadWallet(fFirstRun);
diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h
index ff8ec32b03..e6fe8c9473 100644
--- a/src/wallet/test/wallet_test_fixture.h
+++ b/src/wallet/test/wallet_test_fixture.h
@@ -7,6 +7,8 @@
#include <test/test_bitcoin.h>
+#include <interfaces/chain.h>
+#include <interfaces/wallet.h>
#include <wallet/wallet.h>
#include <memory>
@@ -17,6 +19,7 @@ struct WalletTestingSetup: public TestingSetup {
explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~WalletTestingSetup();
+ std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
CWallet m_wallet;
};
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 3a8e6f751a..623c5c39a2 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -11,11 +11,13 @@
#include <vector>
#include <consensus/validation.h>
+#include <interfaces/chain.h>
#include <rpc/server.h>
#include <test/test_bitcoin.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/test/wallet_test_fixture.h>
+#include <policy/policy.h>
#include <boost/test/unit_test.hpp>
#include <univalue.h>
@@ -34,6 +36,8 @@ static void AddKey(CWallet& wallet, const CKey& key)
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
{
+ auto chain = interfaces::MakeChain();
+
// Cap last block file size, and mine new block in a new block file.
CBlockIndex* const nullBlock = nullptr;
CBlockIndex* oldTip = chainActive.Tip();
@@ -41,12 +45,12 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
CBlockIndex* newTip = chainActive.Tip();
- LOCK(cs_main);
+ auto locked_chain = chain->lock();
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
- CWallet wallet("dummy", WalletDatabase::CreateDummy());
+ CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -61,7 +65,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
- CWallet wallet("dummy", WalletDatabase::CreateDummy());
+ CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
@@ -73,7 +77,7 @@ BOOST_FIXTURE_TEST_CASE(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>("dummy", WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
AddWallet(wallet);
UniValue keys;
keys.setArray();
@@ -115,6 +119,8 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
// than or equal to key birthday.
BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
{
+ auto chain = interfaces::MakeChain();
+
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5;
@@ -128,13 +134,13 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
SetMockTime(KEY_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
- LOCK(cs_main);
+ auto locked_chain = chain->lock();
std::string backup_file = (SetDataDir("importwallet_rescan") / "wallet.backup").string();
// Import key into wallet and call dumpwallet to create backup file.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
LOCK(wallet->cs_wallet);
wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
@@ -150,7 +156,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy());
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
JSONRPCRequest request;
request.params.setArray();
@@ -180,21 +186,23 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// debit functions.
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
{
- CWallet wallet("dummy", WalletDatabase::CreateDummy());
+ auto chain = interfaces::MakeChain();
+ CWallet wallet(*chain, WalletLocation(), WalletDatabase::CreateDummy());
CWalletTx wtx(&wallet, m_coinbase_txns.back());
- LOCK2(cs_main, wallet.cs_wallet);
+ auto locked_chain = chain->lock();
+ LOCK(wallet.cs_wallet);
wtx.hashBlock = chainActive.Tip()->GetBlockHash();
wtx.nIndex = 0;
// Call GetImmatureCredit() once before adding the key to the wallet to
// cache the current immature credit amount, which is 0.
- BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 0);
+ BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 0);
// Invalidate the cached value, add the key, and make sure a new immature
// credit amount is calculated.
wtx.MarkDirty();
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
- BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50*COIN);
+ BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(*locked_chain), 50*COIN);
}
static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
@@ -204,7 +212,7 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
SetMockTime(mockTime);
CBlockIndex* block = nullptr;
if (blockTime > 0) {
- LOCK(cs_main);
+ auto locked_chain = wallet.chain().lock();
auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256& hash = inserted.first->first;
@@ -273,7 +281,7 @@ public:
ListCoinsTestingSetup()
{
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
- wallet = MakeUnique<CWallet>("mock", WalletDatabase::CreateMock());
+ wallet = MakeUnique<CWallet>(*m_chain, WalletLocation(), WalletDatabase::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
AddKey(*wallet, coinbaseKey);
@@ -295,7 +303,7 @@ public:
int changePos = -1;
std::string error;
CCoinControl dummy;
- BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, reservekey, fee, changePos, error, dummy));
+ BOOST_CHECK(wallet->CreateTransaction(*m_locked_chain, {recipient}, tx, reservekey, fee, changePos, error, dummy));
CValidationState state;
BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, nullptr, state));
CMutableTransaction blocktx;
@@ -311,6 +319,8 @@ public:
return it->second;
}
+ std::unique_ptr<interfaces::Chain> m_chain = interfaces::MakeChain();
+ std::unique_ptr<interfaces::Chain::Lock> m_locked_chain = m_chain->assumeLocked(); // Temporary. Removed in upcoming lock cleanup
std::unique_ptr<CWallet> wallet;
};
@@ -323,7 +333,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
std::map<CTxDestination, std::vector<COutput>> list;
{
LOCK2(cs_main, wallet->cs_wallet);
- list = wallet->ListCoins();
+ list = wallet->ListCoins(*m_locked_chain);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
@@ -339,7 +349,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */});
{
LOCK2(cs_main, wallet->cs_wallet);
- list = wallet->ListCoins();
+ list = wallet->ListCoins(*m_locked_chain);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
@@ -349,7 +359,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
{
LOCK2(cs_main, wallet->cs_wallet);
std::vector<COutput> available;
- wallet->AvailableCoins(available);
+ wallet->AvailableCoins(*m_locked_chain, available);
BOOST_CHECK_EQUAL(available.size(), 2U);
}
for (const auto& group : list) {
@@ -361,14 +371,14 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
{
LOCK2(cs_main, wallet->cs_wallet);
std::vector<COutput> available;
- wallet->AvailableCoins(available);
+ wallet->AvailableCoins(*m_locked_chain, available);
BOOST_CHECK_EQUAL(available.size(), 0U);
}
// Confirm ListCoins still returns same result as before, despite coins
// being locked.
{
LOCK2(cs_main, wallet->cs_wallet);
- list = wallet->ListCoins();
+ list = wallet->ListCoins(*m_locked_chain);
}
BOOST_CHECK_EQUAL(list.size(), 1U);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress);
@@ -377,11 +387,55 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup)
BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
{
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy());
+ auto chain = interfaces::MakeChain();
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(*chain, WalletLocation(), WalletDatabase::CreateDummy());
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
BOOST_CHECK(!wallet->TopUpKeyPool(1000));
CPubKey pubkey;
BOOST_CHECK(!wallet->GetKeyFromPool(pubkey, false));
}
+// Explicit calculation which is used to test the wallet constant
+// We get the same virtual size due to rounding(weight/4) for both use_max_sig values
+static size_t CalculateNestedKeyhashInputSize(bool use_max_sig)
+{
+ // Generate ephemeral valid pubkey
+ CKey key;
+ key.MakeNewKey(true);
+ CPubKey pubkey = key.GetPubKey();
+
+ // Generate pubkey hash
+ uint160 key_hash(Hash160(pubkey.begin(), pubkey.end()));
+
+ // Create inner-script to enter into keystore. Key hash can't be 0...
+ CScript inner_script = CScript() << OP_0 << std::vector<unsigned char>(key_hash.begin(), key_hash.end());
+
+ // Create outer P2SH script for the output
+ uint160 script_id(Hash160(inner_script.begin(), inner_script.end()));
+ CScript script_pubkey = CScript() << OP_HASH160 << std::vector<unsigned char>(script_id.begin(), script_id.end()) << OP_EQUAL;
+
+ // Add inner-script to key store and key to watchonly
+ CBasicKeyStore keystore;
+ keystore.AddCScript(inner_script);
+ keystore.AddKeyPubKey(key, pubkey);
+
+ // Fill in dummy signatures for fee calculation.
+ SignatureData sig_data;
+
+ if (!ProduceSignature(keystore, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, script_pubkey, sig_data)) {
+ // We're hand-feeding it correct arguments; shouldn't happen
+ assert(false);
+ }
+
+ CTxIn tx_in;
+ UpdateInput(tx_in, sig_data);
+ return (size_t)GetVirtualTransactionInputSize(tx_in);
+}
+
+BOOST_FIXTURE_TEST_CASE(dummy_input_size_test, TestChain100Setup)
+{
+ BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(false), DUMMY_NESTED_P2WPKH_INPUT_SIZE);
+ BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(true), DUMMY_NESTED_P2WPKH_INPUT_SIZE);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index e41f829f02..d7798e005f 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -11,6 +11,7 @@
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <fs.h>
+#include <interfaces/chain.h>
#include <key.h>
#include <key_io.h>
#include <keystore.h>
@@ -25,9 +26,8 @@
#include <shutdown.h>
#include <timedata.h>
#include <txmempool.h>
-#include <utilmoneystr.h>
+#include <util/moneystr.h>
#include <wallet/fees.h>
-#include <wallet/walletutil.h>
#include <algorithm>
#include <assert.h>
@@ -594,7 +594,7 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran
* Outpoint is spent if any non-conflicted transaction
* spends it:
*/
-bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
+bool CWallet::IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash, unsigned int n) const
{
const COutPoint outpoint(hash, n);
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
@@ -605,7 +605,7 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
const uint256& wtxid = it->second;
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) {
- int depth = mit->second.GetDepthInMainChain();
+ int depth = mit->second.GetDepthInMainChain(locked_chain);
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned()))
return true; // Spent
}
@@ -687,6 +687,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{
encrypted_batch->TxnAbort();
delete encrypted_batch;
+ encrypted_batch = nullptr;
// We now probably have half of our keys encrypted in memory, and half not...
// die and let the user reload the unencrypted wallet.
assert(false);
@@ -697,6 +698,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
if (!encrypted_batch->TxnCommit()) {
delete encrypted_batch;
+ encrypted_batch = nullptr;
// We now have keys encrypted in memory, but not on disk...
// die to avoid confusion and let the user reload the unencrypted wallet.
assert(false);
@@ -720,6 +722,11 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// bits of the unencrypted private key in slack space in the database file.
database->Rewrite();
+ // BDB seems to have a bad habit of writing old data into
+ // slack space in .dat files; that is bad if the old data is
+ // unencrypted private keys. So:
+ database->ReloadDbEnv();
+
}
NotifyStatusChanged(this);
@@ -999,9 +1006,10 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI
bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
const CWalletTx* wtx = GetWalletTx(hashTx);
- return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool();
+ return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain(*locked_chain) == 0 && !wtx->InMempool();
}
void CWallet::MarkInputsDirty(const CTransactionRef& tx)
@@ -1014,9 +1022,10 @@ void CWallet::MarkInputsDirty(const CTransactionRef& tx)
}
}
-bool CWallet::AbandonTransaction(const uint256& hashTx)
+bool CWallet::AbandonTransaction(interfaces::Chain::Lock& locked_chain, const uint256& hashTx)
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain_recursive = chain().lock(); // Temporary. Removed in upcoming lock cleanup
+ LOCK(cs_wallet);
WalletBatch batch(*database, "r+");
@@ -1027,7 +1036,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
auto it = mapWallet.find(hashTx);
assert(it != mapWallet.end());
CWalletTx& origtx = it->second;
- if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) {
+ if (origtx.GetDepthInMainChain(locked_chain) != 0 || origtx.InMempool()) {
return false;
}
@@ -1040,7 +1049,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
auto it = mapWallet.find(now);
assert(it != mapWallet.end());
CWalletTx& wtx = it->second;
- int currentconfirm = wtx.GetDepthInMainChain();
+ int currentconfirm = wtx.GetDepthInMainChain(locked_chain);
// If the orig tx was not in block, none of its spends can be
assert(currentconfirm <= 0);
// if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
@@ -1071,7 +1080,8 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
int conflictconfirms = 0;
CBlockIndex* pindex = LookupBlockIndex(hashBlock);
@@ -1100,7 +1110,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)
auto it = mapWallet.find(now);
assert(it != mapWallet.end());
CWalletTx& wtx = it->second;
- int currentconfirm = wtx.GetDepthInMainChain();
+ int currentconfirm = wtx.GetDepthInMainChain(*locked_chain);
if (conflictconfirms < currentconfirm) {
// Block is 'more conflicted' than current confirm; update.
// Mark transaction as conflicted with this block.
@@ -1134,7 +1144,8 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, const CBlockIndex *pin
}
void CWallet::TransactionAddedToMempool(const CTransactionRef& ptx) {
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
SyncTransaction(ptx);
auto it = mapWallet.find(ptx->GetHash());
@@ -1152,7 +1163,8 @@ void CWallet::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
}
void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
// TODO: Temporarily ensure that mempool removals are notified before
// connected transactions. This shouldn't matter, but the abandoned
// state of transactions in our wallet is currently cleared when we
@@ -1174,7 +1186,8 @@ void CWallet::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const
}
void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) {
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
for (const CTransactionRef& ptx : pblock->vtx) {
SyncTransaction(ptx);
@@ -1193,7 +1206,7 @@ void CWallet::BlockUntilSyncedToCurrentChain() {
// We could also take cs_wallet here, and call m_last_block_processed
// protected by cs_wallet instead of cs_main, but as long as we need
// cs_main here anyway, it's easier to just call it cs_main-protected.
- LOCK(cs_main);
+ auto locked_chain = chain().lock();
const CBlockIndex* initialChainTip = chainActive.Tip();
if (m_last_block_processed && m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) {
@@ -1255,6 +1268,11 @@ CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) cons
bool CWallet::IsChange(const CTxOut& txout) const
{
+ return IsChange(txout.scriptPubKey);
+}
+
+bool CWallet::IsChange(const CScript& script) const
+{
// TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a script that is ours, but is not in the address book
// is change. That assumption is likely to break when we implement multisignature
@@ -1262,10 +1280,10 @@ bool CWallet::IsChange(const CTxOut& txout) 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).
- if (::IsMine(*this, txout.scriptPubKey))
+ if (::IsMine(*this, script))
{
CTxDestination address;
- if (!ExtractDestination(txout.scriptPubKey, address))
+ if (!ExtractDestination(script, address))
return true;
LOCK(cs_wallet);
@@ -1512,8 +1530,6 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet,
CMutableTransaction txn;
txn.vin.push_back(CTxIn(COutPoint()));
if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
- // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
- // implies that we can sign for every input.
return -1;
}
return GetVirtualTransactionInputSize(txn.vin[0]);
@@ -1589,7 +1605,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
// to be scanned.
CBlockIndex* startBlock = nullptr;
{
- LOCK(cs_main);
+ auto locked_chain = chain().lock();
startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW);
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0);
}
@@ -1641,7 +1657,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
double progress_begin;
double progress_end;
{
- LOCK(cs_main);
+ auto locked_chain = chain().lock();
progress_begin = GuessVerificationProgress(chainParams.TxData(), pindex);
if (pindexStop == nullptr) {
tip = chainActive.Tip();
@@ -1663,7 +1679,8 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
CBlock block;
if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
if (pindex && !chainActive.Contains(pindex)) {
// Abort scan if current block is no longer active, to prevent
// marking transactions as coming from the wrong block.
@@ -1680,7 +1697,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
break;
}
{
- LOCK(cs_main);
+ auto locked_chain = chain().lock();
pindex = chainActive.Next(pindex);
progress_current = GuessVerificationProgress(chainParams.TxData(), pindex);
if (pindexStop == nullptr && tip != chainActive.Tip()) {
@@ -1705,7 +1722,8 @@ void CWallet::ReacceptWalletTransactions()
// If transactions aren't being broadcasted, don't let them into local mempool either
if (!fBroadcastTransactions)
return;
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
std::map<int64_t, CWalletTx*> mapSorted;
// Sort pending wallet transactions based on their initial wallet insertion order
@@ -1715,7 +1733,7 @@ void CWallet::ReacceptWalletTransactions()
CWalletTx& wtx = item.second;
assert(wtx.GetHash() == wtxid);
- int nDepth = wtx.GetDepthInMainChain();
+ int nDepth = wtx.GetDepthInMainChain(*locked_chain);
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
@@ -1726,18 +1744,18 @@ void CWallet::ReacceptWalletTransactions()
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
CValidationState state;
- wtx.AcceptToMemoryPool(maxTxFee, state);
+ wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state);
}
}
-bool CWalletTx::RelayWalletTransaction(CConnman* connman)
+bool CWalletTx::RelayWalletTransaction(interfaces::Chain::Lock& locked_chain, CConnman* connman)
{
assert(pwallet->GetBroadcastTransactions());
- if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0)
+ if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain(locked_chain) == 0)
{
CValidationState state;
/* GetDepthInMainChain already catches known conflicts. */
- if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) {
+ if (InMempool() || AcceptToMemoryPool(locked_chain, maxTxFee, state)) {
pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
if (connman) {
CInv inv(MSG_TX, GetHash());
@@ -1795,10 +1813,10 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
return debit;
}
-CAmount CWalletTx::GetCredit(const isminefilter& filter) const
+CAmount CWalletTx::GetCredit(interfaces::Chain::Lock& locked_chain, const isminefilter& filter) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsImmatureCoinBase())
+ if (IsImmatureCoinBase(locked_chain))
return 0;
CAmount credit = 0;
@@ -1828,9 +1846,9 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const
return credit;
}
-CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
+CAmount CWalletTx::GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache) const
{
- if (IsImmatureCoinBase() && IsInMainChain()) {
+ if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
if (fUseCache && fImmatureCreditCached)
return nImmatureCreditCached;
nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE);
@@ -1841,13 +1859,13 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
return 0;
}
-CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
+CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache, const isminefilter& filter) const
{
if (pwallet == nullptr)
return 0;
// Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsImmatureCoinBase())
+ if (IsImmatureCoinBase(locked_chain))
return 0;
CAmount* cache = nullptr;
@@ -1869,7 +1887,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter
uint256 hashTx = GetHash();
for (unsigned int i = 0; i < tx->vout.size(); i++)
{
- if (!pwallet->IsSpent(hashTx, i))
+ if (!pwallet->IsSpent(locked_chain, hashTx, i))
{
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, filter);
@@ -1886,9 +1904,9 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter
return nCredit;
}
-CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
+CAmount CWalletTx::GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache) const
{
- if (IsImmatureCoinBase() && IsInMainChain()) {
+ if (IsImmatureCoinBase(locked_chain) && IsInMainChain(locked_chain)) {
if (fUseCache && fImmatureWatchCreditCached)
return nImmatureWatchCreditCached;
nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY);
@@ -1913,12 +1931,14 @@ bool CWalletTx::InMempool() const
return fInMempool;
}
-bool CWalletTx::IsTrusted() const
+bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain) const
{
+ LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
+
// Quick answer in most cases
if (!CheckFinalTx(*tx))
return false;
- int nDepth = GetDepthInMainChain();
+ int nDepth = GetDepthInMainChain(locked_chain);
if (nDepth >= 1)
return true;
if (nDepth < 0)
@@ -1953,7 +1973,7 @@ bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const
return CTransaction(tx1) == CTransaction(tx2);
}
-std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman)
+std::vector<uint256> CWallet::ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime, CConnman* connman)
{
std::vector<uint256> result;
@@ -1972,7 +1992,7 @@ std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime, CCon
for (const std::pair<const unsigned int, CWalletTx*>& item : mapSorted)
{
CWalletTx& wtx = *item.second;
- if (wtx.RelayWalletTransaction(connman))
+ if (wtx.RelayWalletTransaction(locked_chain, connman))
result.push_back(wtx.GetHash());
}
return result;
@@ -1996,7 +2016,8 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman
// Rebroadcast unconfirmed txes older than 5 minutes before the last
// block was found:
- std::vector<uint256> relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60, connman);
+ auto locked_chain = chain().assumeLocked(); // Temporary. Removed in upcoming lock cleanup
+ std::vector<uint256> relayed = ResendWalletTransactionsBefore(*locked_chain, nBestBlockTime-5*60, connman);
if (!relayed.empty())
WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size());
}
@@ -2016,12 +2037,13 @@ CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) con
{
CAmount nTotal = 0;
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
for (const auto& entry : mapWallet)
{
const CWalletTx* pcoin = &entry.second;
- if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) {
- nTotal += pcoin->GetAvailableCredit(true, filter);
+ if (pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) >= min_depth) {
+ nTotal += pcoin->GetAvailableCredit(*locked_chain, true, filter);
}
}
}
@@ -2033,12 +2055,13 @@ CAmount CWallet::GetUnconfirmedBalance() const
{
CAmount nTotal = 0;
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
for (const auto& entry : mapWallet)
{
const CWalletTx* pcoin = &entry.second;
- if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())
- nTotal += pcoin->GetAvailableCredit();
+ if (!pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) == 0 && pcoin->InMempool())
+ nTotal += pcoin->GetAvailableCredit(*locked_chain);
}
}
return nTotal;
@@ -2048,11 +2071,12 @@ CAmount CWallet::GetImmatureBalance() const
{
CAmount nTotal = 0;
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
for (const auto& entry : mapWallet)
{
const CWalletTx* pcoin = &entry.second;
- nTotal += pcoin->GetImmatureCredit();
+ nTotal += pcoin->GetImmatureCredit(*locked_chain);
}
}
return nTotal;
@@ -2062,12 +2086,13 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
{
CAmount nTotal = 0;
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
for (const auto& entry : mapWallet)
{
const CWalletTx* pcoin = &entry.second;
- if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 && pcoin->InMempool())
- nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY);
+ if (!pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) == 0 && pcoin->InMempool())
+ nTotal += pcoin->GetAvailableCredit(*locked_chain, true, ISMINE_WATCH_ONLY);
}
}
return nTotal;
@@ -2077,11 +2102,12 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
{
CAmount nTotal = 0;
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
for (const auto& entry : mapWallet)
{
const CWalletTx* pcoin = &entry.second;
- nTotal += pcoin->GetImmatureWatchOnlyCredit();
+ nTotal += pcoin->GetImmatureWatchOnlyCredit(*locked_chain);
}
}
return nTotal;
@@ -2095,13 +2121,15 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
// trusted.
CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) const
{
- LOCK2(cs_main, cs_wallet);
+ LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
CAmount balance = 0;
for (const auto& entry : mapWallet) {
const CWalletTx& wtx = entry.second;
- const int depth = wtx.GetDepthInMainChain();
- if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase()) {
+ const int depth = wtx.GetDepthInMainChain(*locked_chain);
+ if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase(*locked_chain)) {
continue;
}
@@ -2128,11 +2156,12 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) cons
CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
CAmount balance = 0;
std::vector<COutput> vCoins;
- AvailableCoins(vCoins, true, coinControl);
+ AvailableCoins(*locked_chain, vCoins, true, coinControl);
for (const COutput& out : vCoins) {
if (out.fSpendable) {
balance += out.tx->tx->vout[out.i].nValue;
@@ -2141,7 +2170,7 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
return balance;
}
-void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t nMaximumCount, const int nMinDepth, const int nMaxDepth) const
+void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t nMaximumCount, const int nMinDepth, const int nMaxDepth) const
{
AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
@@ -2157,10 +2186,10 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
if (!CheckFinalTx(*pcoin->tx))
continue;
- if (pcoin->IsImmatureCoinBase())
+ if (pcoin->IsImmatureCoinBase(locked_chain))
continue;
- int nDepth = pcoin->GetDepthInMainChain();
+ int nDepth = pcoin->GetDepthInMainChain(locked_chain);
if (nDepth < 0)
continue;
@@ -2169,7 +2198,7 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
if (nDepth == 0 && !pcoin->InMempool())
continue;
- bool safeTx = pcoin->IsTrusted();
+ bool safeTx = pcoin->IsTrusted(locked_chain);
// We should not consider coins from transactions that are replacing
// other transactions.
@@ -2219,7 +2248,7 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
if (IsLockedCoin(entry.first, i))
continue;
- if (IsSpent(wtxid, i))
+ if (IsSpent(locked_chain, wtxid, i))
continue;
isminetype mine = IsMine(pcoin->tx->vout[i]);
@@ -2250,7 +2279,7 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
}
}
-std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
+std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Chain::Lock& locked_chain) const
{
AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
@@ -2258,7 +2287,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins;
- AvailableCoins(availableCoins);
+ AvailableCoins(locked_chain, availableCoins);
for (const COutput& coin : availableCoins) {
CTxDestination address;
@@ -2273,7 +2302,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
for (const COutPoint& output : lockedCoins) {
auto it = mapWallet.find(output.hash);
if (it != mapWallet.end()) {
- int depth = it->second.GetDepthInMainChain();
+ int depth = it->second.GetDepthInMainChain(locked_chain);
if (depth >= 0 && output.n < it->second.tx->vout.size() &&
IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) {
CTxDestination address;
@@ -2488,11 +2517,12 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
// Acquire the locks to prevent races to the new locked unspents between the
// CreateTransaction call and LockCoin calls (when lockUnspents is true).
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
CReserveKey reservekey(this);
CTransactionRef tx_new;
- if (!CreateTransaction(vecSend, tx_new, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
+ if (!CreateTransaction(*locked_chain, vecSend, tx_new, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
return false;
}
@@ -2551,7 +2581,7 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec
return m_default_address_type;
}
-bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet,
+bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet,
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{
CAmount nValue = 0;
@@ -2613,10 +2643,11 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
int nBytes;
{
std::set<CInputCoin> setCoins;
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
{
std::vector<COutput> vAvailableCoins;
- AvailableCoins(vAvailableCoins, true, &coin_control);
+ AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control);
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
// Create change script that will be used if we need change
@@ -2655,7 +2686,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, change_type));
}
CTxOut change_prototype_txout(0, scriptChange);
- coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0);
+ coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
CFeeRate discard_rate = GetDiscardRate(*this, ::feeEstimator);
@@ -2699,7 +2730,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
}
}
// Include the fee cost for outputs. Note this is only used for BnB right now
- coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, SER_NETWORK, PROTOCOL_VERSION);
+ coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
if (IsDust(txout, ::dustRelayFee))
{
@@ -2722,7 +2753,14 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
if (pick_new_inputs) {
nValueIn = 0;
setCoins.clear();
- coin_selection_params.change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
+ int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
+ // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
+ // as lower-bound to allow BnB to do it's thing
+ if (change_spend_size == -1) {
+ coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
+ } else {
+ coin_selection_params.change_spend_size = (size_t)change_spend_size;
+ }
coin_selection_params.effective_fee = nFeeRateNeeded;
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used))
{
@@ -2736,6 +2774,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
return false;
}
}
+ } else {
+ bnb_used = false;
}
const CAmount nChange = nValueIn - nValueToSelect;
@@ -2949,7 +2989,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state)
{
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
CWalletTx wtxNew(this, std::move(tx));
wtxNew.mapValue = std::move(mapValue);
@@ -2982,11 +3023,11 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
if (fBroadcastTransactions)
{
// Broadcast
- if (!wtx.AcceptToMemoryPool(maxTxFee, state)) {
+ if (!wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state));
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
} else {
- wtx.RelayWalletTransaction(connman);
+ wtx.RelayWalletTransaction(*locked_chain, connman);
}
}
}
@@ -2995,7 +3036,8 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
- LOCK2(cs_main, cs_wallet);
+ auto locked_chain = chain().lock();
+ LOCK(cs_wallet);
fFirstRunRet = false;
DBErrors nLoadWalletRet = WalletBatch(*database,"cr+").LoadWallet(this);
@@ -3379,7 +3421,7 @@ int64_t CWallet::GetOldestKeyPoolTime()
return oldestKey;
}
-std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
+std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain::Lock& locked_chain)
{
std::map<CTxDestination, CAmount> balances;
@@ -3389,13 +3431,13 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
{
const CWalletTx *pcoin = &walletEntry.second;
- if (!pcoin->IsTrusted())
+ if (!pcoin->IsTrusted(locked_chain))
continue;
- if (pcoin->IsImmatureCoinBase())
+ if (pcoin->IsImmatureCoinBase(locked_chain))
continue;
- int nDepth = pcoin->GetDepthInMainChain();
+ int nDepth = pcoin->GetDepthInMainChain(locked_chain);
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
continue;
@@ -3407,7 +3449,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr))
continue;
- CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue;
+ CAmount n = IsSpent(locked_chain, walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue;
if (!balances.count(addr))
balances[addr] = 0;
@@ -3632,7 +3674,7 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
/** @} */ // end of Actions
-void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const {
+void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CTxDestination, int64_t> &mapKeyBirth) const {
AssertLockHeld(cs_wallet); // mapKeyMetadata
mapKeyBirth.clear();
@@ -3812,7 +3854,7 @@ void CWallet::MarkPreSplitKeys()
}
}
-bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string& error_string, std::string& warning_string)
+bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::string& warning_string)
{
// Do some checking on wallet path. It should be either a:
//
@@ -3821,25 +3863,23 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
// 3. Path to a symlink to a directory.
// 4. For backwards compatibility, the name of a data file in -walletdir.
LOCK(cs_wallets);
- fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
+ const fs::path& wallet_path = location.GetPath();
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(wallet_file).filename() == wallet_file))) {
+ (path_type == fs::regular_file && fs::path(location.GetName()).filename() == location.GetName()))) {
error_string = 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)",
- wallet_file, GetWalletDir());
+ location.GetName(), GetWalletDir());
return false;
}
// Make sure that the wallet path doesn't clash with an existing wallet path
- for (auto wallet : GetWallets()) {
- if (fs::absolute(wallet->GetName(), GetWalletDir()) == wallet_path) {
- error_string = strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", wallet_file);
- return false;
- }
+ if (IsWalletLoaded(wallet_path)) {
+ error_string = strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName());
+ return false;
}
try {
@@ -3847,13 +3887,13 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
return false;
}
} catch (const fs::filesystem_error& e) {
- error_string = strprintf("Error loading wallet %s. %s", wallet_file, e.what());
+ error_string = strprintf("Error loading wallet %s. %s", location.GetName(), fsbridge::get_filesystem_error_message(e));
return false;
}
if (salvage_wallet) {
// Recover readable keypairs:
- CWallet dummyWallet("dummy", WalletDatabase::CreateDummy());
+ CWallet dummyWallet(chain, WalletLocation(), WalletDatabase::CreateDummy());
std::string backup_filename;
if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
return false;
@@ -3863,9 +3903,9 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, error_string);
}
-std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path, uint64_t wallet_creation_flags)
+std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags)
{
- const std::string& walletFile = name;
+ const std::string& walletFile = location.GetName();
// needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
@@ -3873,7 +3913,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
- std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(name, WalletDatabase::Create(path));
+ std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(chain, location, WalletDatabase::Create(location.GetPath()));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DBErrors::LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
@@ -3887,7 +3927,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
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(name, WalletDatabase::Create(path)), ReleaseWallet);
+ std::shared_ptr<CWallet> walletInstance(new CWallet(chain, location, WalletDatabase::Create(location.GetPath())), ReleaseWallet);
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DBErrors::LOAD_OK)
{
@@ -3980,10 +4020,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
if (fFirstRun)
{
// ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key
- if (!gArgs.GetBoolArg("-usehd", true)) {
- InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile));
- return nullptr;
- }
walletInstance->SetMinVersion(FEATURE_LATEST);
if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
@@ -4001,6 +4037,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
return nullptr;
}
+ auto locked_chain = chain.assumeLocked(); // Temporary. Removed in upcoming lock cleanup
walletInstance->ChainStateFlushed(chainActive.GetLocator());
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation
@@ -4011,16 +4048,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
if (!walletInstance->mapKeys.empty() || !walletInstance->mapCryptedKeys.empty()) {
InitWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
}
- } else if (gArgs.IsArgSet("-usehd")) {
- bool useHD = gArgs.GetBoolArg("-usehd", true);
- if (walletInstance->IsHDEnabled() && !useHD) {
- InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile));
- return nullptr;
- }
- if (!walletInstance->IsHDEnabled() && useHD) {
- InitError(strprintf(_("Error loading %s: You can't enable HD on an already existing non-HD wallet"), walletFile));
- return nullptr;
- }
}
if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
@@ -4098,7 +4125,9 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
// Try to top up keypool. No-op if the wallet is locked.
walletInstance->TopUpKeyPool();
- LOCK(cs_main);
+ LockAnnotation lock(::cs_main); // Temporary, for FindForkInGlobalIndex below. Removed in upcoming commit.
+ auto locked_chain = chain.lock();
+ LOCK(walletInstance->cs_wallet);
CBlockIndex *pindexRescan = chainActive.Genesis();
if (!gArgs.GetBoolArg("-rescan", false))
@@ -4183,7 +4212,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
{
- LOCK(walletInstance->cs_wallet);
walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize());
walletInstance->WalletLogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
walletInstance->WalletLogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size());
@@ -4234,7 +4262,7 @@ void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
nIndex = posInBlock;
}
-int CMerkleTx::GetDepthInMainChain() const
+int CMerkleTx::GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const
{
if (hashUnset())
return 0;
@@ -4249,23 +4277,25 @@ int CMerkleTx::GetDepthInMainChain() const
return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);
}
-int CMerkleTx::GetBlocksToMaturity() const
+int CMerkleTx::GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const
{
if (!IsCoinBase())
return 0;
- int chain_depth = GetDepthInMainChain();
+ int chain_depth = GetDepthInMainChain(locked_chain);
assert(chain_depth >= 0); // coinbase tx should not be conflicted
return std::max(0, (COINBASE_MATURITY+1) - chain_depth);
}
-bool CMerkleTx::IsImmatureCoinBase() const
+bool CMerkleTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const
{
// note GetBlocksToMaturity is 0 for non-coinbase tx
- return GetBlocksToMaturity() > 0;
+ return GetBlocksToMaturity(locked_chain) > 0;
}
-bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
+bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, const CAmount& nAbsurdFee, CValidationState& state)
{
+ LockAnnotation lock(::cs_main); // Temporary, for AcceptToMemoryPool below. Removed in upcoming commit.
+
// We must set fInMempool here - while it will be re-set to true by the
// entered-mempool callback, if we did not there would be a race where a
// user could call sendmoney in a loop and hit spurious out of funds errors
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index da326517c0..4291163bea 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -7,20 +7,21 @@
#define BITCOIN_WALLET_WALLET_H
#include <amount.h>
+#include <interfaces/chain.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <streams.h>
#include <tinyformat.h>
#include <ui_interface.h>
-#include <utilstrencodings.h>
+#include <util/strencodings.h>
#include <validationinterface.h>
#include <script/ismine.h>
#include <script/sign.h>
-#include <util.h>
+#include <util/system.h>
#include <wallet/crypter.h>
#include <wallet/coinselection.h>
#include <wallet/walletdb.h>
-#include <wallet/rpcwallet.h>
+#include <wallet/walletutil.h>
#include <algorithm>
#include <atomic>
@@ -33,6 +34,26 @@
#include <utility>
#include <vector>
+//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
+// This function will perform salvage on the wallet if requested, as long as only one wallet is
+// being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
+bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
+
+//! Load wallet databases.
+bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
+
+//! Complete startup of wallets.
+void StartWallets(CScheduler& scheduler);
+
+//! Flush all wallets in preparation for shutdown.
+void FlushWallets();
+
+//! Stop all wallets. Wallets will be flushed first.
+void StopWallets();
+
+//! Close all wallets.
+void UnloadWallets();
+
bool AddWallet(const std::shared_ptr<CWallet>& wallet);
bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
bool HasWallets();
@@ -64,6 +85,9 @@ static const bool DEFAULT_WALLET_RBF = false;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;
+//! Pre-calculated constants for input size estimation in *virtual size*
+static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
+
class CBlockIndex;
class CCoinControl;
class COutput;
@@ -264,22 +288,22 @@ public:
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
- int GetDepthInMainChain() const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool IsInMainChain() const EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return GetDepthInMainChain() > 0; }
+ int GetDepthInMainChain(interfaces::Chain::Lock& locked_chain) const;
+ bool IsInMainChain(interfaces::Chain::Lock& locked_chain) const { return GetDepthInMainChain(locked_chain) > 0; }
/**
* @return number of blocks to maturity for this transaction:
* 0 : is not a coinbase transaction, or is a mature coinbase transaction
* >0 : is a coinbase transaction which matures in this many blocks
*/
- int GetBlocksToMaturity() const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ int GetBlocksToMaturity(interfaces::Chain::Lock& locked_chain) const;
bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); }
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; }
const uint256& GetHash() const { return tx->GetHash(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
- bool IsImmatureCoinBase() const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const;
};
//Get the marginal bytes of spending the specified output
@@ -458,10 +482,14 @@ public:
//! filter decides which addresses will count towards the debit
CAmount GetDebit(const isminefilter& filter) const;
- CAmount GetCredit(const isminefilter& filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- CAmount GetImmatureCredit(bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ CAmount GetCredit(interfaces::Chain::Lock& locked_chain, const isminefilter& filter) const;
+ CAmount GetImmatureCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true) const;
+ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(cs_main, pwallet->cs_wallet)". The
+ // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
+ // having to resolve the issue of member access into incomplete type CWallet.
+ CAmount GetAvailableCredit(interfaces::Chain::Lock& locked_chain, bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
+ CAmount GetImmatureWatchOnlyCredit(interfaces::Chain::Lock& locked_chain, const bool fUseCache=true) const;
CAmount GetChange() const;
// Get the marginal bytes if spending the specified output from this transaction
@@ -482,17 +510,23 @@ public:
bool IsEquivalentTo(const CWalletTx& tx) const;
bool InMempool() const;
- bool IsTrusted() const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool IsTrusted(interfaces::Chain::Lock& locked_chain) const;
int64_t GetTxTime() const;
// RelayWalletTransaction may only be called if fBroadcastTransactions!
- bool RelayWalletTransaction(CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain, CConnman* connman);
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
- bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
- std::set<uint256> GetConflicts() const;
+ bool AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, const CAmount& nAbsurdFee, CValidationState& state);
+
+ // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
+ // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
+ // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
+ // resolve the issue of member access into incomplete type CWallet. Note
+ // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
+ // in place.
+ std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
};
class COutput
@@ -591,13 +625,13 @@ private:
std::mutex mutexScanning;
friend class WalletRescanReserver;
- WalletBatch *encrypted_batch = nullptr;
+ WalletBatch *encrypted_batch GUARDED_BY(cs_wallet) = nullptr;
//! the current wallet version: clients below this version are not able to load the wallet
int nWalletVersion = FEATURE_BASE;
//! the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded
- int nWalletMaxVersion = FEATURE_BASE;
+ int nWalletMaxVersion GUARDED_BY(cs_wallet) = FEATURE_BASE;
int64_t nNextResend = 0;
int64_t nLastResend = 0;
@@ -609,9 +643,9 @@ private:
* mutated transactions where the mutant gets mined).
*/
typedef std::multimap<COutPoint, uint256> TxSpends;
- TxSpends mapTxSpends;
- void AddToSpends(const COutPoint& outpoint, const uint256& wtxid);
- void AddToSpends(const uint256& wtxid);
+ TxSpends mapTxSpends GUARDED_BY(cs_wallet);
+ void AddToSpends(const COutPoint& outpoint, const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void AddToSpends(const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Add a transaction to the wallet, or update it. pIndex and posInBlock should
@@ -632,9 +666,9 @@ private:
void MarkConflicted(const uint256& hashBlock, const uint256& hashTx);
/* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
- void MarkInputsDirty(const CTransactionRef& tx);
+ void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
+ void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
* Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */
@@ -647,13 +681,13 @@ private:
void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::set<int64_t> setInternalKeyPool;
- std::set<int64_t> setExternalKeyPool;
+ std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_wallet);
std::set<int64_t> set_pre_split_keypool;
- int64_t m_max_keypool_index = 0;
+ int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0;
std::map<CKeyID, int64_t> m_pool_key_to_index;
std::atomic<uint64_t> m_wallet_flags{0};
- int64_t nTimeFirstKey = 0;
+ int64_t nTimeFirstKey GUARDED_BY(cs_wallet) = 0;
/**
* Private version of AddWatchOnly method which does not accept a
@@ -666,12 +700,11 @@ private:
*/
bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- /**
- * Wallet filename from wallet=<path> command line or config option.
- * Used in debug logs and to send RPCs to the right wallet instance when
- * more than one wallet is loaded.
- */
- std::string m_name;
+ /** Interface for accessing chain state. */
+ interfaces::Chain& m_chain;
+
+ /** Wallet location which includes wallet name (see WalletLocation). */
+ WalletLocation m_location;
/** Internal database handle. */
std::unique_ptr<WalletDatabase> database;
@@ -709,27 +742,29 @@ public:
* if they are not ours
*/
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;
+ 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_name; }
+ const std::string& GetName() const { return m_location.GetName(); }
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void MarkPreSplitKeys();
+ void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
// Map from Key ID to key metadata.
- std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
+ std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_wallet);
// Map from Script ID to key metadata (for watch-only keys).
- std::map<CScriptID, CKeyMetadata> m_script_metadata;
+ std::map<CScriptID, CKeyMetadata> m_script_metadata GUARDED_BY(cs_wallet);
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID = 0;
/** Construct wallet with specified name and database implementation. */
- CWallet(std::string name, std::unique_ptr<WalletDatabase> database) : m_name(std::move(name)), database(std::move(database))
+ CWallet(interfaces::Chain& chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database) : m_chain(chain), m_location(location), database(std::move(database))
{
}
@@ -739,17 +774,20 @@ public:
encrypted_batch = nullptr;
}
- std::map<uint256, CWalletTx> mapWallet;
+ std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);
typedef std::multimap<int64_t, CWalletTx*> TxItems;
TxItems wtxOrdered;
- int64_t nOrderPosNext = 0;
+ int64_t nOrderPosNext GUARDED_BY(cs_wallet) = 0;
uint64_t nAccountingEntryNumber = 0;
std::map<CTxDestination, CAddressBookData> mapAddressBook;
- std::set<COutPoint> setLockedCoins;
+ std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet);
+
+ /** Interface for accessing chain state. */
+ interfaces::Chain& chain() const { return m_chain; }
const CWalletTx* GetWalletTx(const uint256& hash) const;
@@ -759,17 +797,17 @@ public:
/**
* populate vCoins with vector of available COutputs.
*/
- void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet);
+ void AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Return list of available coins and locked coins grouped by non-change output address.
*/
- std::map<CTxDestination, std::vector<COutput>> ListCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet);
+ std::map<CTxDestination, std::vector<COutput>> ListCoins(interfaces::Chain::Lock& locked_chain) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Find non-change parent output.
*/
- const CTxOut& FindNonChangeParentOutput(const CTransaction& tx, int output) const;
+ const CTxOut& FindNonChangeParentOutput(const CTransaction& tx, int output) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Shuffle and select coins until nTargetValue is reached while avoiding
@@ -780,7 +818,7 @@ public:
bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
- bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool IsSpent(interfaces::Chain::Lock& locked_chain, const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -844,7 +882,7 @@ public:
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
- void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<CTxDestination, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int ComputeTimeSmart(const CWalletTx& wtx) const;
/**
@@ -856,7 +894,7 @@ public:
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
- void LoadToWallet(const CWalletTx& wtxIn);
+ void LoadToWallet(const CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void TransactionAddedToMempool(const CTransactionRef& tx) override;
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
@@ -866,7 +904,7 @@ public:
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
- std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ std::vector<uint256> ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime, CConnman* connman);
CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
CAmount GetUnconfirmedBalance() const;
CAmount GetImmatureBalance() const;
@@ -889,7 +927,7 @@ public:
* selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random position
*/
- bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
+ bool CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
@@ -948,7 +986,7 @@ public:
const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; }
std::set<std::set<CTxDestination>> GetAddressGroupings() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- std::map<CTxDestination, CAmount> GetAddressBalances() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ std::map<CTxDestination, CAmount> GetAddressBalances(interfaces::Chain::Lock& locked_chain);
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
@@ -961,6 +999,7 @@ public:
isminetype IsMine(const CTxOut& txout) const;
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;
/** should probably be renamed to IsRelevantToMe */
@@ -1000,7 +1039,7 @@ public:
int GetVersion() { LOCK(cs_wallet); return nWalletVersion; }
//! Get wallet transactions that conflict with given transaction (spend same outputs)
- std::set<uint256> GetConflicts(const uint256& txid) const;
+ std::set<uint256> GetConflicts(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Check if a given transaction has any of its outputs spent by another transaction in the wallet
bool HasWalletSpend(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -1042,16 +1081,16 @@ public:
bool TransactionCanBeAbandoned(const uint256& hashTx) const;
/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
- bool AbandonTransaction(const uint256& hashTx);
+ bool AbandonTransaction(interfaces::Chain::Lock& locked_chain, const uint256& hashTx);
/** 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(std::string wallet_file, bool salvage_wallet, std::string& error_string, std::string& warning_string);
+ static bool Verify(interfaces::Chain& chain, const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::string& warning_string);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
- static std::shared_ptr<CWallet> CreateWalletFromFile(const std::string& name, const fs::path& path, uint64_t wallet_creation_flags = 0);
+ static std::shared_ptr<CWallet> CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags = 0);
/**
* Wallet post-init setup
@@ -1198,6 +1237,6 @@ public:
// Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
// be IsAllFromMe).
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false);
+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);
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 5e85151358..09a33f252c 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -12,8 +12,8 @@
#include <protocol.h>
#include <serialize.h>
#include <sync.h>
-#include <util.h>
-#include <utiltime.h>
+#include <util/system.h>
+#include <util/time.h>
#include <wallet/wallet.h>
#include <atomic>
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index 34c76ec4c4..6db4c63acb 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -4,6 +4,8 @@
#include <wallet/walletutil.h>
+#include <util/system.h>
+
fs::path GetWalletDir()
{
fs::path path;
@@ -25,3 +27,67 @@ fs::path GetWalletDir()
return path;
}
+
+static bool IsBerkeleyBtree(const fs::path& path)
+{
+ // A Berkeley DB Btree file has at least 4K.
+ // This check also prevents opening lock files.
+ boost::system::error_code ec;
+ if (fs::file_size(path, ec) < 4096) return false;
+
+ fs::ifstream file(path.string(), std::ios::binary);
+ if (!file.is_open()) return false;
+
+ file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
+ uint32_t data = 0;
+ file.read((char*) &data, sizeof(data)); // Read 4 bytes of file to compare against magic
+
+ // Berkeley DB Btree magic bytes, from:
+ // https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75
+ // - big endian systems - 00 05 31 62
+ // - little endian systems - 62 31 05 00
+ return data == 0x00053162 || data == 0x62310500;
+}
+
+std::vector<fs::path> ListWalletDir()
+{
+ const fs::path wallet_dir = GetWalletDir();
+ const size_t offset = wallet_dir.string().size() + 1;
+ std::vector<fs::path> paths;
+
+ for (auto it = fs::recursive_directory_iterator(wallet_dir); it != fs::recursive_directory_iterator(); ++it) {
+ // Get wallet path relative to walletdir by removing walletdir from the wallet path.
+ // This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
+ const fs::path path = it->path().string().substr(offset);
+
+ if (it->status().type() == fs::directory_file && IsBerkeleyBtree(it->path() / "wallet.dat")) {
+ // Found a directory which contains wallet.dat btree file, add it as a wallet.
+ paths.emplace_back(path);
+ } else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && IsBerkeleyBtree(it->path())) {
+ if (it->path().filename() == "wallet.dat") {
+ // Found top-level wallet.dat btree file, add top level directory ""
+ // as a wallet.
+ paths.emplace_back();
+ } else {
+ // Found top-level btree file not called wallet.dat. Current bitcoin
+ // software will never create these files but will allow them to be
+ // opened in a shared database environment for backwards compatibility.
+ // Add it to the list of available wallets.
+ paths.emplace_back(path);
+ }
+ }
+ }
+
+ return paths;
+}
+
+WalletLocation::WalletLocation(const std::string& name)
+ : m_name(name)
+ , m_path(fs::absolute(name, GetWalletDir()))
+{
+}
+
+bool WalletLocation::Exists() const
+{
+ return fs::symlink_status(m_path).type() != fs::file_not_found;
+}
diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h
index 7c42954616..ba2f913841 100644
--- a/src/wallet/walletutil.h
+++ b/src/wallet/walletutil.h
@@ -5,10 +5,34 @@
#ifndef BITCOIN_WALLET_WALLETUTIL_H
#define BITCOIN_WALLET_WALLETUTIL_H
-#include <chainparamsbase.h>
-#include <util.h>
+#include <fs.h>
+
+#include <vector>
//! Get the path of the wallet directory.
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;
+};
+
#endif // BITCOIN_WALLET_WALLETUTIL_H
diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h
index e955816162..22aca65990 100644
--- a/src/walletinitinterface.h
+++ b/src/walletinitinterface.h
@@ -9,29 +9,22 @@
class CScheduler;
class CRPCTable;
+struct InitInterfaces;
class WalletInitInterface {
public:
+ /** Is the wallet component enabled */
+ virtual bool HasWalletSupport() const = 0;
/** Get wallet help string */
virtual void AddWalletOptions() const = 0;
/** Check wallet parameter interaction */
virtual bool ParameterInteraction() const = 0;
- /** Register wallet RPC*/
- virtual void RegisterRPC(CRPCTable &) const = 0;
- /** Verify wallets */
- virtual bool Verify() const = 0;
- /** Open wallets*/
- virtual bool Open() const = 0;
- /** Start wallets*/
- virtual void Start(CScheduler& scheduler) const = 0;
- /** Flush Wallets*/
- virtual void Flush() const = 0;
- /** Stop Wallets*/
- virtual void Stop() const = 0;
- /** Close wallets */
- virtual void Close() const = 0;
+ /** Add wallets that should be opened to list of init interfaces. */
+ virtual void Construct(InitInterfaces& interfaces) const = 0;
virtual ~WalletInitInterface() {}
};
+extern const WalletInitInterface& g_wallet_init_interface;
+
#endif // BITCOIN_WALLETINITINTERFACE_H
diff --git a/src/warnings.cpp b/src/warnings.cpp
index 9f10c48eef..1c6ba13f60 100644
--- a/src/warnings.cpp
+++ b/src/warnings.cpp
@@ -5,7 +5,7 @@
#include <sync.h>
#include <clientversion.h>
-#include <util.h>
+#include <util/system.h>
#include <warnings.h>
CCriticalSection cs_warnings;
diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp
index 39cc8968d2..6a9661e3e8 100644
--- a/src/zmq/zmqabstractnotifier.cpp
+++ b/src/zmq/zmqabstractnotifier.cpp
@@ -3,8 +3,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <zmq/zmqabstractnotifier.h>
-#include <util.h>
+#include <util/system.h>
+const int CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM;
CZMQAbstractNotifier::~CZMQAbstractNotifier()
{
diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h
index 5081c6cd02..887dde7b27 100644
--- a/src/zmq/zmqabstractnotifier.h
+++ b/src/zmq/zmqabstractnotifier.h
@@ -15,7 +15,9 @@ typedef CZMQAbstractNotifier* (*CZMQNotifierFactory)();
class CZMQAbstractNotifier
{
public:
- CZMQAbstractNotifier() : psocket(nullptr) { }
+ static const int DEFAULT_ZMQ_SNDHWM {1000};
+
+ CZMQAbstractNotifier() : psocket(nullptr), outbound_message_high_water_mark(DEFAULT_ZMQ_SNDHWM) { }
virtual ~CZMQAbstractNotifier();
template <typename T>
@@ -28,6 +30,12 @@ public:
void SetType(const std::string &t) { type = t; }
std::string GetAddress() const { return address; }
void SetAddress(const std::string &a) { address = a; }
+ int GetOutboundMessageHighWaterMark() const { return outbound_message_high_water_mark; }
+ void SetOutboundMessageHighWaterMark(const int sndhwm) {
+ if (sndhwm >= 0) {
+ outbound_message_high_water_mark = sndhwm;
+ }
+ }
virtual bool Initialize(void *pcontext) = 0;
virtual void Shutdown() = 0;
@@ -39,6 +47,7 @@ protected:
void *psocket;
std::string type;
std::string address;
+ int outbound_message_high_water_mark; // aka SNDHWM
};
#endif // BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index 8cbc969972..6826cf62d6 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -8,7 +8,7 @@
#include <version.h>
#include <validation.h>
#include <streams.h>
-#include <util.h>
+#include <util/system.h>
void zmqError(const char *str)
{
@@ -59,6 +59,7 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create()
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);
}
}
@@ -81,10 +82,14 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create()
// Called at startup to conditionally set up ZMQ socket(s)
bool CZMQNotificationInterface::Initialize()
{
+ int major = 0, minor = 0, patch = 0;
+ zmq_version(&major, &minor, &patch);
+ LogPrint(BCLog::ZMQ, "zmq: version %d.%d.%d\n", major, minor, patch);
+
LogPrint(BCLog::ZMQ, "zmq: Initialize notification interface\n");
assert(!pcontext);
- pcontext = zmq_init(1);
+ pcontext = zmq_ctx_new();
if (!pcontext)
{
@@ -98,11 +103,11 @@ bool CZMQNotificationInterface::Initialize()
CZMQAbstractNotifier *notifier = *i;
if (notifier->Initialize(pcontext))
{
- LogPrint(BCLog::ZMQ, " Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress());
+ LogPrint(BCLog::ZMQ, "zmq: Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress());
}
else
{
- LogPrint(BCLog::ZMQ, " Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress());
+ LogPrint(BCLog::ZMQ, "zmq: Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress());
break;
}
}
@@ -124,10 +129,10 @@ void CZMQNotificationInterface::Shutdown()
for (std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin(); i!=notifiers.end(); ++i)
{
CZMQAbstractNotifier *notifier = *i;
- LogPrint(BCLog::ZMQ, " Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress());
+ LogPrint(BCLog::ZMQ, "zmq: Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress());
notifier->Shutdown();
}
- zmq_ctx_destroy(pcontext);
+ zmq_ctx_term(pcontext);
pcontext = nullptr;
}
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index 36a6458f67..15d4ac1b89 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -7,7 +7,7 @@
#include <streams.h>
#include <zmq/zmqpublishnotifier.h>
#include <validation.h>
-#include <util.h>
+#include <util/system.h>
#include <rpc/server.h>
static std::multimap<std::string, CZMQAbstractPublishNotifier*> mapPublishNotifiers;
@@ -76,8 +76,18 @@ bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)
return false;
}
- int rc = zmq_bind(psocket, address.c_str());
- if (rc!=0)
+ LogPrint(BCLog::ZMQ, "zmq: Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);
+
+ int rc = zmq_setsockopt(psocket, ZMQ_SNDHWM, &outbound_message_high_water_mark, sizeof(outbound_message_high_water_mark));
+ if (rc != 0)
+ {
+ zmqError("Failed to set outbound message high water mark");
+ zmq_close(psocket);
+ return false;
+ }
+
+ rc = zmq_bind(psocket, address.c_str());
+ if (rc != 0)
{
zmqError("Failed to bind address");
zmq_close(psocket);
@@ -120,7 +130,7 @@ void CZMQAbstractPublishNotifier::Shutdown()
if (count == 1)
{
- LogPrint(BCLog::ZMQ, "Close socket at address %s\n", address);
+ LogPrint(BCLog::ZMQ, "zmq: Close socket at address %s\n", address);
int linger = 0;
zmq_setsockopt(psocket, ZMQ_LINGER, &linger, sizeof(linger));
zmq_close(psocket);
diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h
index 0f5b43d726..278fdb94d2 100644
--- a/src/zmq/zmqpublishnotifier.h
+++ b/src/zmq/zmqpublishnotifier.h
@@ -12,7 +12,7 @@ class CBlockIndex;
class CZMQAbstractPublishNotifier : public CZMQAbstractNotifier
{
private:
- uint32_t nSequence; //!< upcounting per message sequence number
+ uint32_t nSequence {0U}; //!< upcounting per message sequence number
public:
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
index 4f88bf4eb9..d3eab46e5f 100644
--- a/src/zmq/zmqrpc.cpp
+++ b/src/zmq/zmqrpc.cpp
@@ -5,6 +5,7 @@
#include <zmq/zmqrpc.h>
#include <rpc/server.h>
+#include <rpc/util.h>
#include <zmq/zmqabstractnotifier.h>
#include <zmq/zmqnotificationinterface.h>
@@ -16,13 +17,15 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
- "getzmqnotifications\n"
- "\nReturns information about the active ZeroMQ notifications.\n"
+ RPCHelpMan{"getzmqnotifications",
+ "\nReturns information about the active ZeroMQ notifications.\n", {}}
+ .ToString() +
"\nResult:\n"
"[\n"
" { (json object)\n"
" \"type\": \"pubhashtx\", (string) Type of notification\n"
- " \"address\": \"...\" (string) Address of the publisher\n"
+ " \"address\": \"...\", (string) Address of the publisher\n"
+ " \"hwm\": n (numeric) Outbound message high water mark\n"
" },\n"
" ...\n"
"]\n"
@@ -38,6 +41,7 @@ UniValue getzmqnotifications(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
obj.pushKV("type", n->GetType());
obj.pushKV("address", n->GetAddress());
+ obj.pushKV("hwm", n->GetOutboundMessageHighWaterMark());
result.push_back(obj);
}
}