aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcontrib/zmq/zmq_sub.py19
-rw-r--r--depends/funcs.mk2
-rw-r--r--depends/packages/qt.mk3
-rw-r--r--depends/patches/qt/fix_powerpc_libpng.patch23
-rw-r--r--doc/bips.md1
-rw-r--r--doc/developer-notes.md19
-rw-r--r--doc/release-notes.md4
-rw-r--r--doc/zmq.md29
-rw-r--r--src/bitcoin-cli.cpp10
-rw-r--r--src/bitcoin-tx.cpp5
-rw-r--r--src/bitcoin-wallet.cpp2
-rw-r--r--src/bitcoind.cpp6
-rw-r--r--src/init.cpp6
-rw-r--r--src/interfaces/chain.cpp10
-rw-r--r--src/interfaces/chain.h4
-rw-r--r--src/net.h7
-rw-r--r--src/net_processing.cpp21
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/test/apptests.cpp7
-rw-r--r--src/rest.cpp4
-rw-r--r--src/rpc/blockchain.cpp342
-rw-r--r--src/rpc/blockchain.h2
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/net.cpp148
-rw-r--r--src/rpc/rawtransaction.cpp183
-rw-r--r--src/signet.cpp24
-rw-r--r--src/signet.h13
-rw-r--r--src/sync.h14
-rw-r--r--src/test/fuzz/signet.cpp6
-rw-r--r--src/test/util/setup_common.cpp2
-rw-r--r--src/txmempool.cpp6
-rw-r--r--src/txmempool.h14
-rw-r--r--src/validation.cpp88
-rw-r--r--src/validation.h38
-rw-r--r--src/validationinterface.cpp12
-rw-r--r--src/validationinterface.h9
-rw-r--r--src/wallet/rpcwallet.cpp483
-rw-r--r--src/wallet/rpcwallet.h4
-rw-r--r--src/wallet/test/wallet_tests.cpp6
-rw-r--r--src/wallet/wallet.cpp6
-rw-r--r--src/wallet/wallet.h4
-rw-r--r--src/zmq/zmqabstractnotifier.cpp20
-rw-r--r--src/zmq/zmqabstractnotifier.h10
-rw-r--r--src/zmq/zmqnotificationinterface.cpp41
-rw-r--r--src/zmq/zmqnotificationinterface.h3
-rw-r--r--src/zmq/zmqpublishnotifier.cpp49
-rw-r--r--src/zmq/zmqpublishnotifier.h9
-rw-r--r--test/functional/README.md24
-rwxr-xr-xtest/functional/example_test.py12
-rwxr-xr-xtest/functional/feature_block.py6
-rwxr-xr-xtest/functional/feature_cltv.py14
-rwxr-xr-xtest/functional/feature_csv_activation.py4
-rwxr-xr-xtest/functional/feature_dersig.py14
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py8
-rwxr-xr-xtest/functional/feature_signet.py13
-rwxr-xr-xtest/functional/feature_versionbits_warning.py6
-rwxr-xr-xtest/functional/interface_zmq.py320
-rwxr-xr-xtest/functional/mempool_packages.py4
-rwxr-xr-xtest/functional/mining_basic.py6
-rwxr-xr-xtest/functional/p2p_blocksonly.py34
-rwxr-xr-xtest/functional/p2p_dos_header_tree.py16
-rwxr-xr-xtest/functional/p2p_filter.py2
-rwxr-xr-xtest/functional/p2p_getdata.py2
-rwxr-xr-xtest/functional/p2p_invalid_block.py14
-rwxr-xr-xtest/functional/p2p_invalid_locator.py14
-rwxr-xr-xtest/functional/p2p_invalid_tx.py12
-rwxr-xr-xtest/functional/p2p_segwit.py4
-rwxr-xr-xtest/functional/rpc_blockchain.py4
-rwxr-xr-xtest/functional/test_framework/test_node.py9
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py10
70 files changed, 1488 insertions, 785 deletions
diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py
index 06893407f5..8b8503331d 100755
--- a/contrib/zmq/zmq_sub.py
+++ b/contrib/zmq/zmq_sub.py
@@ -11,7 +11,8 @@
-zmqpubrawtx=tcp://127.0.0.1:28332 \
-zmqpubrawblock=tcp://127.0.0.1:28332 \
-zmqpubhashtx=tcp://127.0.0.1:28332 \
- -zmqpubhashblock=tcp://127.0.0.1:28332
+ -zmqpubhashblock=tcp://127.0.0.1:28332 \
+ -zmqpubsequence=tcp://127.0.0.1:28332
We use the asyncio library here. `self.handle()` installs itself as a
future at the end of the function. Since it never returns with the event
@@ -47,16 +48,14 @@ class ZMQHandler():
self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "hashtx")
self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "rawblock")
self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "rawtx")
+ self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "sequence")
self.zmqSubSocket.connect("tcp://127.0.0.1:%i" % port)
async def handle(self) :
- msg = await self.zmqSubSocket.recv_multipart()
- topic = msg[0]
- body = msg[1]
+ topic, body, seq = await self.zmqSubSocket.recv_multipart()
sequence = "Unknown"
- if len(msg[-1]) == 4:
- msgSequence = struct.unpack('<I', msg[-1])[-1]
- sequence = str(msgSequence)
+ if len(seq) == 4:
+ sequence = str(struct.unpack('<I', seq)[-1])
if topic == b"hashblock":
print('- HASH BLOCK ('+sequence+') -')
print(binascii.hexlify(body))
@@ -69,6 +68,12 @@ class ZMQHandler():
elif topic == b"rawtx":
print('- RAW TX ('+sequence+') -')
print(binascii.hexlify(body))
+ elif topic == b"sequence":
+ hash = binascii.hexlify(body[:32])
+ label = chr(body[32])
+ mempool_sequence = None if len(body) != 32+1+8 else struct.unpack("<Q", body[32+1:])[0]
+ print('- SEQUENCE ('+sequence+') -')
+ print(hash, label, mempool_sequence)
# schedule ourselves to receive the next message
asyncio.ensure_future(self.handle())
diff --git a/depends/funcs.mk b/depends/funcs.mk
index 81ecbedf5c..58d882eb05 100644
--- a/depends/funcs.mk
+++ b/depends/funcs.mk
@@ -265,4 +265,4 @@ $(foreach package,$(all_packages),$(eval $(call int_config_attach_build_config,$
$(foreach package,$(all_packages),$(eval $(call int_add_cmds,$(package))))
#special exception: if a toolchain package exists, all non-native packages depend on it
-$(foreach package,$(packages),$(eval $($(package)_unpacked): |$($($(host_arch)_$(host_os)_native_toolchain)_cached) $($($(host_arch)_$(host_os)_native_binutils)_cached) ))
+$(foreach package,$(packages),$(eval $($(package)_extracted): |$($($(host_arch)_$(host_os)_native_toolchain)_cached) $($($(host_arch)_$(host_os)_native_binutils)_cached) ))
diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk
index f560099b6a..083bc68d66 100644
--- a/depends/packages/qt.mk
+++ b/depends/packages/qt.mk
@@ -11,7 +11,7 @@ $(package)_qt_libs=corelib network widgets gui plugins testlib
$(package)_patches=fix_qt_pkgconfig.patch mac-qmake.conf fix_configure_mac.patch fix_no_printer.patch
$(package)_patches+= fix_rcc_determinism.patch fix_riscv64_arch.patch xkb-default.patch no-xlib.patch
$(package)_patches+= fix_android_qmake_conf.patch fix_android_jni_static.patch dont_hardcode_pwd.patch
-$(package)_patches+= freetype_back_compat.patch drop_lrelease_dependency.patch
+$(package)_patches+= freetype_back_compat.patch drop_lrelease_dependency.patch fix_powerpc_libpng.patch
# Update OSX_QT_TRANSLATIONS when this is updated
$(package)_qttranslations_file_name=qttranslations-$($(package)_suffix)
@@ -194,6 +194,7 @@ endef
define $(package)_preprocess_cmds
patch -p1 -i $($(package)_patch_dir)/freetype_back_compat.patch && \
+ patch -p1 -i $($(package)_patch_dir)/fix_powerpc_libpng.patch && \
sed -i.old "s|updateqm.commands = \$$$$\$$$$LRELEASE|updateqm.commands = $($(package)_extract_dir)/qttools/bin/lrelease|" qttranslations/translations/translations.pro && \
patch -p1 -i $($(package)_patch_dir)/drop_lrelease_dependency.patch && \
patch -p1 -i $($(package)_patch_dir)/dont_hardcode_pwd.patch &&\
diff --git a/depends/patches/qt/fix_powerpc_libpng.patch b/depends/patches/qt/fix_powerpc_libpng.patch
new file mode 100644
index 0000000000..d37b6c7776
--- /dev/null
+++ b/depends/patches/qt/fix_powerpc_libpng.patch
@@ -0,0 +1,23 @@
+commit 6f9feb773a43c5abfa3455da2e324180e789285b
+Author: fanquake <fanquake@gmail.com>
+Date: Tue Sep 15 21:44:31 2020 +0800
+
+ Fix PowerPC build of libpng
+
+ See https://bugreports.qt.io/browse/QTBUG-66388.
+
+ Can be dropped when we are building qt 5.12.0 or later.
+
+diff --git a/qtbase/src/3rdparty/libpng/libpng.pro b/qtbase/src/3rdparty/libpng/libpng.pro
+index 577b61d8..a2f56669 100644
+--- a/qtbase/src/3rdparty/libpng/libpng.pro
++++ b/qtbase/src/3rdparty/libpng/libpng.pro
+@@ -10,7 +10,7 @@ MODULE_INCLUDEPATH = $$PWD
+
+ load(qt_helper_lib)
+
+-DEFINES += PNG_ARM_NEON_OPT=0
++DEFINES += PNG_ARM_NEON_OPT=0 PNG_POWERPC_VSX_OPT=0
+ SOURCES += \
+ png.c \
+ pngerror.c \
diff --git a/doc/bips.md b/doc/bips.md
index 456fea7a5a..2d099b9626 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -42,4 +42,5 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
* [`BIP 173`](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki): Bech32 addresses for native Segregated Witness outputs are supported as of **v0.16.0** ([PR 11167](https://github.com/bitcoin/bitcoin/pull/11167)). Bech32 addresses are generated by default as of **v0.20.0** ([PR 16884](https://github.com/bitcoin/bitcoin/pull/16884)).
* [`BIP 174`](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki): RPCs to operate on Partially Signed Bitcoin Transactions (PSBT) are present as of **v0.17.0** ([PR 13557](https://github.com/bitcoin/bitcoin/pull/13557)).
* [`BIP 176`](https://github.com/bitcoin/bips/blob/master/bip-0176.mediawiki): Bits Denomination [QT only] is supported as of **v0.16.0** ([PR 12035](https://github.com/bitcoin/bitcoin/pull/12035)).
+* [`BIP 325`](https://github.com/bitcoin/bips/blob/master/bip-0325.mediawiki): Signet test network is supported as of **v0.21.0** ([PR 18267](https://github.com/bitcoin/bitcoin/pull/18267)).
* [`BIP 339`](https://github.com/bitcoin/bips/blob/master/bip-0339.mediawiki): Relay of transactions by wtxid is supported as of **v0.21.0** ([PR 18044](https://github.com/bitcoin/bitcoin/pull/18044)).
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index ef9ecbb085..fa188dbcd6 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -793,25 +793,6 @@ bool ChainstateManager::ProcessNewBlock(...)
}
```
-- When Clang Thread Safety Analysis is unable to determine if a mutex is locked, use `LockAssertion` class instances:
-
-```C++
-// net_processing.h
-void RelayTransaction(...) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
-
-// net_processing.cpp
-void RelayTransaction(...)
-{
- AssertLockHeld(::cs_main);
-
- connman.ForEachNode([&txid, &wtxid](CNode* pnode) {
- LockAssertion lock(::cs_main);
- ...
- });
-}
-
-```
-
- Build and run tests with `-DDEBUG_LOCKORDER` to verify that no potential
deadlocks are introduced. As of 0.12, this is defined by default when
configuring with `--enable-debug`.
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 059c8cd029..1580bbd9ae 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -346,6 +346,10 @@ RPC
Tests
-----
+- The BIP 325 default signet can be enabled by the `-chain=signet` or `-signet`
+ setting. The settings `-signetchallenge` and `-signetseednode` allow
+ enabling a custom signet.
+
Credits
=======
diff --git a/doc/zmq.md b/doc/zmq.md
index 835dea23f2..f003c90d3a 100644
--- a/doc/zmq.md
+++ b/doc/zmq.md
@@ -63,6 +63,7 @@ Currently, the following notifications are supported:
-zmqpubhashblock=address
-zmqpubrawblock=address
-zmqpubrawtx=address
+ -zmqpubsequence=address
The socket type is PUB and the address must be a valid ZeroMQ socket
address. The same address can be used in more than one notification.
@@ -74,6 +75,7 @@ The option to set the PUB socket's outbound message high water mark
-zmqpubhashblockhwm=n
-zmqpubrawblockhwm=n
-zmqpubrawtxhwm=n
+ -zmqpubsequencehwm=address
The high water mark value must be an integer greater than or equal to 0.
@@ -87,7 +89,15 @@ Each PUB notification has a topic and body, where the header
corresponds to the notification type. For instance, for the
notification `-zmqpubhashtx` the topic is `hashtx` (no null
terminator) and the body is the transaction hash (32
-bytes).
+bytes) for all but `sequence` topic. For `sequence`, the body
+is structured as the following based on the type of message:
+
+ <32-byte hash>C : Blockhash connected
+ <32-byte hash>D : Blockhash disconnected
+ <32-byte hash>R<8-byte LE uint> : Transactionhash removed from mempool for non-block inclusion reason
+ <32-byte hash>A<8-byte LE uint> : Transactionhash added mempool
+
+Where the 8-byte uints correspond to the mempool sequence number.
These options can also be provided in bitcoin.conf.
@@ -124,13 +134,20 @@ No authentication or authorization is done on connecting clients; it
is assumed that the ZeroMQ port is exposed only to trusted entities,
using other means such as firewalling.
-Note that when the block chain tip changes, a reorganisation may occur
-and just the tip will be notified. It is up to the subscriber to
-retrieve the chain from the last known block to the new tip. Also note
-that no notification occurs if the tip was in the active chain - this
-is the case after calling invalidateblock RPC.
+Note that for `*block` topics, when the block chain tip changes,
+a reorganisation may occur and just the tip will be notified.
+It is up to the subscriber to retrieve the chain from the last known
+block to the new tip. Also note that no notification will occur if the tip
+was in the active chain--as would be the case after calling invalidateblock RPC.
+In contrast, the `sequence` topic publishes all block connections and
+disconnections.
There are several possibilities that ZMQ notification can get lost
during transmission depending on the communication type you are
using. Bitcoind appends an up-counting sequence number to each
notification which allows listeners to detect lost notifications.
+
+The `sequence` topic refers specifically to the mempool sequence
+number, which is also published along with all mempool events. This
+is a different sequence value than in ZMQ itself in order to allow a total
+ordering of mempool events to be constructed.
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 198cd5eb52..e94d4dff49 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -87,11 +87,6 @@ static void libevent_log_cb(int severity, const char *msg)
}
}
-//////////////////////////////////////////////////////////////////////////////
-//
-// Start
-//
-
//
// Exception thrown on connection error. This error is used to determine
// when to wait if -rpcwait is given.
@@ -112,9 +107,6 @@ public:
//
static int AppInitRPC(int argc, char* argv[])
{
- //
- // Parameters
- //
SetupCliArgs(gArgs);
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
@@ -147,7 +139,7 @@ static int AppInitRPC(int argc, char* argv[])
tfm::format(std::cerr, "Error reading configuration file: %s\n", error);
return EXIT_FAILURE;
}
- // Check for -chain, -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
+ // Check for chain settings (BaseParams() calls are only valid after this clause)
try {
SelectBaseParams(gArgs.GetChainName());
} catch (const std::exception& e) {
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index a9119d5144..085f1ecfda 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -78,9 +78,6 @@ static void SetupBitcoinTxArgs(ArgsManager &argsman)
//
static int AppInitRawTx(int argc, char* argv[])
{
- //
- // Parameters
- //
SetupBitcoinTxArgs(gArgs);
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
@@ -88,7 +85,7 @@ static int AppInitRawTx(int argc, char* argv[])
return EXIT_FAILURE;
}
- // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for chain settings (Params() calls are only valid after this clause)
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception& e) {
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 06b0c86476..8fdf1bae0f 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -62,7 +62,7 @@ static bool WalletAppInit(int argc, char* argv[])
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""));
return false;
}
- // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for chain settings (Params() calls are only valid after this clause)
SelectParams(gArgs.GetChainName());
return true;
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 02074f820a..455a82e390 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -37,10 +37,6 @@ static void WaitForShutdown(NodeContext& node)
Interrupt(node);
}
-//////////////////////////////////////////////////////////////////////////////
-//
-// Start
-//
static bool AppInit(int argc, char* argv[])
{
NodeContext node;
@@ -81,7 +77,7 @@ static bool AppInit(int argc, char* argv[])
if (!args.ReadConfigFiles(error, true)) {
return InitError(Untranslated(strprintf("Error reading configuration file: %s\n", error)));
}
- // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for chain settings (Params() calls are only valid after this clause)
try {
SelectParams(args.GetChainName());
} catch (const std::exception& e) {
diff --git a/src/init.cpp b/src/init.cpp
index 023aa9aba5..5ad807cbac 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -488,19 +488,23 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-zmqpubhashtx=<address>", "Enable publish hash transaction in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawblock=<address>", "Enable publish raw block in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawtx=<address>", "Enable publish raw transaction in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ argsman.AddArg("-zmqpubsequence=<address>", "Enable publish hash block and tx sequence in <address>", ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubhashblockhwm=<n>", strprintf("Set publish hash block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubhashtxhwm=<n>", strprintf("Set publish hash transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawblockhwm=<n>", strprintf("Set publish raw block outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
argsman.AddArg("-zmqpubrawtxhwm=<n>", strprintf("Set publish raw transaction outbound message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, OptionsCategory::ZMQ);
+ argsman.AddArg("-zmqpubsequencehwm=<n>", strprintf("Set publish hash sequence message high water mark (default: %d)", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM), ArgsManager::ALLOW_ANY, 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("-zmqpubsequence=<n>");
hidden_args.emplace_back("-zmqpubhashblockhwm=<n>");
hidden_args.emplace_back("-zmqpubhashtxhwm=<n>");
hidden_args.emplace_back("-zmqpubrawblockhwm=<n>");
hidden_args.emplace_back("-zmqpubrawtxhwm=<n>");
+ hidden_args.emplace_back("-zmqpubsequencehwm=<n>");
#endif
argsman.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
@@ -1559,7 +1563,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
chainman.m_total_coinstip_cache = nCoinCacheUsage;
chainman.m_total_coinsdb_cache = nCoinDBCache;
- UnloadBlockIndex(node.mempool.get());
+ UnloadBlockIndex(node.mempool.get(), chainman);
// new CBlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it first:
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
index 13d885a20d..4c5ebe66fc 100644
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -59,13 +59,13 @@ public:
explicit NotificationsProxy(std::shared_ptr<Chain::Notifications> notifications)
: m_notifications(std::move(notifications)) {}
virtual ~NotificationsProxy() = default;
- void TransactionAddedToMempool(const CTransactionRef& tx) override
+ void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) override
{
- m_notifications->transactionAddedToMempool(tx);
+ m_notifications->transactionAddedToMempool(tx, mempool_sequence);
}
- void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override
+ void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override
{
- m_notifications->transactionRemovedFromMempool(tx, reason);
+ m_notifications->transactionRemovedFromMempool(tx, reason, mempool_sequence);
}
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* index) override
{
@@ -405,7 +405,7 @@ public:
if (!m_node.mempool) return;
LOCK2(::cs_main, m_node.mempool->cs);
for (const CTxMemPoolEntry& entry : m_node.mempool->mapTx) {
- notifications.transactionAddedToMempool(entry.GetSharedTx());
+ notifications.transactionAddedToMempool(entry.GetSharedTx(), 0 /* mempool_sequence */);
}
}
NodeContext& m_node;
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 6e50ccb27a..85d09be0f3 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -242,8 +242,8 @@ public:
{
public:
virtual ~Notifications() {}
- virtual void transactionAddedToMempool(const CTransactionRef& tx) {}
- virtual void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {}
+ virtual void transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {}
+ virtual void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {}
virtual void blockConnected(const CBlock& block, int height) {}
virtual void blockDisconnected(const CBlock& block, int height) {}
virtual void updatedBlockTip() {}
diff --git a/src/net.h b/src/net.h
index 5e134ca7c8..5a8e57b68b 100644
--- a/src/net.h
+++ b/src/net.h
@@ -266,8 +266,8 @@ public:
void PushMessage(CNode* pnode, CSerializedNetMsg&& msg);
- template<typename Callable>
- void ForEachNode(Callable&& func)
+ using NodeFn = std::function<void(CNode*)>;
+ void ForEachNode(const NodeFn& func)
{
LOCK(cs_vNodes);
for (auto&& node : vNodes) {
@@ -276,8 +276,7 @@ public:
}
};
- template<typename Callable>
- void ForEachNode(Callable&& func) const
+ void ForEachNode(const NodeFn& func) const
{
LOCK(cs_vNodes);
for (auto&& node : vNodes) {
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 859b67755d..d2a76550ea 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -662,8 +662,8 @@ static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connma
return;
}
}
- connman.ForNode(nodeid, [&connman](CNode* pfrom){
- LockAssertion lock(::cs_main);
+ connman.ForNode(nodeid, [&connman](CNode* pfrom) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1;
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
@@ -1355,8 +1355,8 @@ void PeerManager::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_
fWitnessesPresentInMostRecentCompactBlock = fWitnessEnabled;
}
- m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) {
- LockAssertion lock(::cs_main);
+ m_connman.ForEachNode([this, &pcmpctblock, pindex, &msgMaker, fWitnessEnabled, &hashBlock](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
// TODO: Avoid the repeated-serialization here
if (pnode->GetCommonVersion() < INVALID_CB_NO_BAN_VERSION || pnode->fDisconnect)
@@ -1489,9 +1489,8 @@ bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED
void RelayTransaction(const uint256& txid, const uint256& wtxid, const CConnman& connman)
{
- connman.ForEachNode([&txid, &wtxid](CNode* pnode)
- {
- LockAssertion lock(::cs_main);
+ connman.ForEachNode([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
CNodeState* state = State(pnode->GetId());
if (state == nullptr) return;
@@ -3971,8 +3970,8 @@ void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds)
NodeId worst_peer = -1;
int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
- m_connman.ForEachNode([&](CNode* pnode) {
- LockAssertion lock(::cs_main);
+ m_connman.ForEachNode([&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
// Ignore non-outbound peers, or nodes marked for disconnect already
if (!pnode->IsOutboundOrBlockRelayConn() || pnode->fDisconnect) return;
@@ -3988,8 +3987,8 @@ void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds)
}
});
if (worst_peer != -1) {
- bool disconnected = m_connman.ForNode(worst_peer, [&](CNode *pnode) {
- LockAssertion lock(::cs_main);
+ bool disconnected = m_connman.ForNode(worst_peer, [&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ AssertLockHeld(::cs_main);
// Only disconnect a peer that has been connected to us for
// some reasonable fraction of our check-frequency, to give
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 7a3fb420cc..3055cd7da6 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -533,7 +533,7 @@ int GuiMain(int argc, char* argv[])
// - QSettings() will use the new application name after this, resulting in network-specific settings
// - Needs to be done before createOptionsModel
- // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
+ // Check for chain settings (Params() calls are only valid after this clause)
try {
SelectParams(gArgs.GetChainName());
} catch(std::exception &e) {
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index 0b5c341548..8dffd2f59f 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -84,8 +84,11 @@ void AppTests::appTests()
// Reset global state to avoid interfering with later tests.
LogInstance().DisconnectTestLogger();
AbortShutdown();
- UnloadBlockIndex(/* mempool */ nullptr);
- WITH_LOCK(::cs_main, g_chainman.Reset());
+ {
+ LOCK(cs_main);
+ UnloadBlockIndex(/* mempool */ nullptr, g_chainman);
+ g_chainman.Reset();
+ }
}
//! Entry point for BitcoinGUI tests.
diff --git a/src/rest.cpp b/src/rest.cpp
index f0bcbe55f9..949cc9d84a 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -303,7 +303,7 @@ static bool rest_block_notxdetails(const util::Ref& context, HTTPRequest* req, c
}
// A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
-UniValue getblockchaininfo(const JSONRPCRequest& request);
+RPCHelpMan getblockchaininfo();
static bool rest_chaininfo(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{
@@ -316,7 +316,7 @@ static bool rest_chaininfo(const util::Ref& context, HTTPRequest* req, const std
case RetFormat::JSON: {
JSONRPCRequest jsonRequest(context);
jsonRequest.params = UniValue(UniValue::VARR);
- UniValue chainInfoObject = getblockchaininfo(jsonRequest);
+ UniValue chainInfoObject = getblockchaininfo().HandleRequest(jsonRequest);
std::string strJSON = chainInfoObject.write() + "\n";
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strJSON);
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 04b9c6b1cc..0bb7342db0 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -189,9 +189,9 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
return result;
}
-static UniValue getblockcount(const JSONRPCRequest& request)
+static RPCHelpMan getblockcount()
{
- RPCHelpMan{"getblockcount",
+ return RPCHelpMan{"getblockcount",
"\nReturns the height of the most-work fully-validated chain.\n"
"The genesis block has height 0.\n",
{},
@@ -201,15 +201,17 @@ static UniValue getblockcount(const JSONRPCRequest& request)
HelpExampleCli("getblockcount", "")
+ HelpExampleRpc("getblockcount", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
return ::ChainActive().Height();
+},
+ };
}
-static UniValue getbestblockhash(const JSONRPCRequest& request)
+static RPCHelpMan getbestblockhash()
{
- RPCHelpMan{"getbestblockhash",
+ return RPCHelpMan{"getbestblockhash",
"\nReturns the hash of the best (tip) block in the most-work fully-validated chain.\n",
{},
RPCResult{
@@ -218,10 +220,12 @@ static UniValue getbestblockhash(const JSONRPCRequest& request)
HelpExampleCli("getbestblockhash", "")
+ HelpExampleRpc("getbestblockhash", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
return ::ChainActive().Tip()->GetBlockHash().GetHex();
+},
+ };
}
void RPCNotifyBlockChange(const CBlockIndex* pindex)
@@ -234,9 +238,9 @@ void RPCNotifyBlockChange(const CBlockIndex* pindex)
cond_blockchange.notify_all();
}
-static UniValue waitfornewblock(const JSONRPCRequest& request)
+static RPCHelpMan waitfornewblock()
{
- RPCHelpMan{"waitfornewblock",
+ return RPCHelpMan{"waitfornewblock",
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n",
{
@@ -252,7 +256,8 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
HelpExampleCli("waitfornewblock", "1000")
+ HelpExampleRpc("waitfornewblock", "1000")
},
- }.Check(request);
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
int timeout = 0;
if (!request.params[0].isNull())
timeout = request.params[0].get_int();
@@ -271,11 +276,13 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
ret.pushKV("hash", block.hash.GetHex());
ret.pushKV("height", block.height);
return ret;
+},
+ };
}
-static UniValue waitforblock(const JSONRPCRequest& request)
+static RPCHelpMan waitforblock()
{
- RPCHelpMan{"waitforblock",
+ return RPCHelpMan{"waitforblock",
"\nWaits for a specific new block and returns useful info about it.\n"
"\nReturns the current block on timeout or exit.\n",
{
@@ -292,7 +299,8 @@ static UniValue waitforblock(const JSONRPCRequest& request)
HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\" 1000")
+ HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
},
- }.Check(request);
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
int timeout = 0;
uint256 hash(ParseHashV(request.params[0], "blockhash"));
@@ -314,11 +322,13 @@ static UniValue waitforblock(const JSONRPCRequest& request)
ret.pushKV("hash", block.hash.GetHex());
ret.pushKV("height", block.height);
return ret;
+},
+ };
}
-static UniValue waitforblockheight(const JSONRPCRequest& request)
+static RPCHelpMan waitforblockheight()
{
- RPCHelpMan{"waitforblockheight",
+ return 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",
@@ -336,7 +346,8 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
HelpExampleCli("waitforblockheight", "100 1000")
+ HelpExampleRpc("waitforblockheight", "100, 1000")
},
- }.Check(request);
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
int timeout = 0;
int height = request.params[0].get_int();
@@ -357,11 +368,13 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
ret.pushKV("hash", block.hash.GetHex());
ret.pushKV("height", block.height);
return ret;
+},
+ };
}
-static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
+static RPCHelpMan syncwithvalidationinterfacequeue()
{
- RPCHelpMan{"syncwithvalidationinterfacequeue",
+ return RPCHelpMan{"syncwithvalidationinterfacequeue",
"\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n",
{},
RPCResult{RPCResult::Type::NONE, "", ""},
@@ -369,15 +382,17 @@ static UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
HelpExampleCli("syncwithvalidationinterfacequeue","")
+ HelpExampleRpc("syncwithvalidationinterfacequeue","")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
SyncWithValidationInterfaceQueue();
return NullUniValue;
+},
+ };
}
-static UniValue getdifficulty(const JSONRPCRequest& request)
+static RPCHelpMan getdifficulty()
{
- RPCHelpMan{"getdifficulty",
+ return RPCHelpMan{"getdifficulty",
"\nReturns the proof-of-work difficulty as a multiple of the minimum difficulty.\n",
{},
RPCResult{
@@ -386,10 +401,12 @@ static UniValue getdifficulty(const JSONRPCRequest& request)
HelpExampleCli("getdifficulty", "")
+ HelpExampleRpc("getdifficulty", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
return GetDifficulty(::ChainActive().Tip());
+},
+ };
}
static std::vector<RPCResult> MempoolEntryDescription() { return {
@@ -483,9 +500,12 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash()));
}
-UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose)
+UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempool_sequence)
{
if (verbose) {
+ if (include_mempool_sequence) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbose results cannot contain mempool sequence values.");
+ }
LOCK(pool.cs);
UniValue o(UniValue::VOBJ);
for (const CTxMemPoolEntry& e : pool.mapTx) {
@@ -499,24 +519,36 @@ UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose)
}
return o;
} else {
+ uint64_t mempool_sequence;
std::vector<uint256> vtxid;
- pool.queryHashes(vtxid);
-
+ {
+ LOCK(pool.cs);
+ pool.queryHashes(vtxid);
+ mempool_sequence = pool.GetSequence();
+ }
UniValue a(UniValue::VARR);
for (const uint256& hash : vtxid)
a.push_back(hash.ToString());
- return a;
+ if (!include_mempool_sequence) {
+ return a;
+ } else {
+ UniValue o(UniValue::VOBJ);
+ o.pushKV("txids", a);
+ o.pushKV("mempool_sequence", mempool_sequence);
+ return o;
+ }
}
}
-static UniValue getrawmempool(const JSONRPCRequest& request)
+static RPCHelpMan getrawmempool()
{
- RPCHelpMan{"getrawmempool",
+ return RPCHelpMan{"getrawmempool",
"\nReturns all transaction ids in memory pool as a json array of string transaction ids.\n"
"\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n",
{
{"verbose", RPCArg::Type::BOOL, /* default */ "false", "True for a json object, false for array of transaction ids"},
+ {"mempool_sequence", RPCArg::Type::BOOL, /* default */ "false", "If verbose=false, returns a json object with transaction list and mempool sequence number attached."},
},
{
RPCResult{"for verbose = false",
@@ -529,23 +561,39 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
{
{RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()},
}},
+ RPCResult{"for verbose = false and mempool_sequence = true",
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ARR, "txids", "",
+ {
+ {RPCResult::Type::STR_HEX, "", "The transaction id"},
+ }},
+ {RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."},
+ }},
},
RPCExamples{
HelpExampleCli("getrawmempool", "true")
+ HelpExampleRpc("getrawmempool", "true")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
bool fVerbose = false;
if (!request.params[0].isNull())
fVerbose = request.params[0].get_bool();
- return MempoolToJSON(EnsureMemPool(request.context), fVerbose);
+ bool include_mempool_sequence = false;
+ if (!request.params[1].isNull()) {
+ include_mempool_sequence = request.params[1].get_bool();
+ }
+
+ return MempoolToJSON(EnsureMemPool(request.context), fVerbose, include_mempool_sequence);
+},
+ };
}
-static UniValue getmempoolancestors(const JSONRPCRequest& request)
+static RPCHelpMan getmempoolancestors()
{
- RPCHelpMan{"getmempoolancestors",
+ return RPCHelpMan{"getmempoolancestors",
"\nIf txid is in the mempool, returns all in-mempool ancestors.\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
@@ -565,8 +613,8 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
HelpExampleCli("getmempoolancestors", "\"mytxid\"")
+ HelpExampleRpc("getmempoolancestors", "\"mytxid\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
bool fVerbose = false;
if (!request.params[1].isNull())
fVerbose = request.params[1].get_bool();
@@ -603,11 +651,13 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
}
return o;
}
+},
+ };
}
-static UniValue getmempooldescendants(const JSONRPCRequest& request)
+static RPCHelpMan getmempooldescendants()
{
- RPCHelpMan{"getmempooldescendants",
+ return RPCHelpMan{"getmempooldescendants",
"\nIf txid is in the mempool, returns all in-mempool descendants.\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
@@ -627,8 +677,8 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
HelpExampleCli("getmempooldescendants", "\"mytxid\"")
+ HelpExampleRpc("getmempooldescendants", "\"mytxid\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
bool fVerbose = false;
if (!request.params[1].isNull())
fVerbose = request.params[1].get_bool();
@@ -666,11 +716,13 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
}
return o;
}
+},
+ };
}
-static UniValue getmempoolentry(const JSONRPCRequest& request)
+static RPCHelpMan getmempoolentry()
{
- RPCHelpMan{"getmempoolentry",
+ return RPCHelpMan{"getmempoolentry",
"\nReturns mempool data for given transaction\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"},
@@ -681,8 +733,8 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
HelpExampleCli("getmempoolentry", "\"mytxid\"")
+ HelpExampleRpc("getmempoolentry", "\"mytxid\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash = ParseHashV(request.params[0], "parameter 1");
const CTxMemPool& mempool = EnsureMemPool(request.context);
@@ -697,11 +749,13 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
UniValue info(UniValue::VOBJ);
entryToJSON(mempool, info, e);
return info;
+},
+ };
}
-static UniValue getblockhash(const JSONRPCRequest& request)
+static RPCHelpMan getblockhash()
{
- RPCHelpMan{"getblockhash",
+ return RPCHelpMan{"getblockhash",
"\nReturns hash of block in best-block-chain at height provided.\n",
{
{"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The height index"},
@@ -712,8 +766,8 @@ static UniValue getblockhash(const JSONRPCRequest& request)
HelpExampleCli("getblockhash", "1000")
+ HelpExampleRpc("getblockhash", "1000")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
int nHeight = request.params[0].get_int();
@@ -722,11 +776,13 @@ static UniValue getblockhash(const JSONRPCRequest& request)
CBlockIndex* pblockindex = ::ChainActive()[nHeight];
return pblockindex->GetBlockHash().GetHex();
+},
+ };
}
-static UniValue getblockheader(const JSONRPCRequest& request)
+static RPCHelpMan getblockheader()
{
- RPCHelpMan{"getblockheader",
+ return 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",
{
@@ -760,8 +816,8 @@ static UniValue getblockheader(const JSONRPCRequest& request)
HelpExampleCli("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ HelpExampleRpc("getblockheader", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash(ParseHashV(request.params[0], "hash"));
bool fVerbose = true;
@@ -789,6 +845,8 @@ static UniValue getblockheader(const JSONRPCRequest& request)
}
return blockheaderToJSON(tip, pblockindex);
+},
+ };
}
static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
@@ -822,9 +880,9 @@ static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
return blockUndo;
}
-static UniValue getblock(const JSONRPCRequest& request)
+static RPCHelpMan getblock()
{
- RPCHelpMan{"getblock",
+ return 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",
@@ -877,8 +935,8 @@ static UniValue getblock(const JSONRPCRequest& request)
HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
+ HelpExampleRpc("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash(ParseHashV(request.params[0], "blockhash"));
int verbosity = 1;
@@ -913,11 +971,13 @@ static UniValue getblock(const JSONRPCRequest& request)
}
return blockToJSON(block, tip, pblockindex, verbosity >= 2);
+},
+ };
}
-static UniValue pruneblockchain(const JSONRPCRequest& request)
+static RPCHelpMan pruneblockchain()
{
- RPCHelpMan{"pruneblockchain", "",
+ return RPCHelpMan{"pruneblockchain", "",
{
{"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or to a " + UNIX_EPOCH_TIME + "\n"
" to prune blocks whose block time is at least 2 hours older than the provided timestamp."},
@@ -928,8 +988,8 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
HelpExampleCli("pruneblockchain", "1000")
+ HelpExampleRpc("pruneblockchain", "1000")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
if (!fPruneMode)
throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
@@ -968,11 +1028,13 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
block = block->pprev;
}
return uint64_t(block->nHeight);
+},
+ };
}
-static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
+static RPCHelpMan gettxoutsetinfo()
{
- RPCHelpMan{"gettxoutsetinfo",
+ return RPCHelpMan{"gettxoutsetinfo",
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time.\n",
{
@@ -994,8 +1056,8 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
HelpExampleCli("gettxoutsetinfo", "")
+ HelpExampleRpc("gettxoutsetinfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue ret(UniValue::VOBJ);
CCoinsStats stats;
@@ -1020,11 +1082,13 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
return ret;
+},
+ };
}
-UniValue gettxout(const JSONRPCRequest& request)
+static RPCHelpMan gettxout()
{
- RPCHelpMan{"gettxout",
+ return RPCHelpMan{"gettxout",
"\nReturns details about an unspent transaction output.\n",
{
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
@@ -1056,8 +1120,8 @@ UniValue gettxout(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("gettxout", "\"txid\", 1")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
UniValue ret(UniValue::VOBJ);
@@ -1099,11 +1163,13 @@ UniValue gettxout(const JSONRPCRequest& request)
ret.pushKV("coinbase", (bool)coin.fCoinBase);
return ret;
+},
+ };
}
-static UniValue verifychain(const JSONRPCRequest& request)
+static RPCHelpMan verifychain()
{
- RPCHelpMan{"verifychain",
+ return RPCHelpMan{"verifychain",
"\nVerifies blockchain database.\n",
{
{"checklevel", RPCArg::Type::NUM, /* default */ strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL),
@@ -1116,14 +1182,16 @@ static UniValue verifychain(const JSONRPCRequest& request)
HelpExampleCli("verifychain", "")
+ HelpExampleRpc("verifychain", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const int check_level(request.params[0].isNull() ? DEFAULT_CHECKLEVEL : request.params[0].get_int());
const int check_depth{request.params[1].isNull() ? DEFAULT_CHECKBLOCKS : request.params[1].get_int()};
LOCK(cs_main);
return CVerifyDB().VerifyDB(Params(), &::ChainstateActive().CoinsTip(), check_level, check_depth);
+},
+ };
}
static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int height) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@@ -1192,9 +1260,9 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
softforks.pushKV(name, rv);
}
-UniValue getblockchaininfo(const JSONRPCRequest& request)
+RPCHelpMan getblockchaininfo()
{
- RPCHelpMan{"getblockchaininfo",
+ return RPCHelpMan{"getblockchaininfo",
"Returns an object containing various state info regarding blockchain processing.\n",
{},
RPCResult{
@@ -1245,8 +1313,8 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
HelpExampleCli("getblockchaininfo", "")
+ HelpExampleRpc("getblockchaininfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
const CBlockIndex* tip = ::ChainActive().Tip();
@@ -1291,6 +1359,8 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
+},
+ };
}
/** Comparison function for sorting the getchaintips heads. */
@@ -1308,9 +1378,9 @@ struct CompareBlocksByHeight
}
};
-static UniValue getchaintips(const JSONRPCRequest& request)
+static RPCHelpMan getchaintips()
{
- RPCHelpMan{"getchaintips",
+ return RPCHelpMan{"getchaintips",
"Return information about all known tips in the block tree,"
" including the main chain as well as orphaned branches.\n",
{},
@@ -1333,8 +1403,8 @@ static UniValue getchaintips(const JSONRPCRequest& request)
HelpExampleCli("getchaintips", "")
+ HelpExampleRpc("getchaintips", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
ChainstateManager& chainman = EnsureChainman(request.context);
LOCK(cs_main);
@@ -1401,6 +1471,8 @@ static UniValue getchaintips(const JSONRPCRequest& request)
}
return res;
+},
+ };
}
UniValue MempoolInfoToJSON(const CTxMemPool& pool)
@@ -1420,9 +1492,9 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
return ret;
}
-static UniValue getmempoolinfo(const JSONRPCRequest& request)
+static RPCHelpMan getmempoolinfo()
{
- RPCHelpMan{"getmempoolinfo",
+ return RPCHelpMan{"getmempoolinfo",
"\nReturns details on the active state of the TX memory pool.\n",
{},
RPCResult{
@@ -1441,14 +1513,16 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
HelpExampleCli("getmempoolinfo", "")
+ HelpExampleRpc("getmempoolinfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
return MempoolInfoToJSON(EnsureMemPool(request.context));
+},
+ };
}
-static UniValue preciousblock(const JSONRPCRequest& request)
+static RPCHelpMan preciousblock()
{
- RPCHelpMan{"preciousblock",
+ return 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",
@@ -1460,8 +1534,8 @@ static UniValue preciousblock(const JSONRPCRequest& request)
HelpExampleCli("preciousblock", "\"blockhash\"")
+ HelpExampleRpc("preciousblock", "\"blockhash\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash(ParseHashV(request.params[0], "blockhash"));
CBlockIndex* pblockindex;
@@ -1481,11 +1555,13 @@ static UniValue preciousblock(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue invalidateblock(const JSONRPCRequest& request)
+static RPCHelpMan invalidateblock()
{
- RPCHelpMan{"invalidateblock",
+ return RPCHelpMan{"invalidateblock",
"\nPermanently marks a block as invalid, as if it violated a consensus rule.\n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hash of the block to mark as invalid"},
@@ -1495,8 +1571,8 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
HelpExampleCli("invalidateblock", "\"blockhash\"")
+ HelpExampleRpc("invalidateblock", "\"blockhash\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash(ParseHashV(request.params[0], "blockhash"));
BlockValidationState state;
@@ -1519,11 +1595,13 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue reconsiderblock(const JSONRPCRequest& request)
+static RPCHelpMan reconsiderblock()
{
- RPCHelpMan{"reconsiderblock",
+ return RPCHelpMan{"reconsiderblock",
"\nRemoves invalidity status of a block, its ancestors and its descendants, reconsider them for activation.\n"
"This can be used to undo the effects of invalidateblock.\n",
{
@@ -1534,8 +1612,8 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
HelpExampleCli("reconsiderblock", "\"blockhash\"")
+ HelpExampleRpc("reconsiderblock", "\"blockhash\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 hash(ParseHashV(request.params[0], "blockhash"));
{
@@ -1556,11 +1634,13 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue getchaintxstats(const JSONRPCRequest& request)
+static RPCHelpMan getchaintxstats()
{
- RPCHelpMan{"getchaintxstats",
+ return RPCHelpMan{"getchaintxstats",
"\nCompute statistics about the total number and rate of transactions in the chain.\n",
{
{"nblocks", RPCArg::Type::NUM, /* default */ "one month", "Size of the window in number of blocks"},
@@ -1582,8 +1662,8 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
HelpExampleCli("getchaintxstats", "")
+ HelpExampleRpc("getchaintxstats", "2016")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const CBlockIndex* pindex;
int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month
@@ -1633,6 +1713,8 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
}
return ret;
+},
+ };
}
template<typename T>
@@ -1691,9 +1773,9 @@ static inline bool SetHasKeys(const std::set<T>& set, const Tk& key, const Args&
// outpoint (needed for the utxo index) + nHeight + fCoinBase
static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t) + sizeof(bool);
-static UniValue getblockstats(const JSONRPCRequest& request)
+static RPCHelpMan getblockstats()
{
- RPCHelpMan{"getblockstats",
+ return 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",
{
@@ -1751,8 +1833,8 @@ static UniValue getblockstats(const JSONRPCRequest& request)
HelpExampleRpc("getblockstats", R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", ["minfeerate","avgfeerate"])") +
HelpExampleRpc("getblockstats", R"(1000, ["minfeerate","avgfeerate"])")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
CBlockIndex* pindex;
@@ -1948,11 +2030,13 @@ static UniValue getblockstats(const JSONRPCRequest& request)
ret.pushKV(stat, value);
}
return ret;
+},
+ };
}
-static UniValue savemempool(const JSONRPCRequest& request)
+static RPCHelpMan savemempool()
{
- RPCHelpMan{"savemempool",
+ return RPCHelpMan{"savemempool",
"\nDumps the mempool to disk. It will fail until the previous dump is fully loaded.\n",
{},
RPCResult{RPCResult::Type::NONE, "", ""},
@@ -1960,8 +2044,8 @@ static UniValue savemempool(const JSONRPCRequest& request)
HelpExampleCli("savemempool", "")
+ HelpExampleRpc("savemempool", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const CTxMemPool& mempool = EnsureMemPool(request.context);
if (!mempool.IsLoaded()) {
@@ -1973,6 +2057,8 @@ static UniValue savemempool(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
namespace {
@@ -2034,9 +2120,9 @@ public:
}
};
-UniValue scantxoutset(const JSONRPCRequest& request)
+static RPCHelpMan scantxoutset()
{
- RPCHelpMan{"scantxoutset",
+ return 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"
@@ -2090,8 +2176,8 @@ UniValue scantxoutset(const JSONRPCRequest& request)
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
}},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
UniValue result(UniValue::VOBJ);
@@ -2184,11 +2270,13 @@ UniValue scantxoutset(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
}
return result;
+},
+ };
}
-static UniValue getblockfilter(const JSONRPCRequest& request)
+static RPCHelpMan getblockfilter()
{
- RPCHelpMan{"getblockfilter",
+ return RPCHelpMan{"getblockfilter",
"\nRetrieve a BIP 157 content filter for a particular block.\n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hash of the block"},
@@ -2203,9 +2291,9 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" \"basic\"") +
HelpExampleRpc("getblockfilter", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\", \"basic\"")
- }
- }.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
uint256 block_hash = ParseHashV(request.params[0], "blockhash");
std::string filtertype_name = "basic";
if (!request.params[1].isNull()) {
@@ -2260,6 +2348,8 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
ret.pushKV("filter", HexStr(filter.GetEncodedFilter()));
ret.pushKV("header", filter_header.GetHex());
return ret;
+},
+ };
}
/**
@@ -2267,9 +2357,9 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
*
* @see SnapshotMetadata
*/
-UniValue dumptxoutset(const JSONRPCRequest& request)
+static RPCHelpMan dumptxoutset()
{
- RPCHelpMan{
+ return RPCHelpMan{
"dumptxoutset",
"\nWrite the serialized UTXO set to disk.\n",
{
@@ -2290,9 +2380,9 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
},
RPCExamples{
HelpExampleCli("dumptxoutset", "utxo.dat")
- }
- }.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
fs::path path = fs::absolute(request.params[0].get_str(), GetDataDir());
// Write to a temporary path and then move into `path` on completion
// to avoid confusion due to an interruption.
@@ -2366,6 +2456,8 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
result.pushKV("base_height", tip->nHeight);
result.pushKV("path", path.string());
return result;
+},
+ };
}
void RegisterBlockchainRPCCommands(CRPCTable &t)
@@ -2388,7 +2480,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "getmempooldescendants", &getmempooldescendants, {"txid","verbose"} },
{ "blockchain", "getmempoolentry", &getmempoolentry, {"txid"} },
{ "blockchain", "getmempoolinfo", &getmempoolinfo, {} },
- { "blockchain", "getrawmempool", &getrawmempool, {"verbose"} },
+ { "blockchain", "getrawmempool", &getrawmempool, {"verbose", "mempool_sequence"} },
{ "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} },
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {"hash_type"} },
{ "blockchain", "pruneblockchain", &pruneblockchain, {"height"} },
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 5c9a43b13e..5b362bf211 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -43,7 +43,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
UniValue MempoolInfoToJSON(const CTxMemPool& pool);
/** Mempool to JSON */
-UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose = false);
+UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose = false, bool include_mempool_sequence = false);
/** Block header to JSON */
UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex) LOCKS_EXCLUDED(cs_main);
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 6ef3294132..3c432464f2 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -142,6 +142,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "pruneblockchain", 0, "height" },
{ "keypoolrefill", 0, "newsize" },
{ "getrawmempool", 0, "verbose" },
+ { "getrawmempool", 1, "mempool_sequence" },
{ "estimatesmartfee", 0, "conf_target" },
{ "estimaterawfee", 0, "conf_target" },
{ "estimaterawfee", 1, "threshold" },
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index e195affd54..def21b119e 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -29,9 +29,9 @@
#include <univalue.h>
-static UniValue getconnectioncount(const JSONRPCRequest& request)
+static RPCHelpMan getconnectioncount()
{
- RPCHelpMan{"getconnectioncount",
+ return RPCHelpMan{"getconnectioncount",
"\nReturns the number of connections to other nodes.\n",
{},
RPCResult{
@@ -41,18 +41,20 @@ static UniValue getconnectioncount(const JSONRPCRequest& request)
HelpExampleCli("getconnectioncount", "")
+ HelpExampleRpc("getconnectioncount", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
return (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
+},
+ };
}
-static UniValue ping(const JSONRPCRequest& request)
+static RPCHelpMan ping()
{
- RPCHelpMan{"ping",
+ return 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",
@@ -62,8 +64,8 @@ static UniValue ping(const JSONRPCRequest& request)
HelpExampleCli("ping", "")
+ HelpExampleRpc("ping", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -73,11 +75,13 @@ static UniValue ping(const JSONRPCRequest& request)
pnode->fPingQueued = true;
});
return NullUniValue;
+},
+ };
}
-static UniValue getpeerinfo(const JSONRPCRequest& request)
+static RPCHelpMan getpeerinfo()
{
- RPCHelpMan{"getpeerinfo",
+ return RPCHelpMan{"getpeerinfo",
"\nReturns data about each connected network node as a json array of objects.\n",
{},
RPCResult{
@@ -144,8 +148,8 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
HelpExampleCli("getpeerinfo", "")
+ HelpExampleRpc("getpeerinfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -239,17 +243,13 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
}
return ret;
+},
+ };
}
-static UniValue addnode(const JSONRPCRequest& request)
+static RPCHelpMan addnode()
{
- std::string strCommand;
- if (!request.params[1].isNull())
- strCommand = request.params[1].get_str();
- if (request.fHelp || request.params.size() != 2 ||
- (strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
- throw std::runtime_error(
- RPCHelpMan{"addnode",
+ return 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"
@@ -263,7 +263,15 @@ static UniValue addnode(const JSONRPCRequest& request)
HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"")
+ HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")
},
- }.ToString());
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ std::string strCommand;
+ if (!request.params[1].isNull())
+ strCommand = request.params[1].get_str();
+ if (request.fHelp || request.params.size() != 2 ||
+ (strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
+ throw std::runtime_error(
+ self.ToString());
NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
@@ -290,11 +298,13 @@ static UniValue addnode(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue disconnectnode(const JSONRPCRequest& request)
+static RPCHelpMan disconnectnode()
{
- RPCHelpMan{"disconnectnode",
+ return 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",
@@ -309,8 +319,8 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
+ HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"")
+ HelpExampleRpc("disconnectnode", "\"\", 1")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -335,11 +345,13 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue getaddednodeinfo(const JSONRPCRequest& request)
+static RPCHelpMan getaddednodeinfo()
{
- RPCHelpMan{"getaddednodeinfo",
+ return RPCHelpMan{"getaddednodeinfo",
"\nReturns information about the given added node, or all added nodes\n"
"(note that onetry addnodes are not listed here)\n",
{
@@ -367,8 +379,8 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"")
+ HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -407,11 +419,13 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
}
return ret;
+},
+ };
}
-static UniValue getnettotals(const JSONRPCRequest& request)
+static RPCHelpMan getnettotals()
{
- RPCHelpMan{"getnettotals",
+ return RPCHelpMan{"getnettotals",
"\nReturns information about network traffic, including bytes in, bytes out,\n"
"and current time.\n",
{},
@@ -436,7 +450,8 @@ static UniValue getnettotals(const JSONRPCRequest& request)
HelpExampleCli("getnettotals", "")
+ HelpExampleRpc("getnettotals", "")
},
- }.Check(request);
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -455,6 +470,8 @@ static UniValue getnettotals(const JSONRPCRequest& request)
outboundLimit.pushKV("time_left_in_cycle", node.connman->GetMaxOutboundTimeLeftInCycle());
obj.pushKV("uploadtarget", outboundLimit);
return obj;
+},
+ };
}
static UniValue GetNetworksInfo()
@@ -478,9 +495,9 @@ static UniValue GetNetworksInfo()
return networks;
}
-static UniValue getnetworkinfo(const JSONRPCRequest& request)
+static RPCHelpMan getnetworkinfo()
{
- RPCHelpMan{"getnetworkinfo",
+ return RPCHelpMan{"getnetworkinfo",
"Returns an object containing various state info regarding P2P networking.\n",
{},
RPCResult{
@@ -529,8 +546,8 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
HelpExampleCli("getnetworkinfo", "")
+ HelpExampleRpc("getnetworkinfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
obj.pushKV("version", CLIENT_VERSION);
@@ -568,11 +585,13 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.pushKV("localaddresses", localAddresses);
obj.pushKV("warnings", GetWarnings(false).original);
return obj;
+},
+ };
}
-static UniValue setban(const JSONRPCRequest& request)
+static RPCHelpMan setban()
{
- const RPCHelpMan help{"setban",
+ return RPCHelpMan{"setban",
"\nAttempts to add or remove an IP/Subnet from the banned list.\n",
{
{"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"},
@@ -586,7 +605,8 @@ static UniValue setban(const JSONRPCRequest& request)
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")
},
- };
+ [&](const RPCHelpMan& help, const JSONRPCRequest& request) -> UniValue
+{
std::string strCommand;
if (!request.params[1].isNull())
strCommand = request.params[1].get_str();
@@ -649,11 +669,13 @@ static UniValue setban(const JSONRPCRequest& request)
}
}
return NullUniValue;
+},
+ };
}
-static UniValue listbanned(const JSONRPCRequest& request)
+static RPCHelpMan listbanned()
{
- RPCHelpMan{"listbanned",
+ return RPCHelpMan{"listbanned",
"\nList all manually banned IPs/Subnets.\n",
{},
RPCResult{RPCResult::Type::ARR, "", "",
@@ -669,8 +691,8 @@ static UniValue listbanned(const JSONRPCRequest& request)
HelpExampleCli("listbanned", "")
+ HelpExampleRpc("listbanned", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if(!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
@@ -692,11 +714,13 @@ static UniValue listbanned(const JSONRPCRequest& request)
}
return bannedAddresses;
+},
+ };
}
-static UniValue clearbanned(const JSONRPCRequest& request)
+static RPCHelpMan clearbanned()
{
- RPCHelpMan{"clearbanned",
+ return RPCHelpMan{"clearbanned",
"\nClear all banned IPs.\n",
{},
RPCResult{RPCResult::Type::NONE, "", ""},
@@ -704,7 +728,8 @@ static UniValue clearbanned(const JSONRPCRequest& request)
HelpExampleCli("clearbanned", "")
+ HelpExampleRpc("clearbanned", "")
},
- }.Check(request);
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if (!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
@@ -713,19 +738,21 @@ static UniValue clearbanned(const JSONRPCRequest& request)
node.banman->ClearBanned();
return NullUniValue;
+},
+ };
}
-static UniValue setnetworkactive(const JSONRPCRequest& request)
+static RPCHelpMan setnetworkactive()
{
- RPCHelpMan{"setnetworkactive",
+ return RPCHelpMan{"setnetworkactive",
"\nDisable/enable all p2p network activity.\n",
{
{"state", RPCArg::Type::BOOL, RPCArg::Optional::NO, "true to enable networking, false to disable"},
},
RPCResult{RPCResult::Type::BOOL, "", "The value that was passed in"},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if (!node.connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -734,11 +761,13 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
node.connman->SetNetworkActive(request.params[0].get_bool());
return node.connman->GetNetworkActive();
+},
+ };
}
-static UniValue getnodeaddresses(const JSONRPCRequest& request)
+static RPCHelpMan getnodeaddresses()
{
- RPCHelpMan{"getnodeaddresses",
+ return RPCHelpMan{"getnodeaddresses",
"\nReturn known addresses which can potentially be used to find new nodes in the network\n",
{
{"count", RPCArg::Type::NUM, /* default */ "1", "The maximum number of addresses to return. Specify 0 to return all known addresses."},
@@ -759,7 +788,8 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
HelpExampleCli("getnodeaddresses", "8")
+ HelpExampleRpc("getnodeaddresses", "8")
},
- }.Check(request);
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if (!node.connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -785,11 +815,13 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
ret.push_back(obj);
}
return ret;
+},
+ };
}
-static UniValue addpeeraddress(const JSONRPCRequest& request)
+static RPCHelpMan addpeeraddress()
{
- RPCHelpMan{"addpeeraddress",
+ return RPCHelpMan{"addpeeraddress",
"\nAdd the address of a potential peer to the address manager. This RPC is for testing only.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address of the peer"},
@@ -805,8 +837,8 @@ static UniValue addpeeraddress(const JSONRPCRequest& request)
HelpExampleCli("addpeeraddress", "\"1.2.3.4\" 8333")
+ HelpExampleRpc("addpeeraddress", "\"1.2.3.4\", 8333")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
NodeContext& node = EnsureNodeContext(request.context);
if (!node.connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -833,6 +865,8 @@ static UniValue addpeeraddress(const JSONRPCRequest& request)
obj.pushKV("success", true);
return obj;
+},
+ };
}
void RegisterNetRPCCommands(CRPCTable &t)
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 1a43ffcc53..e60e0a2d90 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -67,9 +67,9 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
}
}
-static UniValue getrawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan getrawtransaction()
{
- RPCHelpMan{
+ return RPCHelpMan{
"getrawtransaction",
"\nReturn the raw transaction data.\n"
@@ -155,8 +155,8 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"")
+ HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
const NodeContext& node = EnsureNodeContext(request.context);
bool in_active_chain = true;
@@ -217,11 +217,13 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
if (blockindex) result.pushKV("in_active_chain", in_active_chain);
TxToJSON(*tx, hash_block, result);
return result;
+},
+ };
}
-static UniValue gettxoutproof(const JSONRPCRequest& request)
+static RPCHelpMan gettxoutproof()
{
- RPCHelpMan{"gettxoutproof",
+ return 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"
@@ -239,8 +241,8 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof."
},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::set<uint256> setTxids;
uint256 oneTxid;
UniValue txids = request.params[0].get_array();
@@ -315,11 +317,13 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
ssMB << mb;
std::string strHex = HexStr(ssMB);
return strHex;
+},
+ };
}
-static UniValue verifytxoutproof(const JSONRPCRequest& request)
+static RPCHelpMan verifytxoutproof()
{
- RPCHelpMan{"verifytxoutproof",
+ return 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",
{
@@ -332,8 +336,8 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
}
},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
CMerkleBlock merkleBlock;
ssMB >> merkleBlock;
@@ -360,11 +364,13 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
}
return res;
+},
+ };
}
-static UniValue createrawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan createrawtransaction()
{
- RPCHelpMan{"createrawtransaction",
+ return RPCHelpMan{"createrawtransaction",
"\nCreate a transaction spending the given inputs and creating new outputs.\n"
"Outputs can be addresses or data.\n"
"Returns hex-encoded raw transaction.\n"
@@ -412,8 +418,8 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {
UniValue::VARR,
UniValueType(), // ARR or OBJ, checked later
@@ -429,11 +435,13 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
return EncodeHexTx(CTransaction(rawTx));
+},
+ };
}
-static UniValue decoderawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan decoderawtransaction()
{
- RPCHelpMan{"decoderawtransaction",
+ return RPCHelpMan{"decoderawtransaction",
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction hex string"},
@@ -498,8 +506,8 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
HelpExampleCli("decoderawtransaction", "\"hexstring\"")
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
CMutableTransaction mtx;
@@ -515,6 +523,8 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
TxToUniv(CTransaction(std::move(mtx)), uint256(), result, false);
return result;
+},
+ };
}
static std::string GetAllOutputTypes()
@@ -527,9 +537,9 @@ static std::string GetAllOutputTypes()
return Join(ret, ", ");
}
-static UniValue decodescript(const JSONRPCRequest& request)
+static RPCHelpMan decodescript()
{
- RPCHelpMan{"decodescript",
+ return RPCHelpMan{"decodescript",
"\nDecode a hex-encoded script.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"},
@@ -563,8 +573,8 @@ static UniValue decodescript(const JSONRPCRequest& request)
HelpExampleCli("decodescript", "\"hexstring\"")
+ HelpExampleRpc("decodescript", "\"hexstring\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR});
UniValue r(UniValue::VOBJ);
@@ -616,11 +626,13 @@ static UniValue decodescript(const JSONRPCRequest& request)
}
return r;
+},
+ };
}
-static UniValue combinerawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan combinerawtransaction()
{
- RPCHelpMan{"combinerawtransaction",
+ return 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.",
@@ -637,8 +649,8 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("combinerawtransaction", R"('["myhex1", "myhex2", "myhex3"]')")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue txs = request.params[0].get_array();
std::vector<CMutableTransaction> txVariants(txs.size());
@@ -699,11 +711,13 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
}
return EncodeHexTx(CTransaction(mergedTx));
+},
+ };
}
-static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
+static RPCHelpMan signrawtransactionwithkey()
{
- RPCHelpMan{"signrawtransactionwithkey",
+ return 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"
@@ -761,8 +775,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
HelpExampleCli("signrawtransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"")
+ HelpExampleRpc("signrawtransactionwithkey", "\"myhex\", \"[\\\"key1\\\",\\\"key2\\\"]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
CMutableTransaction mtx;
@@ -795,11 +809,13 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
SignTransaction(mtx, &keystore, coins, request.params[3], result);
return result;
+},
+ };
}
-static UniValue sendrawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan sendrawtransaction()
{
- RPCHelpMan{"sendrawtransaction",
+ return RPCHelpMan{"sendrawtransaction",
"\nSubmit a raw transaction (serialized, hex-encoded) to local node and network.\n"
"\nNote that the transaction will be sent unconditionally to all peers, so using this\n"
"for manual rebroadcast may degrade privacy by leaking the transaction's origin, as\n"
@@ -824,8 +840,8 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendrawtransaction", "\"signedhex\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {
UniValue::VSTR,
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
@@ -853,11 +869,13 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
}
return tx->GetHash().GetHex();
+},
+ };
}
-static UniValue testmempoolaccept(const JSONRPCRequest& request)
+static RPCHelpMan testmempoolaccept()
{
- RPCHelpMan{"testmempoolaccept",
+ return RPCHelpMan{"testmempoolaccept",
"\nReturns result of mempool acceptance tests indicating if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
"\nThis checks if the transaction violates the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n",
@@ -897,8 +915,8 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {
UniValue::VARR,
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
@@ -958,11 +976,13 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
result.push_back(std::move(result_0));
return result;
+},
+ };
}
-UniValue decodepsbt(const JSONRPCRequest& request)
+static RPCHelpMan decodepsbt()
{
- RPCHelpMan{"decodepsbt",
+ return RPCHelpMan{"decodepsbt",
"\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The PSBT base64 string"},
@@ -1074,8 +1094,8 @@ UniValue decodepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("decodepsbt", "\"psbt\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR});
// Unserialize the transactions
@@ -1267,11 +1287,13 @@ UniValue decodepsbt(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
-UniValue combinepsbt(const JSONRPCRequest& request)
+static RPCHelpMan combinepsbt()
{
- RPCHelpMan{"combinepsbt",
+ return RPCHelpMan{"combinepsbt",
"\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
"Implements the Combiner role.\n",
{
@@ -1287,8 +1309,8 @@ UniValue combinepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("combinepsbt", R"('["mybase64_1", "mybase64_2", "mybase64_3"]')")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VARR}, true);
// Unserialize the transactions
@@ -1315,11 +1337,13 @@ UniValue combinepsbt(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << merged_psbt;
return EncodeBase64(MakeUCharSpan(ssTx));
+},
+ };
}
-UniValue finalizepsbt(const JSONRPCRequest& request)
+static RPCHelpMan finalizepsbt()
{
- RPCHelpMan{"finalizepsbt",
+ return 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"
@@ -1340,8 +1364,8 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("finalizepsbt", "\"psbt\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}, true);
// Unserialize the transactions
@@ -1372,11 +1396,13 @@ UniValue finalizepsbt(const JSONRPCRequest& request)
result.pushKV("complete", complete);
return result;
+},
+ };
}
-UniValue createpsbt(const JSONRPCRequest& request)
+static RPCHelpMan createpsbt()
{
- RPCHelpMan{"createpsbt",
+ return RPCHelpMan{"createpsbt",
"\nCreates a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator role.\n",
{
@@ -1418,8 +1444,8 @@ UniValue createpsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("createpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {
UniValue::VARR,
@@ -1450,11 +1476,13 @@ UniValue createpsbt(const JSONRPCRequest& request)
ssTx << psbtx;
return EncodeBase64(MakeUCharSpan(ssTx));
+},
+ };
}
-UniValue converttopsbt(const JSONRPCRequest& request)
+static RPCHelpMan converttopsbt()
{
- RPCHelpMan{"converttopsbt",
+ return 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",
{
@@ -1478,8 +1506,8 @@ UniValue converttopsbt(const JSONRPCRequest& request)
"\nConvert the transaction to a PSBT\n"
+ HelpExampleCli("converttopsbt", "\"rawtransaction\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true);
// parse hex string from parameter
@@ -1517,11 +1545,13 @@ UniValue converttopsbt(const JSONRPCRequest& request)
ssTx << psbtx;
return EncodeBase64(MakeUCharSpan(ssTx));
+},
+ };
}
-UniValue utxoupdatepsbt(const JSONRPCRequest& request)
+static RPCHelpMan utxoupdatepsbt()
{
- RPCHelpMan{"utxoupdatepsbt",
+ return RPCHelpMan{"utxoupdatepsbt",
"\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set or the mempool.\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
@@ -1538,8 +1568,9 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
},
RPCExamples {
HelpExampleCli("utxoupdatepsbt", "\"psbt\"")
- }}.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}, true);
// Unserialize the transactions
@@ -1605,11 +1636,13 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
return EncodeBase64(MakeUCharSpan(ssTx));
+},
+ };
}
-UniValue joinpsbts(const JSONRPCRequest& request)
+static RPCHelpMan joinpsbts()
{
- RPCHelpMan{"joinpsbts",
+ return RPCHelpMan{"joinpsbts",
"\nJoins multiple distinct PSBTs with different inputs and outputs into one PSBT with inputs and outputs from all of the PSBTs\n"
"No input in any of the PSBTs can be in more than one of the PSBTs.\n",
{
@@ -1623,8 +1656,9 @@ UniValue joinpsbts(const JSONRPCRequest& request)
},
RPCExamples {
HelpExampleCli("joinpsbts", "\"psbt\"")
- }}.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VARR}, true);
// Unserialize the transactions
@@ -1698,11 +1732,13 @@ UniValue joinpsbts(const JSONRPCRequest& request)
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << shuffled_psbt;
return EncodeBase64(MakeUCharSpan(ssTx));
+},
+ };
}
-UniValue analyzepsbt(const JSONRPCRequest& request)
+static RPCHelpMan analyzepsbt()
{
- RPCHelpMan{"analyzepsbt",
+ return RPCHelpMan{"analyzepsbt",
"\nAnalyzes and provides information about the current status of a PSBT and its inputs\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"}
@@ -1741,8 +1777,9 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
},
RPCExamples {
HelpExampleCli("analyzepsbt", "\"psbt\"")
- }}.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
RPCTypeCheck(request.params, {UniValue::VSTR});
// Unserialize the transaction
@@ -1806,6 +1843,8 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
}
return result;
+},
+ };
}
void RegisterRawTransactionRPCCommands(CRPCTable &t)
diff --git a/src/signet.cpp b/src/signet.cpp
index a29f89b58e..e68f031aa4 100644
--- a/src/signet.cpp
+++ b/src/signet.cpp
@@ -65,7 +65,7 @@ static uint256 ComputeModifiedMerkleRoot(const CMutableTransaction& cb, const CB
return ComputeMerkleRoot(std::move(leaves));
}
-SignetTxs SignetTxs::Create(const CBlock& block, const CScript& challenge)
+Optional<SignetTxs> SignetTxs::Create(const CBlock& block, const CScript& challenge)
{
CMutableTransaction tx_to_spend;
tx_to_spend.nVersion = 0;
@@ -83,12 +83,12 @@ SignetTxs SignetTxs::Create(const CBlock& block, const CScript& challenge)
// responses from block coinbase tx
// find and delete signet signature
- if (block.vtx.empty()) return invalid(); // no coinbase tx in block; invalid
+ if (block.vtx.empty()) return nullopt; // no coinbase tx in block; invalid
CMutableTransaction modified_cb(*block.vtx.at(0));
const int cidx = GetWitnessCommitmentIndex(block);
if (cidx == NO_WITNESS_COMMITMENT) {
- return invalid(); // require a witness commitment
+ return nullopt; // require a witness commitment
}
CScript& witness_commitment = modified_cb.vout.at(cidx).scriptPubKey;
@@ -101,9 +101,9 @@ SignetTxs SignetTxs::Create(const CBlock& block, const CScript& challenge)
VectorReader v(SER_NETWORK, INIT_PROTO_VERSION, signet_solution, 0);
v >> tx_spending.vin[0].scriptSig;
v >> tx_spending.vin[0].scriptWitness.stack;
- if (!v.empty()) return invalid(); // extraneous data encountered
+ if (!v.empty()) return nullopt; // extraneous data encountered
} catch (const std::exception&) {
- return invalid(); // parsing error
+ return nullopt; // parsing error
}
}
uint256 signet_merkle = ComputeModifiedMerkleRoot(modified_cb, block);
@@ -117,7 +117,7 @@ SignetTxs SignetTxs::Create(const CBlock& block, const CScript& challenge)
tx_to_spend.vin[0].scriptSig << block_data;
tx_spending.vin[0].prevout = COutPoint(tx_to_spend.GetHash(), 0);
- return {tx_to_spend, tx_spending};
+ return SignetTxs{tx_to_spend, tx_spending};
}
// Signet block solution checker
@@ -129,19 +129,19 @@ bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& cons
}
const CScript challenge(consensusParams.signet_challenge.begin(), consensusParams.signet_challenge.end());
- const SignetTxs signet_txs(block, challenge);
+ const Optional<SignetTxs> signet_txs = SignetTxs::Create(block, challenge);
- if (!signet_txs.m_valid) {
+ if (!signet_txs) {
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution parse failure)\n");
return false;
}
- const CScript& scriptSig = signet_txs.m_to_sign.vin[0].scriptSig;
- const CScriptWitness& witness = signet_txs.m_to_sign.vin[0].scriptWitness;
+ const CScript& scriptSig = signet_txs->m_to_sign.vin[0].scriptSig;
+ const CScriptWitness& witness = signet_txs->m_to_sign.vin[0].scriptWitness;
- TransactionSignatureChecker sigcheck(&signet_txs.m_to_sign, /*nIn=*/ 0, /*amount=*/ signet_txs.m_to_spend.vout[0].nValue);
+ TransactionSignatureChecker sigcheck(&signet_txs->m_to_sign, /*nIn=*/ 0, /*amount=*/ signet_txs->m_to_spend.vout[0].nValue);
- if (!VerifyScript(scriptSig, signet_txs.m_to_spend.vout[0].scriptPubKey, &witness, BLOCK_SCRIPT_VERIFY_FLAGS, sigcheck)) {
+ if (!VerifyScript(scriptSig, signet_txs->m_to_spend.vout[0].scriptPubKey, &witness, BLOCK_SCRIPT_VERIFY_FLAGS, sigcheck)) {
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution invalid)\n");
return false;
}
diff --git a/src/signet.h b/src/signet.h
index 5694716fb6..23563a83c4 100644
--- a/src/signet.h
+++ b/src/signet.h
@@ -9,6 +9,8 @@
#include <primitives/block.h>
#include <primitives/transaction.h>
+#include <optional.h>
+
/**
* Extract signature and check whether a block has a valid solution
*/
@@ -22,21 +24,14 @@ bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& cons
* 2. It skips the nonce.
*/
class SignetTxs {
-private:
- struct invalid {};
- SignetTxs(invalid i) : m_to_spend(), m_to_sign(), m_valid(false) { }
-
template<class T1, class T2>
- SignetTxs(const T1& to_spend, const T2& to_sign) : m_to_spend{to_spend}, m_to_sign{to_sign}, m_valid(true) { }
-
- static SignetTxs Create(const CBlock& block, const CScript& challenge);
+ SignetTxs(const T1& to_spend, const T2& to_sign) : m_to_spend{to_spend}, m_to_sign{to_sign} { }
public:
- SignetTxs(const CBlock& block, const CScript& challenge) : SignetTxs(Create(block, challenge)) { }
+ static Optional<SignetTxs> Create(const CBlock& block, const CScript& challenge);
const CTransaction m_to_spend;
const CTransaction m_to_sign;
- const bool m_valid;
};
#endif // BITCOIN_SIGNET_H
diff --git a/src/sync.h b/src/sync.h
index 7b397a8003..41f4e43bdd 100644
--- a/src/sync.h
+++ b/src/sync.h
@@ -352,18 +352,4 @@ public:
}
};
-// Utility class for indicating to compiler thread analysis that a mutex is
-// locked (when it couldn't be determined otherwise).
-struct SCOPED_LOCKABLE LockAssertion
-{
- template <typename Mutex>
- explicit LockAssertion(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
- {
-#ifdef DEBUG_LOCKORDER
- AssertLockHeld(mutex);
-#endif
- }
- ~LockAssertion() UNLOCK_FUNCTION() {}
-};
-
#endif // BITCOIN_SYNC_H
diff --git a/src/test/fuzz/signet.cpp b/src/test/fuzz/signet.cpp
index 4736ae27f5..786f1a83fe 100644
--- a/src/test/fuzz/signet.cpp
+++ b/src/test/fuzz/signet.cpp
@@ -7,8 +7,8 @@
#include <primitives/block.h>
#include <signet.h>
#include <streams.h>
-#include <test/fuzz/fuzz.h>
#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <cstdint>
@@ -28,7 +28,5 @@ void test_one_input(const std::vector<uint8_t>& buffer)
return;
}
(void)CheckSignetBlockSolution(*block, Params().GetConsensus());
- if (GetWitnessCommitmentIndex(*block) != NO_WITNESS_COMMITMENT) {
- (void)SignetTxs(*block, ConsumeScript(fuzzed_data_provider));
- }
+ (void)SignetTxs::Create(*block, ConsumeScript(fuzzed_data_provider));
}
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 08aff07448..2d3137e1e2 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -187,7 +187,7 @@ TestingSetup::~TestingSetup()
m_node.connman.reset();
m_node.banman.reset();
m_node.args = nullptr;
- UnloadBlockIndex(m_node.mempool.get());
+ UnloadBlockIndex(m_node.mempool.get(), *m_node.chainman);
m_node.mempool.reset();
m_node.scheduler.reset();
m_node.chainman->Reset();
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 6a525c97db..0c2b731967 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -409,12 +409,16 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
{
+ // We increment mempool sequence value no matter removal reason
+ // even if not directly reported below.
+ uint64_t mempool_sequence = GetAndIncrementSequence();
+
if (reason != MemPoolRemovalReason::BLOCK) {
// Notify clients that a transaction has been removed from the mempool
// for any reason except being included in a block. Clients interested
// in transactions included in blocks can subscribe to the BlockConnected
// notification.
- GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx(), reason);
+ GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx(), reason, mempool_sequence);
}
const uint256 hash = it->GetTx().GetHash();
diff --git a/src/txmempool.h b/src/txmempool.h
index 664fb5986a..f513f14af6 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -501,6 +501,11 @@ private:
mutable uint64_t m_epoch;
mutable bool m_has_epoch_guard;
+ // In-memory counter for external mempool tracking purposes.
+ // This number is incremented once every time a transaction
+ // is added or removed from the mempool for any reason.
+ mutable uint64_t m_sequence_number{1};
+
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
bool m_is_loaded GUARDED_BY(cs){false};
@@ -776,6 +781,15 @@ public:
return m_unbroadcast_txids.count(txid) != 0;
}
+ /** Guards this internal counter for external reporting */
+ uint64_t GetAndIncrementSequence() const EXCLUSIVE_LOCKS_REQUIRED(cs) {
+ return m_sequence_number++;
+ }
+
+ uint64_t GetSequence() const EXCLUSIVE_LOCKS_REQUIRED(cs) {
+ return m_sequence_number;
+ }
+
private:
/** UpdateForDescendants is used by UpdateTransactionsFromBlock to update
* the descendants for a single transaction that has been added to the
diff --git a/src/validation.cpp b/src/validation.cpp
index a96913e3a0..d4463bf17b 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -198,9 +198,6 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
std::unique_ptr<CBlockTreeDB> pblocktree;
-// See definition for documentation
-static void FindFilesToPruneManual(ChainstateManager& chainman, std::set<int>& setFilesToPrune, int nManualPruneHeight);
-static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false);
static FlatFileSeq BlockFileSeq();
@@ -1058,7 +1055,7 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
if (!Finalize(args, workspace)) return false;
- GetMainSignals().TransactionAddedToMempool(ptx);
+ GetMainSignals().TransactionAddedToMempool(ptx, m_pool.GetAndIncrementSequence());
return true;
}
@@ -2299,11 +2296,11 @@ bool CChainState::FlushStateToDisk(
if (nManualPruneHeight > 0) {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCH);
- FindFilesToPruneManual(g_chainman, setFilesToPrune, nManualPruneHeight);
+ m_blockman.FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight, m_chain.Height());
} else {
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
- FindFilesToPrune(g_chainman, setFilesToPrune, chainparams.PruneAfterHeight());
+ m_blockman.FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight(), m_chain.Height(), IsInitialBlockDownload());
fCheckForPruning = false;
}
if (!setFilesToPrune.empty()) {
@@ -3909,12 +3906,12 @@ uint64_t CalculateCurrentUsage()
return retval;
}
-void ChainstateManager::PruneOneBlockFile(const int fileNumber)
+void BlockManager::PruneOneBlockFile(const int fileNumber)
{
AssertLockHeld(cs_main);
LOCK(cs_LastBlockFile);
- for (const auto& entry : m_blockman.m_block_index) {
+ for (const auto& entry : m_block_index) {
CBlockIndex* pindex = entry.second;
if (pindex->nFile == fileNumber) {
pindex->nStatus &= ~BLOCK_HAVE_DATA;
@@ -3928,12 +3925,12 @@ void ChainstateManager::PruneOneBlockFile(const int fileNumber)
// to be downloaded again in order to consider its chain, at which
// point it would be considered as a candidate for
// m_blocks_unlinked or setBlockIndexCandidates.
- auto range = m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
+ auto range = m_blocks_unlinked.equal_range(pindex->pprev);
while (range.first != range.second) {
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
range.first++;
if (_it->second == pindex) {
- m_blockman.m_blocks_unlinked.erase(_it);
+ m_blocks_unlinked.erase(_it);
}
}
}
@@ -3954,22 +3951,23 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
}
}
-/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
-static void FindFilesToPruneManual(ChainstateManager& chainman, std::set<int>& setFilesToPrune, int nManualPruneHeight)
+void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
{
assert(fPruneMode && nManualPruneHeight > 0);
LOCK2(cs_main, cs_LastBlockFile);
- if (::ChainActive().Tip() == nullptr)
+ if (chain_tip_height < 0) {
return;
+ }
// last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
- unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP);
- int count=0;
+ unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
+ int count = 0;
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
- if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
+ if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
continue;
- chainman.PruneOneBlockFile(fileNumber);
+ }
+ PruneOneBlockFile(fileNumber);
setFilesToPrune.insert(fileNumber);
count++;
}
@@ -3987,46 +3985,31 @@ void PruneBlockFilesManual(int nManualPruneHeight)
}
}
-/**
- * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target.
- * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
- * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
- * (which in this case means the blockchain must be re-downloaded.)
- *
- * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
- * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
- * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
- * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
- * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
- * A db flag records the fact that at least some block files have been pruned.
- *
- * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
- */
-static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight)
+void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd)
{
LOCK2(cs_main, cs_LastBlockFile);
- if (::ChainActive().Tip() == nullptr || nPruneTarget == 0) {
+ if (chain_tip_height < 0 || nPruneTarget == 0) {
return;
}
- if ((uint64_t)::ChainActive().Tip()->nHeight <= nPruneAfterHeight) {
+ if ((uint64_t)chain_tip_height <= nPruneAfterHeight) {
return;
}
- unsigned int nLastBlockWeCanPrune = ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP;
+ unsigned int nLastBlockWeCanPrune = chain_tip_height - MIN_BLOCKS_TO_KEEP;
uint64_t nCurrentUsage = CalculateCurrentUsage();
// We don't check to prune until after we've allocated new space for files
// So we should leave a buffer under our target to account for another allocation
// before the next pruning.
uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;
uint64_t nBytesToPrune;
- int count=0;
+ int count = 0;
if (nCurrentUsage + nBuffer >= nPruneTarget) {
// On a prune event, the chainstate DB is flushed.
// To avoid excessive prune events negating the benefit of high dbcache
// values, we should not prune too rapidly.
// So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
- if (::ChainstateActive().IsInitialBlockDownload()) {
+ if (is_ibd) {
// Since this is only relevant during IBD, we use a fixed 10%
nBuffer += nPruneTarget / 10;
}
@@ -4034,17 +4017,20 @@ static void FindFilesToPrune(ChainstateManager& chainman, std::set<int>& setFile
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize;
- if (vinfoBlockFile[fileNumber].nSize == 0)
+ if (vinfoBlockFile[fileNumber].nSize == 0) {
continue;
+ }
- if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target?
+ if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target?
break;
+ }
// don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
- if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune)
+ if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
continue;
+ }
- chainman.PruneOneBlockFile(fileNumber);
+ PruneOneBlockFile(fileNumber);
// Queue up the files for removal
setFilesToPrune.insert(fileNumber);
nCurrentUsage -= nBytesToPrune;
@@ -4602,10 +4588,10 @@ void CChainState::UnloadBlockIndex() {
// May NOT be used after any connections are up as much
// of the peer-processing logic assumes a consistent
// block index state
-void UnloadBlockIndex(CTxMemPool* mempool)
+void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
{
LOCK(cs_main);
- g_chainman.Unload();
+ chainman.Unload();
pindexBestInvalid = nullptr;
pindexBestHeader = nullptr;
if (mempool) mempool->clear();
@@ -5220,20 +5206,6 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
return std::min<double>(pindex->nChainTx / fTxTotal, 1.0);
}
-class CMainCleanup
-{
-public:
- CMainCleanup() {}
- ~CMainCleanup() {
- // block headers
- BlockMap::iterator it1 = g_chainman.BlockIndex().begin();
- for (; it1 != g_chainman.BlockIndex().end(); it1++)
- delete (*it1).second;
- g_chainman.BlockIndex().clear();
- }
-};
-static CMainCleanup instance_of_cmaincleanup;
-
Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
if (m_active_chainstate != nullptr) {
// If a snapshot chainstate exists, it will always be our active.
diff --git a/src/validation.h b/src/validation.h
index 0da62093a3..d88bd07765 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -157,7 +157,7 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
bool LoadGenesisBlock(const CChainParams& chainparams);
/** Unload database information */
-void UnloadBlockIndex(CTxMemPool* mempool);
+void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman);
/** Run an instance of the script checking thread */
void ThreadScriptCheck(int worker_num);
/**
@@ -352,7 +352,31 @@ struct CBlockIndexWorkComparator
* This data is used mostly in `CChainState` - information about, e.g.,
* candidate tips is not maintained here.
*/
-class BlockManager {
+class BlockManager
+{
+ friend CChainState;
+
+private:
+ /* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
+ void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height);
+
+ /**
+ * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target.
+ * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
+ * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
+ * (which in this case means the blockchain must be re-downloaded.)
+ *
+ * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set.
+ * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
+ * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest).
+ * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
+ * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
+ * A db flag records the fact that at least some block files have been pruned.
+ *
+ * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
+ */
+ void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd);
+
public:
BlockMap m_block_index GUARDED_BY(cs_main);
@@ -403,6 +427,9 @@ public:
/** Create a new block index entry for a given block hash */
CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ //! Mark one block file as pruned (modify associated database entries)
+ void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
/**
* If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
* that it doesn't descend from an invalid block, and then add it to m_block_index.
@@ -412,6 +439,10 @@ public:
BlockValidationState& state,
const CChainParams& chainparams,
CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+
+ ~BlockManager() {
+ Unload();
+ }
};
/**
@@ -895,9 +926,6 @@ public:
*/
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
- //! Mark one block file as pruned (modify associated database entries)
- void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
//! Load the block tree and coins database from disk, initializing state if we're running with -reindex
bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 3dfbcc581c..1e07ff23ae 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -199,18 +199,18 @@ void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockInd
fInitialDownload);
}
-void CMainSignals::TransactionAddedToMempool(const CTransactionRef& tx) {
- auto event = [tx, this] {
- m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx); });
+void CMainSignals::TransactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {
+ auto event = [tx, mempool_sequence, this] {
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx, mempool_sequence); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
tx->GetHash().ToString(),
tx->GetWitnessHash().ToString());
}
-void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
- auto event = [tx, reason, this] {
- m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason); });
+void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {
+ auto event = [tx, reason, mempool_sequence, this] {
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
tx->GetHash().ToString(),
diff --git a/src/validationinterface.h b/src/validationinterface.h
index e96f2883fc..7c3ce00fbc 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -97,7 +97,8 @@ protected:
*
* Called on a background thread.
*/
- virtual void TransactionAddedToMempool(const CTransactionRef& tx) {}
+ virtual void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {}
+
/**
* Notifies listeners of a transaction leaving mempool.
*
@@ -130,7 +131,7 @@ protected:
*
* Called on a background thread.
*/
- virtual void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {}
+ virtual void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {}
/**
* Notifies listeners of a block being connected.
* Provides a vector of transactions evicted from the mempool as a result.
@@ -197,8 +198,8 @@ public:
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
- void TransactionAddedToMempool(const CTransactionRef&);
- void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason);
+ void TransactionAddedToMempool(const CTransactionRef&, uint64_t mempool_sequence);
+ void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason, uint64_t mempool_sequence);
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex);
void BlockDisconnected(const std::shared_ptr<const CBlock> &, const CBlockIndex* pindex);
void ChainStateFlushed(const CBlockLocator &);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 62a3206802..2217bc2875 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -231,9 +231,9 @@ static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const U
}
}
-static UniValue getnewaddress(const JSONRPCRequest& request)
+static RPCHelpMan getnewaddress()
{
- RPCHelpMan{"getnewaddress",
+ return 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",
@@ -248,8 +248,8 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
HelpExampleCli("getnewaddress", "")
+ HelpExampleRpc("getnewaddress", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -279,11 +279,13 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
}
return EncodeDestination(dest);
+},
+ };
}
-static UniValue getrawchangeaddress(const JSONRPCRequest& request)
+static RPCHelpMan getrawchangeaddress()
{
- RPCHelpMan{"getrawchangeaddress",
+ return RPCHelpMan{"getrawchangeaddress",
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n",
{
@@ -296,8 +298,8 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
HelpExampleCli("getrawchangeaddress", "")
+ HelpExampleRpc("getrawchangeaddress", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -321,12 +323,14 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
}
return EncodeDestination(dest);
+},
+ };
}
-static UniValue setlabel(const JSONRPCRequest& request)
+static RPCHelpMan setlabel()
{
- RPCHelpMan{"setlabel",
+ return RPCHelpMan{"setlabel",
"\nSets the label associated with the given address.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
@@ -337,8 +341,8 @@ static UniValue setlabel(const JSONRPCRequest& request)
HelpExampleCli("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\" \"tabby\"")
+ HelpExampleRpc("setlabel", "\"" + EXAMPLE_ADDRESS[0] + "\", \"tabby\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -359,6 +363,8 @@ static UniValue setlabel(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_fee_outputs, std::vector<CRecipient> &recipients) {
@@ -411,9 +417,9 @@ UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std
return tx->GetHash().GetHex();
}
-static UniValue sendtoaddress(const JSONRPCRequest& request)
+static RPCHelpMan sendtoaddress()
{
- RPCHelpMan{"sendtoaddress",
+ return RPCHelpMan{"sendtoaddress",
"\nSend an amount to a given address." +
HELP_REQUIRING_PASSPHRASE,
{
@@ -444,8 +450,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" false true 2 " + (CURRENCY_ATOM + "/B"))
+ HelpExampleRpc("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 0.1, \"donation\", \"seans outpost\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -493,11 +499,13 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
return SendMoney(pwallet, coin_control, recipients, mapValue);
+},
+ };
}
-static UniValue listaddressgroupings(const JSONRPCRequest& request)
+static RPCHelpMan listaddressgroupings()
{
- RPCHelpMan{"listaddressgroupings",
+ return 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",
@@ -520,8 +528,8 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
HelpExampleCli("listaddressgroupings", "")
+ HelpExampleRpc("listaddressgroupings", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -552,11 +560,13 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
jsonGroupings.push_back(jsonGrouping);
}
return jsonGroupings;
+},
+ };
}
-static UniValue signmessage(const JSONRPCRequest& request)
+static RPCHelpMan signmessage()
{
- RPCHelpMan{"signmessage",
+ return RPCHelpMan{"signmessage",
"\nSign a message with the private key of an address" +
HELP_REQUIRING_PASSPHRASE,
{
@@ -576,8 +586,8 @@ static UniValue signmessage(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -608,6 +618,8 @@ static UniValue signmessage(const JSONRPCRequest& request)
}
return signature;
+},
+ };
}
static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
@@ -656,9 +668,9 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
}
-static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
+static RPCHelpMan getreceivedbyaddress()
{
- RPCHelpMan{"getreceivedbyaddress",
+ return RPCHelpMan{"getreceivedbyaddress",
"\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for transactions."},
@@ -677,8 +689,8 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -690,12 +702,14 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ false));
+},
+ };
}
-static UniValue getreceivedbylabel(const JSONRPCRequest& request)
+static RPCHelpMan getreceivedbylabel()
{
- RPCHelpMan{"getreceivedbylabel",
+ return RPCHelpMan{"getreceivedbylabel",
"\nReturns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.\n",
{
{"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The selected label, may be the default label using \"\"."},
@@ -714,8 +728,8 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -727,12 +741,14 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
return ValueFromAmount(GetReceived(*pwallet, request.params, /* by_label */ true));
+},
+ };
}
-static UniValue getbalance(const JSONRPCRequest& request)
+static RPCHelpMan getbalance()
{
- RPCHelpMan{"getbalance",
+ return 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",
@@ -753,8 +769,8 @@ static UniValue getbalance(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getbalance", "\"*\", 6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -782,17 +798,19 @@ static UniValue getbalance(const JSONRPCRequest& request)
const auto bal = pwallet->GetBalance(min_depth, avoid_reuse);
return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
+},
+ };
}
-static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
+static RPCHelpMan getunconfirmedbalance()
{
- RPCHelpMan{"getunconfirmedbalance",
+ return RPCHelpMan{"getunconfirmedbalance",
"DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
{},
RPCResult{RPCResult::Type::NUM, "", "The balance"},
RPCExamples{""},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -804,12 +822,14 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
LOCK(pwallet->cs_wallet);
return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending);
+},
+ };
}
-static UniValue sendmany(const JSONRPCRequest& request)
+static RPCHelpMan sendmany()
{
- RPCHelpMan{"sendmany",
+ return RPCHelpMan{"sendmany",
"\nSend multiple times. Amounts are double-precision floating point numbers." +
HELP_REQUIRING_PASSPHRASE,
{
@@ -848,8 +868,8 @@ static UniValue sendmany(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendmany", "\"\", {\"" + EXAMPLE_ADDRESS[0] + "\":0.01,\"" + EXAMPLE_ADDRESS[1] + "\":0.02}, 6, \"testing\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -884,11 +904,13 @@ static UniValue sendmany(const JSONRPCRequest& request)
ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
return SendMoney(pwallet, coin_control, recipients, std::move(mapValue));
+},
+ };
}
-static UniValue addmultisigaddress(const JSONRPCRequest& request)
+static RPCHelpMan addmultisigaddress()
{
- RPCHelpMan{"addmultisigaddress",
+ return RPCHelpMan{"addmultisigaddress",
"\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
"This functionality is only intended for use with non-watchonly addresses.\n"
@@ -918,8 +940,8 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("addmultisigaddress", "2, \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -965,6 +987,8 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
result.pushKV("redeemScript", HexStr(inner));
result.pushKV("descriptor", descriptor->ToString());
return result;
+},
+ };
}
struct tallyitem
@@ -1125,9 +1149,9 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
return ret;
}
-static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
+static RPCHelpMan listreceivedbyaddress()
{
- RPCHelpMan{"listreceivedbyaddress",
+ return RPCHelpMan{"listreceivedbyaddress",
"\nList balances by receiving address.\n",
{
{"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
@@ -1158,8 +1182,8 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
+ HelpExampleRpc("listreceivedbyaddress", "6, true, true")
+ HelpExampleRpc("listreceivedbyaddress", "6, true, true, \"" + EXAMPLE_ADDRESS[0] + "\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -1171,11 +1195,13 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
return ListReceived(pwallet, request.params, false);
+},
+ };
}
-static UniValue listreceivedbylabel(const JSONRPCRequest& request)
+static RPCHelpMan listreceivedbylabel()
{
- RPCHelpMan{"listreceivedbylabel",
+ return RPCHelpMan{"listreceivedbylabel",
"\nList received transactions by label.\n",
{
{"minconf", RPCArg::Type::NUM, /* default */ "1", "The minimum number of confirmations before payments are included."},
@@ -1199,8 +1225,8 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
+ HelpExampleCli("listreceivedbylabel", "6 true")
+ HelpExampleRpc("listreceivedbylabel", "6, true, true")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -1212,6 +1238,8 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
LOCK(pwallet->cs_wallet);
return ListReceived(pwallet, request.params, true);
+},
+ };
}
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
@@ -1331,9 +1359,9 @@ static const std::vector<RPCResult> TransactionDescriptionString()
"may be unknown for unconfirmed transactions not in the mempool"}};
}
-UniValue listtransactions(const JSONRPCRequest& request)
+static RPCHelpMan listtransactions()
{
- RPCHelpMan{"listtransactions",
+ return 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",
{
@@ -1378,8 +1406,8 @@ UniValue listtransactions(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listtransactions", "\"*\", 20, 100")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -1439,11 +1467,13 @@ UniValue listtransactions(const JSONRPCRequest& request)
UniValue result{UniValue::VARR};
result.push_backV({ txs.rend() - nFrom - nCount, txs.rend() - nFrom }); // Return oldest to newest
return result;
+},
+ };
}
-static UniValue listsinceblock(const JSONRPCRequest& request)
+static RPCHelpMan listsinceblock()
{
- RPCHelpMan{"listsinceblock",
+ return 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",
@@ -1494,8 +1524,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
+ HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6")
+ HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
@@ -1578,11 +1608,13 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
ret.pushKV("lastblock", lastblock.GetHex());
return ret;
+},
+ };
}
-static UniValue gettransaction(const JSONRPCRequest& request)
+static RPCHelpMan gettransaction()
{
- RPCHelpMan{"gettransaction",
+ return RPCHelpMan{"gettransaction",
"\nGet detailed information about in-wallet transaction <txid>\n",
{
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
@@ -1634,8 +1666,8 @@ static UniValue gettransaction(const JSONRPCRequest& request)
+ HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" false true")
+ HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -1688,11 +1720,13 @@ static UniValue gettransaction(const JSONRPCRequest& request)
}
return entry;
+},
+ };
}
-static UniValue abandontransaction(const JSONRPCRequest& request)
+static RPCHelpMan abandontransaction()
{
- RPCHelpMan{"abandontransaction",
+ return 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"
@@ -1706,8 +1740,8 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
+ HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1728,12 +1762,14 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue backupwallet(const JSONRPCRequest& request)
+static RPCHelpMan backupwallet()
{
- RPCHelpMan{"backupwallet",
+ return RPCHelpMan{"backupwallet",
"\nSafely copies current wallet file to destination, which can be a directory or a path with filename.\n",
{
{"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The destination directory or file"},
@@ -1743,8 +1779,8 @@ static UniValue backupwallet(const JSONRPCRequest& request)
HelpExampleCli("backupwallet", "\"backup.dat\"")
+ HelpExampleRpc("backupwallet", "\"backup.dat\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -1761,12 +1797,14 @@ static UniValue backupwallet(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue keypoolrefill(const JSONRPCRequest& request)
+static RPCHelpMan keypoolrefill()
{
- RPCHelpMan{"keypoolrefill",
+ return RPCHelpMan{"keypoolrefill",
"\nFills the keypool."+
HELP_REQUIRING_PASSPHRASE,
{
@@ -1777,8 +1815,8 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
HelpExampleCli("keypoolrefill", "")
+ HelpExampleRpc("keypoolrefill", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1805,12 +1843,14 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue walletpassphrase(const JSONRPCRequest& request)
+static RPCHelpMan walletpassphrase()
{
- RPCHelpMan{"walletpassphrase",
+ return 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"
"\nNote:\n"
@@ -1829,8 +1869,8 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1899,12 +1939,14 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
}, nSleepTime);
return NullUniValue;
+},
+ };
}
-static UniValue walletpassphrasechange(const JSONRPCRequest& request)
+static RPCHelpMan walletpassphrasechange()
{
- RPCHelpMan{"walletpassphrasechange",
+ return RPCHelpMan{"walletpassphrasechange",
"\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n",
{
{"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO, "The current passphrase"},
@@ -1915,8 +1957,8 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"")
+ HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1946,12 +1988,14 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
}
return NullUniValue;
+},
+ };
}
-static UniValue walletlock(const JSONRPCRequest& request)
+static RPCHelpMan walletlock()
{
- RPCHelpMan{"walletlock",
+ return 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",
@@ -1967,8 +2011,8 @@ static UniValue walletlock(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletlock", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -1983,12 +2027,14 @@ static UniValue walletlock(const JSONRPCRequest& request)
pwallet->nRelockTime = 0;
return NullUniValue;
+},
+ };
}
-static UniValue encryptwallet(const JSONRPCRequest& request)
+static RPCHelpMan encryptwallet()
{
- RPCHelpMan{"encryptwallet",
+ return 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"
@@ -2010,8 +2056,8 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -2041,11 +2087,13 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
}
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)
+static RPCHelpMan lockunspent()
{
- RPCHelpMan{"lockunspent",
+ return 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"
@@ -2082,8 +2130,8 @@ static UniValue lockunspent(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -2165,11 +2213,13 @@ static UniValue lockunspent(const JSONRPCRequest& request)
}
return true;
+},
+ };
}
-static UniValue listlockunspent(const JSONRPCRequest& request)
+static RPCHelpMan listlockunspent()
{
- RPCHelpMan{"listlockunspent",
+ return RPCHelpMan{"listlockunspent",
"\nReturns list of temporarily unspendable outputs.\n"
"See the lockunspent call to lock and unlock transactions for spending.\n",
{},
@@ -2195,8 +2245,8 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlockunspent", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -2217,11 +2267,13 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
}
return ret;
+},
+ };
}
-static UniValue settxfee(const JSONRPCRequest& request)
+static RPCHelpMan settxfee()
{
- RPCHelpMan{"settxfee",
+ return RPCHelpMan{"settxfee",
"\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n"
"Can be deactivated by passing 0 as the fee. In that case automatic fee selection will be used by default.\n",
{
@@ -2234,8 +2286,8 @@ static UniValue settxfee(const JSONRPCRequest& request)
HelpExampleCli("settxfee", "0.00001")
+ HelpExampleRpc("settxfee", "0.00001")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -2257,11 +2309,13 @@ static UniValue settxfee(const JSONRPCRequest& request)
pwallet->m_pay_tx_fee = tx_fee_rate;
return true;
+},
+ };
}
-static UniValue getbalances(const JSONRPCRequest& request)
+static RPCHelpMan getbalances()
{
- RPCHelpMan{
+ return RPCHelpMan{
"getbalances",
"Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
{},
@@ -2286,8 +2340,8 @@ static UniValue getbalances(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("getbalances", "") +
HelpExampleRpc("getbalances", "")},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
if (!rpc_wallet) return NullUniValue;
CWallet& wallet = *rpc_wallet;
@@ -2322,11 +2376,13 @@ static UniValue getbalances(const JSONRPCRequest& request)
balances.pushKV("watchonly", balances_watchonly);
}
return balances;
+},
+ };
}
-static UniValue getwalletinfo(const JSONRPCRequest& request)
+static RPCHelpMan getwalletinfo()
{
- RPCHelpMan{"getwalletinfo",
+ return RPCHelpMan{"getwalletinfo",
"Returns an object containing various wallet state info.\n",
{},
RPCResult{
@@ -2359,8 +2415,8 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
HelpExampleCli("getwalletinfo", "")
+ HelpExampleRpc("getwalletinfo", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -2414,11 +2470,13 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
}
obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
return obj;
+},
+ };
}
-static UniValue listwalletdir(const JSONRPCRequest& request)
+static RPCHelpMan listwalletdir()
{
- RPCHelpMan{"listwalletdir",
+ return RPCHelpMan{"listwalletdir",
"Returns a list of wallets in the wallet directory.\n",
{},
RPCResult{
@@ -2437,8 +2495,8 @@ static UniValue listwalletdir(const JSONRPCRequest& request)
HelpExampleCli("listwalletdir", "")
+ HelpExampleRpc("listwalletdir", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue wallets(UniValue::VARR);
for (const auto& path : ListWalletDir()) {
UniValue wallet(UniValue::VOBJ);
@@ -2449,11 +2507,13 @@ static UniValue listwalletdir(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
result.pushKV("wallets", wallets);
return result;
+},
+ };
}
-static UniValue listwallets(const JSONRPCRequest& request)
+static RPCHelpMan listwallets()
{
- RPCHelpMan{"listwallets",
+ return RPCHelpMan{"listwallets",
"Returns a list of currently loaded wallets.\n"
"For full information on the wallet, use \"getwalletinfo\"\n",
{},
@@ -2467,8 +2527,8 @@ static UniValue listwallets(const JSONRPCRequest& request)
HelpExampleCli("listwallets", "")
+ HelpExampleRpc("listwallets", "")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
UniValue obj(UniValue::VARR);
for (const std::shared_ptr<CWallet>& wallet : GetWallets()) {
@@ -2477,11 +2537,13 @@ static UniValue listwallets(const JSONRPCRequest& request)
}
return obj;
+},
+ };
}
-static UniValue loadwallet(const JSONRPCRequest& request)
+static RPCHelpMan loadwallet()
{
- RPCHelpMan{"loadwallet",
+ return 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 -rescan, etc).\n",
@@ -2500,8 +2562,8 @@ static UniValue loadwallet(const JSONRPCRequest& request)
HelpExampleCli("loadwallet", "\"test.dat\"")
+ HelpExampleRpc("loadwallet", "\"test.dat\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
WalletContext& context = EnsureWalletContext(request.context);
const std::string name(request.params[0].get_str());
@@ -2524,15 +2586,18 @@ static UniValue loadwallet(const JSONRPCRequest& request)
obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
return obj;
+},
+ };
}
-static UniValue setwalletflag(const JSONRPCRequest& request)
+static RPCHelpMan setwalletflag()
{
std::string flags = "";
for (auto& it : WALLET_FLAG_MAP)
if (it.second & MUTABLE_WALLET_FLAGS)
flags += (flags == "" ? "" : ", ") + it.first;
- RPCHelpMan{"setwalletflag",
+
+ return RPCHelpMan{"setwalletflag",
"\nChange the state of the given wallet flag for a wallet.\n",
{
{"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
@@ -2550,8 +2615,8 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
HelpExampleCli("setwalletflag", "avoid_reuse")
+ HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -2589,11 +2654,13 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
}
return res;
+},
+ };
}
-static UniValue createwallet(const JSONRPCRequest& request)
+static RPCHelpMan createwallet()
{
- RPCHelpMan{
+ return RPCHelpMan{
"createwallet",
"\nCreates and loads a new wallet.\n",
{
@@ -2616,8 +2683,8 @@ static UniValue createwallet(const JSONRPCRequest& request)
HelpExampleCli("createwallet", "\"testwallet\"")
+ HelpExampleRpc("createwallet", "\"testwallet\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
WalletContext& context = EnsureWalletContext(request.context);
uint64_t flags = 0;
if (!request.params[1].isNull() && request.params[1].get_bool()) {
@@ -2664,11 +2731,13 @@ static UniValue createwallet(const JSONRPCRequest& request)
obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
return obj;
+},
+ };
}
-static UniValue unloadwallet(const JSONRPCRequest& request)
+static RPCHelpMan unloadwallet()
{
- RPCHelpMan{"unloadwallet",
+ return 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.",
{
@@ -2682,8 +2751,8 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
HelpExampleCli("unloadwallet", "wallet_name")
+ HelpExampleRpc("unloadwallet", "wallet_name")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::string wallet_name;
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
if (!request.params[0].isNull()) {
@@ -2712,11 +2781,13 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
result.pushKV("warning", Join(warnings, Untranslated("\n")).original);
return result;
+},
+ };
}
-static UniValue listunspent(const JSONRPCRequest& request)
+static RPCHelpMan listunspent()
{
- RPCHelpMan{
+ return RPCHelpMan{
"listunspent",
"\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
@@ -2771,8 +2842,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'")
+ HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -2933,6 +3004,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
}
return results;
+},
+ };
}
void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options, CCoinControl& coinControl)
@@ -3064,9 +3137,9 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
}
}
-static UniValue fundrawtransaction(const JSONRPCRequest& request)
+static RPCHelpMan fundrawtransaction()
{
- RPCHelpMan{"fundrawtransaction",
+ return RPCHelpMan{"fundrawtransaction",
"\nIf the transaction has no inputs, they will be automatically selected to meet its out value.\n"
"It will add at most one change output to the outputs.\n"
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
@@ -3132,8 +3205,8 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
"\nSend the transaction\n"
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -3161,11 +3234,13 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
result.pushKV("changepos", change_position);
return result;
+},
+ };
}
-UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
+RPCHelpMan signrawtransactionwithwallet()
{
- RPCHelpMan{"signrawtransactionwithwallet",
+ return 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." +
@@ -3216,8 +3291,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"")
+ HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -3252,13 +3327,15 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
return result;
+},
+ };
}
-static UniValue bumpfee(const JSONRPCRequest& request)
+static RPCHelpMan bumpfee_helper(std::string method_name)
{
- bool want_psbt = request.strMethod == "psbtbumpfee";
+ bool want_psbt = method_name == "psbtbumpfee";
- RPCHelpMan{request.strMethod,
+ return RPCHelpMan{method_name,
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
+ std::string(want_psbt ? "Returns a PSBT instead of creating and signing a new transaction.\n" : "") +
"An opt-in RBF transaction with the given txid must be in the wallet.\n"
@@ -3308,10 +3385,10 @@ static UniValue bumpfee(const JSONRPCRequest& request)
},
RPCExamples{
"\nBump the fee, get the new transaction\'s" + std::string(want_psbt ? "psbt" : "txid") + "\n" +
- HelpExampleCli(request.strMethod, "<txid>")
+ HelpExampleCli(method_name, "<txid>")
},
- }.Check(request);
-
+ [want_psbt](const RPCHelpMan& self, const JSONRPCRequest& request) mutable -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -3438,16 +3515,16 @@ static UniValue bumpfee(const JSONRPCRequest& request)
result.pushKV("errors", result_errors);
return result;
+},
+ };
}
-static UniValue psbtbumpfee(const JSONRPCRequest& request)
-{
- return bumpfee(request);
-}
+static RPCHelpMan bumpfee() { return bumpfee_helper("bumpfee"); }
+static RPCHelpMan psbtbumpfee() { return bumpfee_helper("psbtbumpfee"); }
-UniValue rescanblockchain(const JSONRPCRequest& request)
+static RPCHelpMan rescanblockchain()
{
- RPCHelpMan{"rescanblockchain",
+ return RPCHelpMan{"rescanblockchain",
"\nRescan the local blockchain for wallet related transactions.\n"
"Note: Use \"getwalletinfo\" to query the scanning progress.\n",
{
@@ -3465,8 +3542,8 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
HelpExampleCli("rescanblockchain", "100000 120000")
+ HelpExampleRpc("rescanblockchain", "100000, 120000")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -3522,6 +3599,8 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
response.pushKV("start_height", start_height);
response.pushKV("stop_height", result.last_scanned_height ? *result.last_scanned_height : UniValue());
return response;
+},
+ };
}
class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue>
@@ -3641,9 +3720,9 @@ static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool v
return ret;
}
-UniValue getaddressinfo(const JSONRPCRequest& request)
+RPCHelpMan getaddressinfo()
{
- RPCHelpMan{"getaddressinfo",
+ return RPCHelpMan{"getaddressinfo",
"\nReturn information about the given bitcoin address.\n"
"Some of the information will only be present if the address is in the active wallet.\n",
{
@@ -3694,8 +3773,8 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
HelpExampleCli("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
HelpExampleRpc("getaddressinfo", "\"" + EXAMPLE_ADDRESS[0] + "\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -3759,11 +3838,13 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
ret.pushKV("labels", std::move(labels));
return ret;
+},
+ };
}
-static UniValue getaddressesbylabel(const JSONRPCRequest& request)
+static RPCHelpMan getaddressesbylabel()
{
- RPCHelpMan{"getaddressesbylabel",
+ return RPCHelpMan{"getaddressesbylabel",
"\nReturns the list of addresses assigned the specified label.\n",
{
{"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
@@ -3781,8 +3862,8 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
HelpExampleCli("getaddressesbylabel", "\"tabby\"")
+ HelpExampleRpc("getaddressesbylabel", "\"tabby\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -3816,11 +3897,13 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
}
return ret;
+},
+ };
}
-static UniValue listlabels(const JSONRPCRequest& request)
+static RPCHelpMan listlabels()
{
- RPCHelpMan{"listlabels",
+ return RPCHelpMan{"listlabels",
"\nReturns the list of all labels, or labels that are assigned to addresses with a specific purpose.\n",
{
{"purpose", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
@@ -3841,8 +3924,8 @@ static UniValue listlabels(const JSONRPCRequest& request)
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlabels", "receive")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -3869,6 +3952,8 @@ static UniValue listlabels(const JSONRPCRequest& request)
}
return ret;
+},
+ };
}
static RPCHelpMan send()
@@ -4050,9 +4135,9 @@ static RPCHelpMan send()
};
}
-UniValue sethdseed(const JSONRPCRequest& request)
+static RPCHelpMan sethdseed()
{
- RPCHelpMan{"sethdseed",
+ return 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." +
@@ -4072,8 +4157,8 @@ UniValue sethdseed(const JSONRPCRequest& request)
+ HelpExampleCli("sethdseed", "true \"wifkey\"")
+ HelpExampleRpc("sethdseed", "true, \"wifkey\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -4118,11 +4203,13 @@ UniValue sethdseed(const JSONRPCRequest& request)
if (flush_key_pool) spk_man.NewKeyPool();
return NullUniValue;
+},
+ };
}
-UniValue walletprocesspsbt(const JSONRPCRequest& request)
+static RPCHelpMan walletprocesspsbt()
{
- RPCHelpMan{"walletprocesspsbt",
+ return RPCHelpMan{"walletprocesspsbt",
"\nUpdate a PSBT with input information from our wallet and then sign inputs\n"
"that we can sign for." +
HELP_REQUIRING_PASSPHRASE,
@@ -4148,8 +4235,8 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("walletprocesspsbt", "\"psbt\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
@@ -4182,11 +4269,13 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
result.pushKV("complete", complete);
return result;
+},
+ };
}
-UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
+static RPCHelpMan walletcreatefundedpsbt()
{
- RPCHelpMan{"walletcreatefundedpsbt",
+ return RPCHelpMan{"walletcreatefundedpsbt",
"\nCreates and funds a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator and Updater roles.\n",
{
@@ -4257,8 +4346,8 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
"\nCreate a transaction with no inputs\n"
+ HelpExampleCli("walletcreatefundedpsbt", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -4307,11 +4396,13 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
result.pushKV("fee", ValueFromAmount(fee));
result.pushKV("changepos", change_position);
return result;
+},
+ };
}
-static UniValue upgradewallet(const JSONRPCRequest& request)
+static RPCHelpMan upgradewallet()
{
- RPCHelpMan{"upgradewallet",
+ return RPCHelpMan{"upgradewallet",
"\nUpgrade the wallet. Upgrades to the latest version if no version number is specified\n"
"New keys may be generated and a new wallet backup will need to be made.",
{
@@ -4321,9 +4412,9 @@ static UniValue upgradewallet(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("upgradewallet", "169900")
+ HelpExampleRpc("upgradewallet", "169900")
- }
- }.Check(request);
-
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
@@ -4343,6 +4434,8 @@ static UniValue upgradewallet(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
}
return error.original;
+},
+ };
}
RPCHelpMan abortrescan();
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index fb1e91282b..184a16e91d 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -34,6 +34,6 @@ void EnsureWalletIsUnlocked(const CWallet*);
WalletContext& EnsureWalletContext(const util::Ref& context);
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);
-UniValue getaddressinfo(const JSONRPCRequest& request);
-UniValue signrawtransactionwithwallet(const JSONRPCRequest& request);
+RPCHelpMan getaddressinfo();
+RPCHelpMan signrawtransactionwithwallet();
#endif //BITCOIN_WALLET_RPCWALLET_H
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 6b98482f98..4393bb7701 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -126,7 +126,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Prune the older block file.
{
LOCK(cs_main);
- Assert(m_node.chainman)->PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
}
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
@@ -152,7 +152,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Prune the remaining block file.
{
LOCK(cs_main);
- Assert(m_node.chainman)->PruneOneBlockFile(newTip->GetBlockPos().nFile);
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(newTip->GetBlockPos().nFile);
}
UnlinkPrunedFiles({newTip->GetBlockPos().nFile});
@@ -189,7 +189,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
// Prune the older block file.
{
LOCK(cs_main);
- Assert(m_node.chainman)->PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
}
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 73e11a5b52..66857dbb39 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1177,7 +1177,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmatio
MarkInputsDirty(ptx);
}
-void CWallet::transactionAddedToMempool(const CTransactionRef& tx) {
+void CWallet::transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {
LOCK(cs_wallet);
SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
@@ -1187,7 +1187,7 @@ void CWallet::transactionAddedToMempool(const CTransactionRef& tx) {
}
}
-void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
+void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {
LOCK(cs_wallet);
auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
@@ -1234,7 +1234,7 @@ void CWallet::blockConnected(const CBlock& block, int height)
m_last_block_processed = block_hash;
for (size_t index = 0; index < block.vtx.size(); index++) {
SyncTransaction(block.vtx[index], {CWalletTx::Status::CONFIRMED, height, block_hash, (int)index});
- transactionRemovedFromMempool(block.vtx[index], MemPoolRemovalReason::BLOCK);
+ transactionRemovedFromMempool(block.vtx[index], MemPoolRemovalReason::BLOCK, 0 /* mempool_sequence */);
}
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index c54480612a..f15712dd0e 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -900,7 +900,7 @@ public:
CWalletTx* AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx=nullptr, bool fFlushOnClose=true);
bool LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void transactionAddedToMempool(const CTransactionRef& tx) override;
+ void transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) override;
void blockConnected(const CBlock& block, int height) override;
void blockDisconnected(const CBlock& block, int height) override;
void updatedBlockTip() override;
@@ -922,7 +922,7 @@ public:
uint256 last_failed_block;
};
ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate);
- void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) override;
+ void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override;
void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions();
struct Balance {
diff --git a/src/zmq/zmqabstractnotifier.cpp b/src/zmq/zmqabstractnotifier.cpp
index 0d0428f3c0..3938f6fd2c 100644
--- a/src/zmq/zmqabstractnotifier.cpp
+++ b/src/zmq/zmqabstractnotifier.cpp
@@ -22,3 +22,23 @@ bool CZMQAbstractNotifier::NotifyTransaction(const CTransaction &/*transaction*/
{
return true;
}
+
+bool CZMQAbstractNotifier::NotifyBlockConnect(const CBlockIndex * /*CBlockIndex*/)
+{
+ return true;
+}
+
+bool CZMQAbstractNotifier::NotifyBlockDisconnect(const CBlockIndex * /*CBlockIndex*/)
+{
+ return true;
+}
+
+bool CZMQAbstractNotifier::NotifyTransactionAcceptance(const CTransaction &/*transaction*/, uint64_t mempool_sequence)
+{
+ return true;
+}
+
+bool CZMQAbstractNotifier::NotifyTransactionRemoval(const CTransaction &/*transaction*/, uint64_t mempool_sequence)
+{
+ return true;
+}
diff --git a/src/zmq/zmqabstractnotifier.h b/src/zmq/zmqabstractnotifier.h
index 34d7e5ef03..dddba8d6b6 100644
--- a/src/zmq/zmqabstractnotifier.h
+++ b/src/zmq/zmqabstractnotifier.h
@@ -44,7 +44,17 @@ public:
virtual bool Initialize(void *pcontext) = 0;
virtual void Shutdown() = 0;
+ // Notifies of ConnectTip result, i.e., new active tip only
virtual bool NotifyBlock(const CBlockIndex *pindex);
+ // Notifies of every block connection
+ virtual bool NotifyBlockConnect(const CBlockIndex *pindex);
+ // Notifies of every block disconnection
+ virtual bool NotifyBlockDisconnect(const CBlockIndex *pindex);
+ // Notifies of every mempool acceptance
+ virtual bool NotifyTransactionAcceptance(const CTransaction &transaction, uint64_t mempool_sequence);
+ // Notifies of every mempool removal, except inclusion in blocks
+ virtual bool NotifyTransactionRemoval(const CTransaction &transaction, uint64_t mempool_sequence);
+ // Notifies of transactions added to mempool or appearing in blocks
virtual bool NotifyTransaction(const CTransaction &transaction);
protected:
diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp
index a22772baed..a7e9a34269 100644
--- a/src/zmq/zmqnotificationinterface.cpp
+++ b/src/zmq/zmqnotificationinterface.cpp
@@ -36,6 +36,7 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create()
factories["pubhashtx"] = CZMQAbstractNotifier::Create<CZMQPublishHashTransactionNotifier>;
factories["pubrawblock"] = CZMQAbstractNotifier::Create<CZMQPublishRawBlockNotifier>;
factories["pubrawtx"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionNotifier>;
+ factories["pubsequence"] = CZMQAbstractNotifier::Create<CZMQPublishSequenceNotifier>;
std::list<std::unique_ptr<CZMQAbstractNotifier>> notifiers;
for (const auto& entry : factories)
@@ -140,31 +141,53 @@ void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, co
});
}
-void CZMQNotificationInterface::TransactionAddedToMempool(const CTransactionRef& ptx)
+void CZMQNotificationInterface::TransactionAddedToMempool(const CTransactionRef& ptx, uint64_t mempool_sequence)
{
- // Used by BlockConnected and BlockDisconnected as well, because they're
- // all the same external callback.
const CTransaction& tx = *ptx;
- TryForEachAndRemoveFailed(notifiers, [&tx](CZMQAbstractNotifier* notifier) {
- return notifier->NotifyTransaction(tx);
+ TryForEachAndRemoveFailed(notifiers, [&tx, mempool_sequence](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyTransaction(tx) && notifier->NotifyTransactionAcceptance(tx, mempool_sequence);
+ });
+}
+
+void CZMQNotificationInterface::TransactionRemovedFromMempool(const CTransactionRef& ptx, MemPoolRemovalReason reason, uint64_t mempool_sequence)
+{
+ // Called for all non-block inclusion reasons
+ const CTransaction& tx = *ptx;
+
+ TryForEachAndRemoveFailed(notifiers, [&tx, mempool_sequence](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyTransactionRemoval(tx, mempool_sequence);
});
}
void CZMQNotificationInterface::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected)
{
for (const CTransactionRef& ptx : pblock->vtx) {
- // Do a normal notify for each transaction added in the block
- TransactionAddedToMempool(ptx);
+ const CTransaction& tx = *ptx;
+ TryForEachAndRemoveFailed(notifiers, [&tx](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyTransaction(tx);
+ });
}
+
+ // Next we notify BlockConnect listeners for *all* blocks
+ TryForEachAndRemoveFailed(notifiers, [pindexConnected](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyBlockConnect(pindexConnected);
+ });
}
void CZMQNotificationInterface::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected)
{
for (const CTransactionRef& ptx : pblock->vtx) {
- // Do a normal notify for each transaction removed in block disconnection
- TransactionAddedToMempool(ptx);
+ const CTransaction& tx = *ptx;
+ TryForEachAndRemoveFailed(notifiers, [&tx](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyTransaction(tx);
+ });
}
+
+ // Next we notify BlockDisconnect listeners for *all* blocks
+ TryForEachAndRemoveFailed(notifiers, [pindexDisconnected](CZMQAbstractNotifier* notifier) {
+ return notifier->NotifyBlockDisconnect(pindexDisconnected);
+ });
}
CZMQNotificationInterface* g_zmq_notification_interface = nullptr;
diff --git a/src/zmq/zmqnotificationinterface.h b/src/zmq/zmqnotificationinterface.h
index 0686960ed4..788a383517 100644
--- a/src/zmq/zmqnotificationinterface.h
+++ b/src/zmq/zmqnotificationinterface.h
@@ -26,7 +26,8 @@ protected:
void Shutdown();
// CValidationInterface
- void TransactionAddedToMempool(const CTransactionRef& tx) override;
+ void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) override;
+ void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override;
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override;
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexDisconnected) override;
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override;
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index d4d21b05ba..a0e7a0a600 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -26,6 +26,7 @@ static const char *MSG_HASHBLOCK = "hashblock";
static const char *MSG_HASHTX = "hashtx";
static const char *MSG_RAWBLOCK = "rawblock";
static const char *MSG_RAWTX = "rawtx";
+static const char *MSG_SEQUENCE = "sequence";
// Internal function to send multipart message
static int zmq_send_multipart(void *sock, const void* data, size_t size, ...)
@@ -225,3 +226,51 @@ bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &tr
ss << transaction;
return SendZmqMessage(MSG_RAWTX, &(*ss.begin()), ss.size());
}
+
+
+// TODO: Dedup this code to take label char, log string
+bool CZMQPublishSequenceNotifier::NotifyBlockConnect(const CBlockIndex *pindex)
+{
+ uint256 hash = pindex->GetBlockHash();
+ LogPrint(BCLog::ZMQ, "zmq: Publish sequence block connect %s\n", hash.GetHex());
+ char data[sizeof(uint256)+1];
+ for (unsigned int i = 0; i < sizeof(uint256); i++)
+ data[sizeof(uint256) - 1 - i] = hash.begin()[i];
+ data[sizeof(data) - 1] = 'C'; // Block (C)onnect
+ return SendZmqMessage(MSG_SEQUENCE, data, sizeof(data));
+}
+
+bool CZMQPublishSequenceNotifier::NotifyBlockDisconnect(const CBlockIndex *pindex)
+{
+ uint256 hash = pindex->GetBlockHash();
+ LogPrint(BCLog::ZMQ, "zmq: Publish sequence block disconnect %s\n", hash.GetHex());
+ char data[sizeof(uint256)+1];
+ for (unsigned int i = 0; i < sizeof(uint256); i++)
+ data[sizeof(uint256) - 1 - i] = hash.begin()[i];
+ data[sizeof(data) - 1] = 'D'; // Block (D)isconnect
+ return SendZmqMessage(MSG_SEQUENCE, data, sizeof(data));
+}
+
+bool CZMQPublishSequenceNotifier::NotifyTransactionAcceptance(const CTransaction &transaction, uint64_t mempool_sequence)
+{
+ uint256 hash = transaction.GetHash();
+ LogPrint(BCLog::ZMQ, "zmq: Publish hashtx mempool acceptance %s\n", hash.GetHex());
+ unsigned char data[sizeof(uint256)+sizeof(mempool_sequence)+1];
+ for (unsigned int i = 0; i < sizeof(uint256); i++)
+ data[sizeof(uint256) - 1 - i] = hash.begin()[i];
+ data[sizeof(uint256)] = 'A'; // Mempool (A)cceptance
+ WriteLE64(data+sizeof(uint256)+1, mempool_sequence);
+ return SendZmqMessage(MSG_SEQUENCE, data, sizeof(data));
+}
+
+bool CZMQPublishSequenceNotifier::NotifyTransactionRemoval(const CTransaction &transaction, uint64_t mempool_sequence)
+{
+ uint256 hash = transaction.GetHash();
+ LogPrint(BCLog::ZMQ, "zmq: Publish hashtx mempool removal %s\n", hash.GetHex());
+ unsigned char data[sizeof(uint256)+sizeof(mempool_sequence)+1];
+ for (unsigned int i = 0; i < sizeof(uint256); i++)
+ data[sizeof(uint256) - 1 - i] = hash.begin()[i];
+ data[sizeof(uint256)] = 'R'; // Mempool (R)emoval
+ WriteLE64(data+sizeof(uint256)+1, mempool_sequence);
+ return SendZmqMessage(MSG_SEQUENCE, data, sizeof(data));
+}
diff --git a/src/zmq/zmqpublishnotifier.h b/src/zmq/zmqpublishnotifier.h
index eb9ae881be..f13ed6f537 100644
--- a/src/zmq/zmqpublishnotifier.h
+++ b/src/zmq/zmqpublishnotifier.h
@@ -52,4 +52,13 @@ public:
bool NotifyTransaction(const CTransaction &transaction) override;
};
+class CZMQPublishSequenceNotifier : public CZMQAbstractPublishNotifier
+{
+public:
+ bool NotifyBlockConnect(const CBlockIndex *pindex) override;
+ bool NotifyBlockDisconnect(const CBlockIndex *pindex) override;
+ bool NotifyTransactionAcceptance(const CTransaction &transaction, uint64_t mempool_sequence) override;
+ bool NotifyTransactionRemoval(const CTransaction &transaction, uint64_t mempool_sequence) override;
+};
+
#endif // BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H
diff --git a/test/functional/README.md b/test/functional/README.md
index 0d85a74074..82b30fed51 100644
--- a/test/functional/README.md
+++ b/test/functional/README.md
@@ -87,7 +87,9 @@ P2P messages. These can be found in the following source files:
#### Using the P2P interface
-- [messages.py](test_framework/messages.py) contains all the definitions for objects that pass
+- `P2P`s can be used to test specific P2P protocol behavior.
+[p2p.py](test_framework/p2p.py) contains test framework p2p objects and
+[messages.py](test_framework/messages.py) contains all the definitions for objects passed
over the network (`CBlock`, `CTransaction`, etc, along with the network-level
wrappers for them, `msg_block`, `msg_tx`, etc).
@@ -100,8 +102,22 @@ contains the higher level logic for processing P2P payloads and connecting to
the Bitcoin Core node application logic. For custom behaviour, subclass the
P2PInterface object and override the callback methods.
-- Can be used to write tests where specific P2P protocol behavior is tested.
-Examples tests are [p2p_unrequested_blocks.py](p2p_unrequested_blocks.py),
+`P2PConnection`s can be used as such:
+
+```python
+p2p_conn = node.add_p2p_connection(P2PInterface())
+p2p_conn.send_and_ping(msg)
+```
+
+They can also be referenced by indexing into a `TestNode`'s `p2ps` list, which
+contains the list of test framework `p2p` objects connected to itself
+(it does not include any `TestNode`s):
+
+```python
+node.p2ps[0].sync_with_ping()
+```
+
+More examples can be found in [p2p_unrequested_blocks.py](p2p_unrequested_blocks.py),
[p2p_compactblocks.py](p2p_compactblocks.py).
#### Prototyping tests
@@ -157,7 +173,7 @@ way is the use the `profile_with_perf` context manager, e.g.
with node.profile_with_perf("send-big-msgs"):
# Perform activity on the node you're interested in profiling, e.g.:
for _ in range(10000):
- node.p2p.send_message(some_large_message)
+ node.p2ps[0].send_message(some_large_message)
```
To see useful textual output, run
diff --git a/test/functional/example_test.py b/test/functional/example_test.py
index 083deb6460..3b9bd3048f 100755
--- a/test/functional/example_test.py
+++ b/test/functional/example_test.py
@@ -136,7 +136,7 @@ class ExampleTest(BitcoinTestFramework):
"""Main test logic"""
# Create P2P connections will wait for a verack to make sure the connection is fully up
- self.nodes[0].add_p2p_connection(BaseNode())
+ peer_messaging = self.nodes[0].add_p2p_connection(BaseNode())
# Generating a block on one of the nodes will get us out of IBD
blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)]
@@ -173,7 +173,7 @@ class ExampleTest(BitcoinTestFramework):
block.solve()
block_message = msg_block(block)
# Send message is used to send a P2P message to the node over our P2PInterface
- self.nodes[0].p2p.send_message(block_message)
+ peer_messaging.send_message(block_message)
self.tip = block.sha256
blocks.append(self.tip)
self.block_time += 1
@@ -191,25 +191,25 @@ class ExampleTest(BitcoinTestFramework):
self.log.info("Add P2P connection to node2")
self.nodes[0].disconnect_p2ps()
- self.nodes[2].add_p2p_connection(BaseNode())
+ peer_receiving = self.nodes[2].add_p2p_connection(BaseNode())
self.log.info("Test that node2 propagates all the blocks to us")
getdata_request = msg_getdata()
for block in blocks:
getdata_request.inv.append(CInv(MSG_BLOCK, block))
- self.nodes[2].p2p.send_message(getdata_request)
+ peer_receiving.send_message(getdata_request)
# wait_until() will loop until a predicate condition is met. Use it to test properties of the
# P2PInterface objects.
- self.nodes[2].p2p.wait_until(lambda: sorted(blocks) == sorted(list(self.nodes[2].p2p.block_receive_map.keys())), timeout=5)
+ peer_receiving.wait_until(lambda: sorted(blocks) == sorted(list(peer_receiving.block_receive_map.keys())), timeout=5)
self.log.info("Check that each block was received only once")
# The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving
# messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking
# and synchronization issues. Note p2p.wait_until() acquires this global lock internally when testing the predicate.
with p2p_lock:
- for block in self.nodes[2].p2p.block_receive_map.values():
+ for block in peer_receiving.block_receive_map.values():
assert_equal(block, 1)
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index efafcfaec3..19753d73ef 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -1386,14 +1386,14 @@ class FullBlockTest(BitcoinTestFramework):
"""Add a P2P connection to the node.
Helper to connect and wait for version handshake."""
- self.nodes[0].add_p2p_connection(P2PDataStore())
+ self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
# We need to wait for the initial getheaders from the peer before we
# start populating our blockstore. If we don't, then we may run ahead
# to the next subtest before we receive the getheaders. We'd then send
# an INV for the next block and receive two getheaders - one for the
# IBD and one for the INV. We'd respond to both and could get
# unexpectedly disconnected if the DoS score for that error is 50.
- self.nodes[0].p2p.wait_for_getheaders(timeout=timeout)
+ self.helper_peer.wait_for_getheaders(timeout=timeout)
def reconnect_p2p(self, timeout=60):
"""Tear down and bootstrap the P2P connection to the node.
@@ -1407,7 +1407,7 @@ class FullBlockTest(BitcoinTestFramework):
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
Call with success = False if the tip shouldn't advance to the most recent block."""
- self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect)
+ self.helper_peer.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, force_send=force_send, timeout=timeout, expect_disconnect=reconnect)
if reconnect:
self.reconnect_p2p(timeout=timeout)
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index 2919b0ea0b..aad255c4a9 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -75,7 +75,7 @@ class BIP65Test(BitcoinTestFramework):
)
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PInterface())
+ peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.test_cltv_info(is_active=False)
@@ -99,7 +99,7 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
@@ -111,9 +111,9 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000003)'.format(block.hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ peer.sync_with_ping()
self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block")
block.nVersion = 4
@@ -136,9 +136,9 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with non-mandatory-script-verify-flag (Negative locktime)'.format(block.vtx[-1].hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ peer.sync_with_ping()
self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")
spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1)
@@ -150,7 +150,7 @@ class BIP65Test(BitcoinTestFramework):
block.solve()
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_cltv_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index 38e95f00e9..39e8bca751 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -182,10 +182,10 @@ class BIP68_112_113Test(BitcoinTestFramework):
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
Call with success = False if the tip shouldn't advance to the most recent block."""
- self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason)
+ self.helper_peer.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason)
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PDataStore())
+ self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
self.log.info("Generate blocks in the past for coinbase outputs.")
long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index f263c93c8a..3f7efdbded 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -59,7 +59,7 @@ class BIP66Test(BitcoinTestFramework):
)
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PInterface())
+ peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.test_dersig_info(is_active=False)
@@ -84,7 +84,7 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
@@ -97,9 +97,9 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000002)'.format(block.hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ peer.sync_with_ping()
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
block.nVersion = 3
@@ -123,9 +123,9 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with non-mandatory-script-verify-flag (Non-canonical DER signature)'.format(block.vtx[-1].hash)]):
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
- self.nodes[0].p2p.sync_with_ping()
+ peer.sync_with_ping()
self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0)
@@ -134,7 +134,7 @@ class BIP66Test(BitcoinTestFramework):
block.solve()
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
- self.nodes[0].p2p.send_and_ping(msg_block(block))
+ peer.send_and_ping(msg_block(block))
self.test_dersig_info(is_active=True) # Active as of current tip
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index e5c62d1ea7..d0a94658ff 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -145,16 +145,16 @@ class MaxUploadTest(BitcoinTestFramework):
self.restart_node(0, ["-whitelist=download@127.0.0.1", "-maxuploadtarget=1"])
# Reconnect to self.nodes[0]
- self.nodes[0].add_p2p_connection(TestP2PConn())
+ peer = self.nodes[0].add_p2p_connection(TestP2PConn())
#retrieve 20 blocks which should be enough to break the 1MB limit
getdata_request.inv = [CInv(MSG_BLOCK, big_new_block)]
for i in range(20):
- self.nodes[0].p2p.send_and_ping(getdata_request)
- assert_equal(self.nodes[0].p2p.block_receive_map[big_new_block], i+1)
+ peer.send_and_ping(getdata_request)
+ assert_equal(peer.block_receive_map[big_new_block], i+1)
getdata_request.inv = [CInv(MSG_BLOCK, big_old_block)]
- self.nodes[0].p2p.send_and_ping(getdata_request)
+ peer.send_and_ping(getdata_request)
self.log.info("Peer still connected after trying to download old block (download permission)")
peer_info = self.nodes[0].getpeerinfo()
diff --git a/test/functional/feature_signet.py b/test/functional/feature_signet.py
index f85431148d..a0e7f3ee6e 100755
--- a/test/functional/feature_signet.py
+++ b/test/functional/feature_signet.py
@@ -22,13 +22,14 @@ signet_blocks = [
'00000020a868e8514be5e46dabd6a122132f423f36a43b716a40c394e2a8d063e1010000f4c6c717e99d800c699c25a2006a75a0c5c09f432a936f385e6fce139cdbd1a5e9964d5fae77031e7d026e0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025a51feffffff0200f2052a01000000160014aaa671c82b138e3b8f510cd801e5f2bd0aa305940000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022042309f4c3c7a1a2ac8c24f890f962df1c0086cec10be0868087cfc427520cb2702201dafee8911c269b7e786e242045bb57cef3f5b0f177010c6159abae42f646cc501000120000000000000000000000000000000000000000000000000000000000000000000000000',
]
+
class SignetBasicTest(BitcoinTestFramework):
def set_test_params(self):
self.chain = "signet"
self.num_nodes = 6
self.setup_clean_chain = True
- shared_args1 = ["-signetchallenge=51"] # OP_TRUE
- shared_args2 = [] # default challenge
+ shared_args1 = ["-signetchallenge=51"] # OP_TRUE
+ shared_args2 = [] # default challenge
# we use the exact same challenge except we do it as a 2-of-2, which means it should fail
shared_args3 = ["-signetchallenge=522103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"]
@@ -38,9 +39,6 @@ class SignetBasicTest(BitcoinTestFramework):
shared_args3, shared_args3,
]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
self.log.info("basic tests using OP_TRUE challenge")
@@ -53,19 +51,20 @@ class SignetBasicTest(BitcoinTestFramework):
assert_equal(mining_info['networkhashps'], Decimal('0'))
assert_equal(mining_info['pooledtx'], 0)
- self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress())
+ self.nodes[0].generate(1)
self.log.info("pregenerated signet blocks check")
height = 0
for block in signet_blocks:
assert_equal(self.nodes[2].submitblock(block), None)
- height = height + 1
+ height += 1
assert_equal(self.nodes[2].getblockcount(), height)
self.log.info("pregenerated signet blocks check (incompatible solution)")
assert_equal(self.nodes[4].submitblock(signet_blocks[0]), 'bad-signet-blksig')
+
if __name__ == '__main__':
SignetBasicTest().main()
diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py
index e1016e1581..2e4f4796b0 100755
--- a/test/functional/feature_versionbits_warning.py
+++ b/test/functional/feature_versionbits_warning.py
@@ -61,7 +61,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0]
- node.add_p2p_connection(P2PInterface())
+ peer = node.add_p2p_connection(P2PInterface())
node_deterministic_address = node.get_deterministic_priv_key().address
# Mine one period worth of blocks
@@ -69,7 +69,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
self.log.info("Check that there is no warning if previous VB_BLOCKS have <VB_THRESHOLD blocks with unknown versionbits version.")
# Build one period of blocks with < VB_THRESHOLD blocks signaling some unknown bit
- self.send_blocks_with_version(node.p2p, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION)
+ self.send_blocks_with_version(peer, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION)
node.generatetoaddress(VB_PERIOD - VB_THRESHOLD + 1, node_deterministic_address)
# Check that we're not getting any versionbit-related errors in get*info()
@@ -77,7 +77,7 @@ class VersionBitsWarningTest(BitcoinTestFramework):
assert not VB_PATTERN.match(node.getnetworkinfo()["warnings"])
# Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit
- self.send_blocks_with_version(node.p2p, VB_THRESHOLD, VB_UNKNOWN_VERSION)
+ self.send_blocks_with_version(peer, VB_THRESHOLD, VB_UNKNOWN_VERSION)
node.generatetoaddress(VB_PERIOD - VB_THRESHOLD, node_deterministic_address)
self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.")
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index ef4780cacb..17032a3b83 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -5,13 +5,24 @@
"""Test the ZMQ notification interface."""
import struct
-from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE, ADDRESS_BCRT1_P2WSH_OP_TRUE
+from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.messages import CTransaction, hash256
-from test_framework.util import assert_equal, connect_nodes
+from test_framework.messages import CTransaction, hash256, FromHex
+from test_framework.util import (
+ assert_equal,
+ connect_nodes,
+ assert_raises_rpc_error,
+)
from io import BytesIO
from time import sleep
+# Test may be skipped and not have zmq installed
+try:
+ import zmq
+except ImportError:
+ pass
+
def hash256_reversed(byte_str):
return hash256(byte_str)[::-1]
@@ -21,7 +32,6 @@ class ZMQSubscriber:
self.socket = socket
self.topic = topic
- import zmq
self.socket.setsockopt(zmq.SUBSCRIBE, self.topic)
def receive(self):
@@ -33,6 +43,22 @@ class ZMQSubscriber:
self.sequence += 1
return body
+ def receive_sequence(self):
+ topic, body, seq = self.socket.recv_multipart()
+ # Topic should match the subscriber topic.
+ assert_equal(topic, self.topic)
+ # Sequence should be incremental.
+ assert_equal(struct.unpack('<I', seq)[-1], self.sequence)
+ self.sequence += 1
+ hash = body[:32].hex()
+ label = chr(body[32])
+ mempool_sequence = None if len(body) != 32+1+8 else struct.unpack("<Q", body[32+1:])[0]
+ if mempool_sequence is not None:
+ assert label == "A" or label == "R"
+ else:
+ assert label == "D" or label == "C"
+ return (hash, label, mempool_sequence)
+
class ZMQTest (BitcoinTestFramework):
def set_test_params(self):
@@ -43,10 +69,11 @@ class ZMQTest (BitcoinTestFramework):
self.skip_if_no_bitcoind_zmq()
def run_test(self):
- import zmq
self.ctx = zmq.Context()
try:
self.test_basic()
+ self.test_sequence()
+ self.test_mempool_sync()
self.test_reorg()
finally:
# Destroy the ZMQ context.
@@ -54,7 +81,6 @@ class ZMQTest (BitcoinTestFramework):
self.ctx.destroy(linger=None)
def test_basic(self):
- import zmq
# Invalid zmq arguments don't take down the node, see #17185.
self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"])
@@ -146,7 +172,6 @@ class ZMQTest (BitcoinTestFramework):
self.log.info("Skipping reorg test because wallet is disabled")
return
- import zmq
address = 'tcp://127.0.0.1:28333'
services = [b"hashblock", b"hashtx"]
@@ -177,8 +202,8 @@ class ZMQTest (BitcoinTestFramework):
assert_equal(hashtx.receive().hex(), payment_txid)
assert_equal(hashtx.receive().hex(), disconnect_cb)
- # Generate 2 blocks in nodes[1]
- connect_blocks = self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE)
+ # Generate 2 blocks in nodes[1] to a different address to ensure split
+ connect_blocks = self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_P2WSH_OP_TRUE)
# nodes[0] will reorg chain after connecting back nodes[1]
connect_nodes(self.nodes[0], 1)
@@ -204,5 +229,282 @@ class ZMQTest (BitcoinTestFramework):
# And the current tip
assert_equal(hashtx.receive().hex(), self.nodes[1].getblock(connect_blocks[0])["tx"][0])
+ def test_sequence(self):
+ """
+ Sequence zmq notifications give every blockhash and txhash in order
+ of processing, regardless of IBD, re-orgs, etc.
+ Format of messages:
+ <32-byte hash>C : Blockhash connected
+ <32-byte hash>D : Blockhash disconnected
+ <32-byte hash>R<8-byte LE uint> : Transactionhash removed from mempool for non-block inclusion reason
+ <32-byte hash>A<8-byte LE uint> : Transactionhash added mempool
+ """
+ self.log.info("Testing 'sequence' publisher")
+ address = 'tcp://127.0.0.1:28333'
+ socket = self.ctx.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ seq = ZMQSubscriber(socket, b'sequence')
+
+ self.restart_node(0, ['-zmqpub%s=%s' % (seq.topic.decode(), address)])
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
+
+ # Mempool sequence number starts at 1
+ seq_num = 1
+
+ # Generate 1 block in nodes[0] and receive all notifications
+ dc_block = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0]
+
+ # Note: We are not notified of any block transactions, coinbase or mined
+ assert_equal((self.nodes[0].getbestblockhash(), "C", None), seq.receive_sequence())
+
+ # Generate 2 blocks in nodes[1] to a different address to ensure a chain split
+ self.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_P2WSH_OP_TRUE)
+
+ # nodes[0] will reorg chain after connecting back nodes[1]
+ connect_nodes(self.nodes[0], 1)
+
+ # Then we receive all block (dis)connect notifications for the 2 block reorg
+ assert_equal((dc_block, "D", None), seq.receive_sequence())
+ block_count = self.nodes[1].getblockcount()
+ assert_equal((self.nodes[1].getblockhash(block_count-1), "C", None), seq.receive_sequence())
+ assert_equal((self.nodes[1].getblockhash(block_count), "C", None), seq.receive_sequence())
+
+ # Rest of test requires wallet functionality
+ if self.is_wallet_compiled():
+ self.log.info("Wait for tx from second node")
+ payment_txid = self.nodes[1].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=5.0, replaceable=True)
+ self.sync_all()
+ self.log.info("Testing sequence notifications with mempool sequence values")
+
+ # Should receive the broadcasted txid.
+ assert_equal((payment_txid, "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ self.log.info("Testing RBF notification")
+ # Replace it to test eviction/addition notification
+ rbf_info = self.nodes[1].bumpfee(payment_txid)
+ self.sync_all()
+ assert_equal((payment_txid, "R", seq_num), seq.receive_sequence())
+ seq_num += 1
+ assert_equal((rbf_info["txid"], "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Doesn't get published when mined, make a block and tx to "flush" the possibility
+ # though the mempool sequence number does go up by the number of transactions
+ # removed from the mempool by the block mining it.
+ mempool_size = len(self.nodes[0].getrawmempool())
+ c_block = self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)[0]
+ self.sync_all()
+ # Make sure the number of mined transactions matches the number of txs out of mempool
+ mempool_size_delta = mempool_size - len(self.nodes[0].getrawmempool())
+ assert_equal(len(self.nodes[0].getblock(c_block)["tx"])-1, mempool_size_delta)
+ seq_num += mempool_size_delta
+ payment_txid_2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
+ self.sync_all()
+ assert_equal((c_block, "C", None), seq.receive_sequence())
+ assert_equal((payment_txid_2, "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Spot check getrawmempool results that they only show up when asked for
+ assert type(self.nodes[0].getrawmempool()) is list
+ assert type(self.nodes[0].getrawmempool(mempool_sequence=False)) is list
+ assert "mempool_sequence" not in self.nodes[0].getrawmempool(verbose=True)
+ assert_raises_rpc_error(-8, "Verbose results cannot contain mempool sequence values.", self.nodes[0].getrawmempool, True, True)
+ assert_equal(self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"], seq_num)
+
+ self.log.info("Testing reorg notifications")
+ # Manually invalidate the last block to test mempool re-entry
+ # N.B. This part could be made more lenient in exact ordering
+ # since it greatly depends on inner-workings of blocks/mempool
+ # during "deep" re-orgs. Probably should "re-construct"
+ # blockchain/mempool state from notifications instead.
+ block_count = self.nodes[0].getblockcount()
+ best_hash = self.nodes[0].getbestblockhash()
+ self.nodes[0].invalidateblock(best_hash)
+ sleep(2) # Bit of room to make sure transaction things happened
+
+ # Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective
+ # of the time they were gathered.
+ assert self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] > seq_num
+
+ assert_equal((best_hash, "D", None), seq.receive_sequence())
+ assert_equal((rbf_info["txid"], "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Other things may happen but aren't wallet-deterministic so we don't test for them currently
+ self.nodes[0].reconsiderblock(best_hash)
+ self.nodes[1].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ self.sync_all()
+
+ self.log.info("Evict mempool transaction by block conflict")
+ orig_txid = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True)
+
+ # More to be simply mined
+ more_tx = []
+ for _ in range(5):
+ more_tx.append(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.1))
+
+ raw_tx = self.nodes[0].getrawtransaction(orig_txid)
+ bump_info = self.nodes[0].bumpfee(orig_txid)
+ # Mine the pre-bump tx
+ block = create_block(int(self.nodes[0].getbestblockhash(), 16), create_coinbase(self.nodes[0].getblockcount()+1))
+ tx = FromHex(CTransaction(), raw_tx)
+ block.vtx.append(tx)
+ for txid in more_tx:
+ tx = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
+ block.vtx.append(tx)
+ add_witness_commitment(block)
+ block.solve()
+ assert_equal(self.nodes[0].submitblock(block.serialize().hex()), None)
+ tip = self.nodes[0].getbestblockhash()
+ assert_equal(int(tip, 16), block.sha256)
+ orig_txid_2 = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True)
+
+ # Flush old notifications until evicted tx original entry
+ (hash_str, label, mempool_seq) = seq.receive_sequence()
+ while hash_str != orig_txid:
+ (hash_str, label, mempool_seq) = seq.receive_sequence()
+ mempool_seq += 1
+
+ # Added original tx
+ assert_equal(label, "A")
+ # More transactions to be simply mined
+ for i in range(len(more_tx)):
+ assert_equal((more_tx[i], "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ # Bumped by rbf
+ assert_equal((orig_txid, "R", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ assert_equal((bump_info["txid"], "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ # Conflict announced first, then block
+ assert_equal((bump_info["txid"], "R", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ assert_equal((tip, "C", None), seq.receive_sequence())
+ mempool_seq += len(more_tx)
+ # Last tx
+ assert_equal((orig_txid_2, "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ self.sync_all() # want to make sure we didn't break "consensus" for other tests
+
+ def test_mempool_sync(self):
+ """
+ Use sequence notification plus getrawmempool sequence results to "sync mempool"
+ """
+ if not self.is_wallet_compiled():
+ self.log.info("Skipping mempool sync test")
+ return
+
+ self.log.info("Testing 'mempool sync' usage of sequence notifier")
+ address = 'tcp://127.0.0.1:28333'
+ socket = self.ctx.socket(zmq.SUB)
+ socket.set(zmq.RCVTIMEO, 60000)
+ seq = ZMQSubscriber(socket, b'sequence')
+
+ self.restart_node(0, ['-zmqpub%s=%s' % (seq.topic.decode(), address)])
+ connect_nodes(self.nodes[0], 1)
+ socket.connect(address)
+ # Relax so that the subscriber is ready before publishing zmq messages
+ sleep(0.2)
+
+ # In-memory counter, should always start at 1
+ next_mempool_seq = self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"]
+ assert_equal(next_mempool_seq, 1)
+
+ # Some transactions have been happening but we aren't consuming zmq notifications yet
+ # or we lost a ZMQ message somehow and want to start over
+ txids = []
+ num_txs = 5
+ for _ in range(num_txs):
+ txids.append(self.nodes[1].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True))
+ self.sync_all()
+
+ # 1) Consume backlog until we get a mempool sequence number
+ (hash_str, label, zmq_mem_seq) = seq.receive_sequence()
+ while zmq_mem_seq is None:
+ (hash_str, label, zmq_mem_seq) = seq.receive_sequence()
+
+ assert label == "A" or label == "R"
+ assert hash_str is not None
+
+ # 2) We need to "seed" our view of the mempool
+ mempool_snapshot = self.nodes[0].getrawmempool(mempool_sequence=True)
+ mempool_view = set(mempool_snapshot["txids"])
+ get_raw_seq = mempool_snapshot["mempool_sequence"]
+ assert_equal(get_raw_seq, 6)
+ # Snapshot may be too old compared to zmq message we read off latest
+ while zmq_mem_seq >= get_raw_seq:
+ sleep(2)
+ mempool_snapshot = self.nodes[0].getrawmempool(mempool_sequence=True)
+ mempool_view = set(mempool_snapshot["txids"])
+ get_raw_seq = mempool_snapshot["mempool_sequence"]
+
+ # Things continue to happen in the "interim" while waiting for snapshot results
+ # We have node 0 do all these to avoid p2p races with RBF announcements
+ for _ in range(num_txs):
+ txids.append(self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=0.1, replaceable=True))
+ self.nodes[0].bumpfee(txids[-1])
+ self.sync_all()
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+ final_txid = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=0.1, replaceable=True)
+
+ # 3) Consume ZMQ backlog until we get to "now" for the mempool snapshot
+ while True:
+ if zmq_mem_seq == get_raw_seq - 1:
+ break
+ (hash_str, label, mempool_sequence) = seq.receive_sequence()
+ if mempool_sequence is not None:
+ zmq_mem_seq = mempool_sequence
+ if zmq_mem_seq > get_raw_seq:
+ raise Exception("We somehow jumped mempool sequence numbers! zmq_mem_seq: {} > get_raw_seq: {}".format(zmq_mem_seq, get_raw_seq))
+
+ # 4) Moving forward, we apply the delta to our local view
+ # remaining txs(5) + 1 rbf(A+R) + 1 block connect + 1 final tx
+ expected_sequence = get_raw_seq
+ r_gap = 0
+ for _ in range(num_txs + 2 + 1 + 1):
+ (hash_str, label, mempool_sequence) = seq.receive_sequence()
+ if mempool_sequence is not None:
+ if mempool_sequence != expected_sequence:
+ # Detected "R" gap, means this a conflict eviction, and mempool tx are being evicted before its
+ # position in the incoming block message "C"
+ if label == "R":
+ assert mempool_sequence > expected_sequence
+ r_gap += mempool_sequence - expected_sequence
+ else:
+ raise Exception("WARNING: txhash has unexpected mempool sequence value: {} vs expected {}".format(mempool_sequence, expected_sequence))
+ if label == "A":
+ assert hash_str not in mempool_view
+ mempool_view.add(hash_str)
+ expected_sequence = mempool_sequence + 1
+ elif label == "R":
+ assert hash_str in mempool_view
+ mempool_view.remove(hash_str)
+ expected_sequence = mempool_sequence + 1
+ elif label == "C":
+ # (Attempt to) remove all txids from known block connects
+ block_txids = self.nodes[0].getblock(hash_str)["tx"][1:]
+ for txid in block_txids:
+ if txid in mempool_view:
+ expected_sequence += 1
+ mempool_view.remove(txid)
+ expected_sequence -= r_gap
+ r_gap = 0
+ elif label == "D":
+ # Not useful for mempool tracking per se
+ continue
+ else:
+ raise Exception("Unexpected ZMQ sequence label!")
+
+ assert_equal(self.nodes[0].getrawmempool(), [final_txid])
+ assert_equal(self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"], expected_sequence)
+
+ # 5) If you miss a zmq/mempool sequence number, go back to step (2)
+
+ self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)
+
if __name__ == '__main__':
ZMQTest().main()
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index e74ef8cf16..d7cb7db9f8 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -58,7 +58,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
def run_test(self):
# Mine some blocks and have them mature.
- self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
+ peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
self.nodes[0].generate(101)
utxo = self.nodes[0].listunspent(10)
txid = utxo[0]['txid']
@@ -80,7 +80,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
# Wait until mempool transactions have passed initial broadcast (sent inv and received getdata)
# Otherwise, getrawmempool may be inconsistent with getmempoolentry if unbroadcast changes in between
- self.nodes[0].p2p.wait_for_broadcast(witness_chain)
+ peer_inv_store.wait_for_broadcast(witness_chain)
# Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor
# count and fees should look correct
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index b13740750f..1b2c7644bd 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -234,9 +234,9 @@ class MiningTest(BitcoinTestFramework):
assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=CBlockHeader(bad_block_time).serialize().hex()))
# Should ask for the block from a p2p node, if they announce the header as well:
- node.add_p2p_connection(P2PDataStore())
- node.p2p.wait_for_getheaders(timeout=5) # Drop the first getheaders
- node.p2p.send_blocks_and_test(blocks=[block], node=node)
+ peer = node.add_p2p_connection(P2PDataStore())
+ peer.wait_for_getheaders(timeout=5) # Drop the first getheaders
+ peer.send_blocks_and_test(blocks=[block], node=node)
# Must be active now:
assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips()
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index 65259f1869..646baa1550 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -17,7 +17,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
self.extra_args = [["-blocksonly"]]
def run_test(self):
- self.nodes[0].add_p2p_connection(P2PInterface())
+ block_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.log.info('Check that txs from p2p are rejected and result in disconnect')
prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
@@ -41,45 +41,49 @@ class P2PBlocksOnly(BitcoinTestFramework):
)['hex']
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
- self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
- self.nodes[0].p2p.wait_for_disconnect()
+ block_relay_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
+ block_relay_peer.wait_for_disconnect()
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
# Remove the disconnected peer and add a new one.
del self.nodes[0].p2ps[0]
- self.nodes[0].add_p2p_connection(P2PInterface())
+ tx_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface())
self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
with self.nodes[0].assert_debug_log(['received getdata for: wtx {} peer=1'.format(txid)]):
self.nodes[0].sendrawtransaction(sigtx)
- self.nodes[0].p2p.wait_for_tx(txid)
+ tx_relay_peer.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
- self.log.info('Check that txs from forcerelay peers are not rejected and relayed to others')
- self.log.info("Restarting node 0 with forcerelay permission and blocksonly")
- self.restart_node(0, ["-persistmempool=0", "-whitelist=127.0.0.1", "-whitelistforcerelay", "-blocksonly"])
+ self.log.info('Check that txs from peers with relay-permission are not rejected and relayed to others')
+ self.log.info("Restarting node 0 with relay permission and blocksonly")
+ self.restart_node(0, ["-persistmempool=0", "-whitelist=relay@127.0.0.1", "-blocksonly"])
assert_equal(self.nodes[0].getrawmempool(), [])
first_peer = self.nodes[0].add_p2p_connection(P2PInterface())
second_peer = self.nodes[0].add_p2p_connection(P2PInterface())
peer_1_info = self.nodes[0].getpeerinfo()[0]
- assert_equal(peer_1_info['whitelisted'], True)
- assert_equal(peer_1_info['permissions'], ['noban', 'forcerelay', 'relay', 'mempool', 'download'])
+ assert_equal(peer_1_info['permissions'], ['relay'])
peer_2_info = self.nodes[0].getpeerinfo()[1]
- assert_equal(peer_2_info['whitelisted'], True)
- assert_equal(peer_2_info['permissions'], ['noban', 'forcerelay', 'relay', 'mempool', 'download'])
+ assert_equal(peer_2_info['permissions'], ['relay'])
assert_equal(self.nodes[0].testmempoolaccept([sigtx])[0]['allowed'], True)
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
- self.log.info('Check that the tx from forcerelay first_peer is relayed to others (ie.second_peer)')
+ self.log.info('Check that the tx from first_peer with relay-permission is relayed to others (ie.second_peer)')
with self.nodes[0].assert_debug_log(["received getdata"]):
+ # Note that normally, first_peer would never send us transactions since we're a blocksonly node.
+ # By activating blocksonly, we explicitly tell our peers that they should not send us transactions,
+ # and Bitcoin Core respects that choice and will not send transactions.
+ # But if, for some reason, first_peer decides to relay transactions to us anyway, we should relay them to
+ # second_peer since we gave relay permission to first_peer.
+ # See https://github.com/bitcoin/bitcoin/issues/19943 for details.
first_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
- self.log.info('Check that the forcerelay peer is still connected after sending the transaction')
+ self.log.info('Check that the peer with relay-permission is still connected after sending the transaction')
assert_equal(first_peer.is_connected, True)
second_peer.wait_for_tx(txid)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
- self.log.info("Forcerelay peer's transaction is accepted and relayed")
+ self.log.info("Relay-permission peer's transaction is accepted and relayed")
if __name__ == '__main__':
diff --git a/test/functional/p2p_dos_header_tree.py b/test/functional/p2p_dos_header_tree.py
index 7dd8c3146b..2349afa1ee 100755
--- a/test/functional/p2p_dos_header_tree.py
+++ b/test/functional/p2p_dos_header_tree.py
@@ -46,8 +46,8 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
self.headers_fork = [FromHex(CBlockHeader(), h) for h in self.headers_fork]
self.log.info("Feed all non-fork headers, including and up to the first checkpoint")
- self.nodes[0].add_p2p_connection(P2PInterface())
- self.nodes[0].p2p.send_and_ping(msg_headers(self.headers))
+ peer_checkpoint = self.nodes[0].add_p2p_connection(P2PInterface())
+ peer_checkpoint.send_and_ping(msg_headers(self.headers))
assert {
'height': 546,
'hash': '000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70',
@@ -57,14 +57,14 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
self.log.info("Feed all fork headers (fails due to checkpoint)")
with self.nodes[0].assert_debug_log(['bad-fork-prior-to-checkpoint']):
- self.nodes[0].p2p.send_message(msg_headers(self.headers_fork))
- self.nodes[0].p2p.wait_for_disconnect()
+ peer_checkpoint.send_message(msg_headers(self.headers_fork))
+ peer_checkpoint.wait_for_disconnect()
self.log.info("Feed all fork headers (succeeds without checkpoint)")
# On node 0 it succeeds because checkpoints are disabled
self.restart_node(0, extra_args=['-nocheckpoints'])
- self.nodes[0].add_p2p_connection(P2PInterface())
- self.nodes[0].p2p.send_and_ping(msg_headers(self.headers_fork))
+ peer_no_checkpoint = self.nodes[0].add_p2p_connection(P2PInterface())
+ peer_no_checkpoint.send_and_ping(msg_headers(self.headers_fork))
assert {
"height": 2,
"hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39",
@@ -73,8 +73,8 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
} in self.nodes[0].getchaintips()
# On node 1 it succeeds because no checkpoint has been reached yet by a chain tip
- self.nodes[1].add_p2p_connection(P2PInterface())
- self.nodes[1].p2p.send_and_ping(msg_headers(self.headers_fork))
+ peer_before_checkpoint = self.nodes[1].add_p2p_connection(P2PInterface())
+ peer_before_checkpoint.send_and_ping(msg_headers(self.headers_fork))
assert {
"height": 2,
"hash": "00000000b0494bd6c3d5ff79c497cfce40831871cbf39b1bc28bd1dac817dc39",
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index 613d96eaad..642a217047 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -131,7 +131,7 @@ class FilterTest(BitcoinTestFramework):
self.log.debug("Send a mempool msg after connecting and check that the tx is received")
self.nodes[0].add_p2p_connection(filter_peer)
filter_peer.send_and_ping(filter_peer.watch_filter_init)
- self.nodes[0].p2p.send_message(msg_mempool())
+ filter_peer.send_message(msg_mempool())
filter_peer.wait_for_tx(txid)
def test_frelay_false(self, filter_peer):
diff --git a/test/functional/p2p_getdata.py b/test/functional/p2p_getdata.py
index 51921a8ab5..89d68d5ba0 100755
--- a/test/functional/p2p_getdata.py
+++ b/test/functional/p2p_getdata.py
@@ -42,7 +42,7 @@ class GetdataTest(BitcoinTestFramework):
good_getdata = msg_getdata()
good_getdata.inv.append(CInv(t=2, h=best_block))
p2p_block_store.send_and_ping(good_getdata)
- p2p_block_store.wait_until(lambda: self.nodes[0].p2ps[0].blocks[best_block] == 1)
+ p2p_block_store.wait_until(lambda: p2p_block_store.blocks[best_block] == 1)
if __name__ == '__main__':
diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py
index b2c3c5d45f..483f25f48c 100755
--- a/test/functional/p2p_invalid_block.py
+++ b/test/functional/p2p_invalid_block.py
@@ -27,7 +27,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
def run_test(self):
# Add p2p connection to node0
node = self.nodes[0] # convenience reference to the node
- node.add_p2p_connection(P2PDataStore())
+ peer = node.add_p2p_connection(P2PDataStore())
best_block = node.getblock(node.getbestblockhash())
tip = int(node.getbestblockhash(), 16)
@@ -42,7 +42,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
# Save the coinbase for later
block1 = block
tip = block.sha256
- node.p2p.send_blocks_and_test([block1], node, success=True)
+ peer.send_blocks_and_test([block1], node, success=True)
self.log.info("Mature the block.")
node.generatetoaddress(100, node.get_deterministic_priv_key().address)
@@ -80,7 +80,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
assert_equal(orig_hash, block2.rehash())
assert block2_orig.vtx != block2.vtx
- node.p2p.send_blocks_and_test([block2], node, success=False, reject_reason='bad-txns-duplicate')
+ peer.send_blocks_and_test([block2], node, success=False, reject_reason='bad-txns-duplicate')
# Check transactions for duplicate inputs (CVE-2018-17144)
self.log.info("Test duplicate input block.")
@@ -91,7 +91,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block2_dup.hashMerkleRoot = block2_dup.calc_merkle_root()
block2_dup.rehash()
block2_dup.solve()
- node.p2p.send_blocks_and_test([block2_dup], node, success=False, reject_reason='bad-txns-inputs-duplicate')
+ peer.send_blocks_and_test([block2_dup], node, success=False, reject_reason='bad-txns-inputs-duplicate')
self.log.info("Test very broken block.")
@@ -104,14 +104,14 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block3.rehash()
block3.solve()
- node.p2p.send_blocks_and_test([block3], node, success=False, reject_reason='bad-cb-amount')
+ peer.send_blocks_and_test([block3], node, success=False, reject_reason='bad-cb-amount')
# Complete testing of CVE-2012-2459 by sending the original block.
# It should be accepted even though it has the same hash as the mutated one.
self.log.info("Test accepting original block after rejecting its mutated version.")
- node.p2p.send_blocks_and_test([block2_orig], node, success=True, timeout=5)
+ peer.send_blocks_and_test([block2_orig], node, success=True, timeout=5)
# Update tip info
height += 1
@@ -131,7 +131,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block4.rehash()
block4.solve()
self.log.info("Test inflation by duplicating input")
- node.p2p.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
+ peer.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
if __name__ == '__main__':
InvalidBlockRequestTest().main()
diff --git a/test/functional/p2p_invalid_locator.py b/test/functional/p2p_invalid_locator.py
index 24328c2919..e4fc9fd178 100755
--- a/test/functional/p2p_invalid_locator.py
+++ b/test/functional/p2p_invalid_locator.py
@@ -23,20 +23,20 @@ class InvalidLocatorTest(BitcoinTestFramework):
block_count = node.getblockcount()
for msg in [msg_getheaders(), msg_getblocks()]:
self.log.info('Wait for disconnect when sending {} hashes in locator'.format(MAX_LOCATOR_SZ + 1))
- node.add_p2p_connection(P2PInterface())
+ exceed_max_peer = node.add_p2p_connection(P2PInterface())
msg.locator.vHave = [int(node.getblockhash(i - 1), 16) for i in range(block_count, block_count - (MAX_LOCATOR_SZ + 1), -1)]
- node.p2p.send_message(msg)
- node.p2p.wait_for_disconnect()
+ exceed_max_peer.send_message(msg)
+ exceed_max_peer.wait_for_disconnect()
node.disconnect_p2ps()
self.log.info('Wait for response when sending {} hashes in locator'.format(MAX_LOCATOR_SZ))
- node.add_p2p_connection(P2PInterface())
+ within_max_peer = node.add_p2p_connection(P2PInterface())
msg.locator.vHave = [int(node.getblockhash(i - 1), 16) for i in range(block_count, block_count - (MAX_LOCATOR_SZ), -1)]
- node.p2p.send_message(msg)
+ within_max_peer.send_message(msg)
if type(msg) == msg_getheaders:
- node.p2p.wait_for_header(node.getbestblockhash())
+ within_max_peer.wait_for_header(node.getbestblockhash())
else:
- node.p2p.wait_for_block(int(node.getbestblockhash(), 16))
+ within_max_peer.wait_for_block(int(node.getbestblockhash(), 16))
if __name__ == '__main__':
diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py
index a0ef6c9d6e..489d903c21 100755
--- a/test/functional/p2p_invalid_tx.py
+++ b/test/functional/p2p_invalid_tx.py
@@ -61,7 +61,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
# Save the coinbase for later
block1 = block
tip = block.sha256
- node.p2p.send_blocks_and_test([block], node, success=True)
+ node.p2ps[0].send_blocks_and_test([block], node, success=True)
self.log.info("Mature the block.")
self.nodes[0].generatetoaddress(100, self.nodes[0].get_deterministic_priv_key().address)
@@ -72,7 +72,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__)
template = BadTxTemplate(spend_block=block1)
tx = template.get_tx()
- node.p2p.send_txs_and_test(
+ node.p2ps[0].send_txs_and_test(
[tx], node, success=False,
expect_disconnect=template.expect_disconnect,
reject_reason=template.reject_reason,
@@ -121,7 +121,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
self.log.info('Send the orphans ... ')
# Send valid orphan txs from p2ps[0]
- node.p2p.send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False)
+ node.p2ps[0].send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False)
# Send invalid tx from p2ps[1]
node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False)
@@ -130,7 +130,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
self.log.info('Send the withhold tx ... ')
with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]):
- node.p2p.send_txs_and_test([tx_withhold], node, success=True)
+ node.p2ps[0].send_txs_and_test([tx_withhold], node, success=True)
# Transactions that should end up in the mempool
expected_mempool = {
@@ -155,14 +155,14 @@ class InvalidTxRequestTest(BitcoinTestFramework):
orphan_tx_pool[i].vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
with node.assert_debug_log(['mapOrphan overflow, removed 1 tx']):
- node.p2p.send_txs_and_test(orphan_tx_pool, node, success=False)
+ node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False)
rejected_parent = CTransaction()
rejected_parent.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.sha256, 0)))
rejected_parent.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
rejected_parent.rehash()
with node.assert_debug_log(['not keeping orphan with rejected parents {}'.format(rejected_parent.hash)]):
- node.p2p.send_txs_and_test([rejected_parent], node, success=False)
+ node.p2ps[0].send_txs_and_test([rejected_parent], node, success=False)
if __name__ == '__main__':
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index dfbf5d52f4..e190df403a 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -2097,14 +2097,14 @@ class SegWitTest(BitcoinTestFramework):
tx = FromHex(CTransaction(), raw)
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
with self.nodes[0].assert_debug_log(['Superfluous witness record']):
- self.nodes[0].p2p.send_and_ping(msg_bogus_tx(tx))
+ self.test_node.send_and_ping(msg_bogus_tx(tx))
raw = self.nodes[0].signrawtransactionwithwallet(raw)
assert raw['complete']
raw = raw['hex']
tx = FromHex(CTransaction(), raw)
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
with self.nodes[0].assert_debug_log(['Unknown transaction optional data']):
- self.nodes[0].p2p.send_and_ping(msg_bogus_tx(tx))
+ self.test_node.send_and_ping(msg_bogus_tx(tx))
@subtest # type: ignore
def test_wtxid_relay(self):
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index c005584485..35cea85c07 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -317,7 +317,7 @@ class BlockchainTest(BitcoinTestFramework):
def _test_waitforblockheight(self):
self.log.info("Test waitforblockheight")
node = self.nodes[0]
- node.add_p2p_connection(P2PInterface())
+ peer = node.add_p2p_connection(P2PInterface())
current_height = node.getblock(node.getbestblockhash())['height']
@@ -334,7 +334,7 @@ class BlockchainTest(BitcoinTestFramework):
def solve_and_send_block(prevhash, height, time):
b = create_block(prevhash, create_coinbase(height), time)
b.solve()
- node.p2p.send_and_ping(msg_block(b))
+ peer.send_and_ping(msg_block(b))
return b
b21f = solve_and_send_block(int(b20hash, 16), 21, b20['time'] + 1)
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index d034986821..046efe730e 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -542,15 +542,6 @@ class TestNode():
return p2p_conn
- @property
- def p2p(self):
- """Return the first p2p connection
-
- Convenience property - most tests only use a single p2p connection to each
- node, so this saves having to write node.p2ps[0] many times."""
- assert self.p2ps, self._node_msg("No p2p connection")
- return self.p2ps[0]
-
def num_test_p2p_connections(self):
"""Return number of test framework p2p connections to the node."""
return len([peer for peer in self.getpeerinfo() if peer['subver'] == MY_SUBVERSION])
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 0327c9e070..d3c03c4764 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -21,7 +21,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0] # alias
- node.add_p2p_connection(P2PTxInvStore())
+ peer_first = node.add_p2p_connection(P2PTxInvStore())
self.log.info("Create a new transaction and wait until it's broadcast")
txid = node.sendtoaddress(node.getnewaddress(), 1)
@@ -33,10 +33,10 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
time.sleep(1.1)
# Can take a few seconds due to transaction trickling
- node.p2p.wait_for_broadcast([txid])
+ peer_first.wait_for_broadcast([txid])
# Add a second peer since txs aren't rebroadcast to the same peer (see filterInventoryKnown)
- node.add_p2p_connection(P2PTxInvStore())
+ peer_second = node.add_p2p_connection(P2PTxInvStore())
self.log.info("Create a block")
# Create and submit a block without the transaction.
@@ -58,13 +58,13 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
two_min = 2 * 60
node.setmocktime(now + twelve_hrs - two_min)
time.sleep(2) # ensure enough time has passed for rebroadcast attempt to occur
- assert_equal(int(txid, 16) in node.p2ps[1].get_invs(), False)
+ assert_equal(int(txid, 16) in peer_second.get_invs(), False)
self.log.info("Bump time & check that transaction is rebroadcast")
# Transaction should be rebroadcast approximately 24 hours in the future,
# but can range from 12-36. So bump 36 hours to be sure.
node.setmocktime(now + 36 * 60 * 60)
- node.p2p.wait_for_broadcast([txid])
+ peer_second.wait_for_broadcast([txid])
if __name__ == '__main__':