aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/README.md6
-rw-r--r--contrib/completions/bash/bitcoin-cli.bash-completion (renamed from contrib/bitcoin-cli.bash-completion)0
-rw-r--r--contrib/completions/bash/bitcoin-tx.bash-completion (renamed from contrib/bitcoin-tx.bash-completion)0
-rw-r--r--contrib/completions/bash/bitcoind.bash-completion (renamed from contrib/bitcoind.bash-completion)0
-rw-r--r--contrib/completions/fish/bitcoin-cli.fish99
-rw-r--r--contrib/completions/fish/bitcoin-qt.fish35
-rw-r--r--contrib/completions/fish/bitcoin-tx.fish65
-rw-r--r--contrib/completions/fish/bitcoin-util.fish38
-rw-r--r--contrib/completions/fish/bitcoin-wallet.fish35
-rw-r--r--contrib/completions/fish/bitcoind.fish35
-rw-r--r--doc/release-notes-25934.md8
-rw-r--r--src/net_processing.cpp39
-rw-r--r--src/rest.cpp6
-rw-r--r--src/rpc/blockchain.cpp31
-rw-r--r--src/rpc/txoutproof.cpp4
-rw-r--r--src/validation.cpp18
-rw-r--r--src/validation.h3
-rw-r--r--src/wallet/rpc/transactions.cpp24
-rw-r--r--src/zmq/zmqpublishnotifier.cpp16
-rwxr-xr-xtest/functional/feature_fee_estimation.py6
-rwxr-xr-xtest/functional/wallet_listsinceblock.py15
21 files changed, 413 insertions, 70 deletions
diff --git a/contrib/README.md b/contrib/README.md
index ae1372e95d..0252d668d9 100644
--- a/contrib/README.md
+++ b/contrib/README.md
@@ -40,3 +40,9 @@ Utilities to generate test vectors for the data-driven Bitcoin tests.
### [Verify Binaries](/contrib/verifybinaries) ###
This script attempts to download and verify the signature file SHA256SUMS.asc from bitcoin.org.
+
+Command Line Tools
+---------------------
+
+### [Completions](/contrib/completions) ###
+Shell completions for bash and fish.
diff --git a/contrib/bitcoin-cli.bash-completion b/contrib/completions/bash/bitcoin-cli.bash-completion
index ddea58a05c..ddea58a05c 100644
--- a/contrib/bitcoin-cli.bash-completion
+++ b/contrib/completions/bash/bitcoin-cli.bash-completion
diff --git a/contrib/bitcoin-tx.bash-completion b/contrib/completions/bash/bitcoin-tx.bash-completion
index a83d2979ed..a83d2979ed 100644
--- a/contrib/bitcoin-tx.bash-completion
+++ b/contrib/completions/bash/bitcoin-tx.bash-completion
diff --git a/contrib/bitcoind.bash-completion b/contrib/completions/bash/bitcoind.bash-completion
index ec1d9512d4..ec1d9512d4 100644
--- a/contrib/bitcoind.bash-completion
+++ b/contrib/completions/bash/bitcoind.bash-completion
diff --git a/contrib/completions/fish/bitcoin-cli.fish b/contrib/completions/fish/bitcoin-cli.fish
new file mode 100644
index 0000000000..2f034c475c
--- /dev/null
+++ b/contrib/completions/fish/bitcoin-cli.fish
@@ -0,0 +1,99 @@
+# Disable files from being included in completions by default
+complete --command bitcoin-cli --no-files
+
+function __fish_bitcoin_cli_get_commands_helper
+ set --local cmd (commandline -oc)
+
+ # Don't return commands if '-help or -?' in commandline
+ if string match --quiet --regex -- '^-help$|^-\?$' $cmd
+ return
+ end
+
+ # Strip help cmd from token to avoid duplication errors
+ set --local cmd (string match --invert --regex -- '^help$' $cmd)
+ # Strip -stdin* options to avoid waiting for input while we fetch completions
+ # TODO: this appears to be broken when run as tab completion (requires ctrl+c to exit)
+ set --local cmd (string match --invert --regex -- '^-stdin.*$' $cmd)
+
+ # Match, format and return commands
+ for command in ($cmd help 2>&1 | string match --invert -r '^\=\=.*' | string match --invert -r '^\\s*$')
+ echo $command
+ end
+end
+
+function __fish_bitcoin_cli_get_commands
+ argparse 'nohelp' 'commandsonly' -- $argv
+ set --local commands
+
+ # Exclude description, exclude help
+ if set -q _flag_nohelp; and set -q _flag_commandsonly
+ set --append commands (__fish_bitcoin_cli_get_commands_helper | string replace -r ' .*$' '' | string match --invert -r 'help')
+ # Include description, exclude help
+ else if set -q _flag_nohelp
+ set --append commands (__fish_bitcoin_cli_get_commands_helper | string replace ' ' \t | string match --invert -r 'help')
+ # Exclude description, include help
+ else if set -q _flag_commandsonly
+ set --append commands (__fish_bitcoin_cli_get_commands_helper | string replace -r ' .*$' '')
+ # Include description, include help
+ else
+ set --append commands (__fish_bitcoin_cli_get_commands_helper | string replace ' ' \t)
+ end
+
+ if string match -q -r '^.*error.*$' $commands[1]
+ # RPC offline or RPC wallet not loaded
+ return
+ else
+ for command in $commands
+ echo $command
+ end
+ end
+end
+
+
+function __fish_bitcoin_cli_get_options
+ argparse 'nofiles' -- $argv
+ set --local cmd (commandline -oc)
+ # Don't return options if '-help or -?' in commandline
+ if string match --quiet --regex -- '^-help$|-\?$' $cmd
+ return
+ end
+ set --local options
+
+ if set -q _flag_nofiles
+ set --append options ($cmd -help 2>&1 | string match -r '^ -.*' | string replace -r ' -' '-' | string replace -r '=.*' '=' | string match --invert -r '^.*=$')
+ else
+ set --append options ($cmd -help 2>&1 | string match -r '^ -.*' | string replace -r ' -' '-' | string replace -r '=.*' '=' | string match -r '^.*=$')
+ end
+
+ for option in $options
+ echo $option
+ end
+end
+
+# Add options with file completion
+# Don't offer after a command is given
+complete \
+ --command bitcoin-cli \
+ --no-files \
+ --condition "not __fish_seen_subcommand_from (__fish_bitcoin_cli_get_commands --commandsonly)" \
+ --arguments "(__fish_bitcoin_cli_get_options)"
+# Enable file completions only if the commandline now contains a `*.=` style option
+complete --command bitcoin-cli \
+ --condition 'string match --regex -- ".*=" (commandline -pt)' \
+ --force-files
+
+# Add options without file completion
+# Don't offer after a command is given
+complete \
+ --command bitcoin-cli \
+ --no-files \
+ --condition "not __fish_seen_subcommand_from (__fish_bitcoin_cli_get_commands --commandsonly)" \
+ --arguments "(__fish_bitcoin_cli_get_options --nofiles)"
+
+# Add commands
+# Permit command completions after `bitcoin-cli help` but not after other commands
+complete \
+ --command bitcoin-cli \
+ --no-files \
+ --condition "not __fish_seen_subcommand_from (__fish_bitcoin_cli_get_commands --commandsonly --nohelp)" \
+ --arguments "(__fish_bitcoin_cli_get_commands)"
diff --git a/contrib/completions/fish/bitcoin-qt.fish b/contrib/completions/fish/bitcoin-qt.fish
new file mode 100644
index 0000000000..15a355ae88
--- /dev/null
+++ b/contrib/completions/fish/bitcoin-qt.fish
@@ -0,0 +1,35 @@
+# Disable files from being included in completions by default
+complete --command bitcoin-qt --no-files
+
+# Extract options
+function __fish_bitcoinqt_get_options
+ argparse 'nofiles' -- $argv
+ set --local cmd (commandline -opc)[1]
+ set --local options
+
+ if set -q _flag_nofiles
+ set --append options ($cmd -help-debug | string match -r '^ -.*' | string replace -r ' -' '-' | string replace -r '=.*' '=' | string match --invert -r '^.*=$')
+ else
+ set --append options ($cmd -help-debug | string match -r '^ -.*' | string replace -r ' -' '-' | string replace -r '=.*' '=' | string match -r '^.*=$')
+ end
+
+ for option in $options
+ echo $option
+ end
+end
+
+
+# Add options with file completion
+complete \
+ --command bitcoin-qt \
+ --arguments "(__fish_bitcoinqt_get_options)"
+# Enable file completions only if the commandline now contains a `*.=` style option
+complete -c bitcoin-qt \
+ --condition 'string match --regex -- ".*=" (commandline -pt)' \
+ --force-files
+
+# Add options without file completion
+complete \
+ --command bitcoin-qt \
+ --arguments "(__fish_bitcoinqt_get_options --nofiles)"
+
diff --git a/contrib/completions/fish/bitcoin-tx.fish b/contrib/completions/fish/bitcoin-tx.fish
new file mode 100644
index 0000000000..0ff262b948
--- /dev/null
+++ b/contrib/completions/fish/bitcoin-tx.fish
@@ -0,0 +1,65 @@
+# Disable files from being included in completions by default
+complete --command bitcoin-tx --no-files
+
+# Modified version of __fish_seen_subcommand_from
+# Uses regex to detect cmd= syntax
+function __fish_bitcoin_seen_cmd
+ set -l cmd (commandline -oc)
+ set -e cmd[1]
+ for i in $cmd
+ for j in $argv
+ if string match --quiet --regex -- "^$j.*" $i
+ return 0
+ end
+ end
+ end
+ return 1
+end
+
+# Extract options
+function __fish_bitcoin_tx_get_options
+ set --local cmd (commandline -oc)[1]
+ if string match --quiet --regex -- '^-help$|-\?$' $cmd
+ return
+ end
+
+ for option in ($cmd -help 2>&1 | string match -r '^ -.*' | string replace -r ' -' '-' | string replace -r '=.*' '=')
+ echo $option
+ end
+end
+
+# Extract commands
+function __fish_bitcoin_tx_get_commands
+ argparse 'commandsonly' -- $argv
+ set --local cmd (commandline -oc)[1]
+ set --local commands
+
+ if set -q _flag_commandsonly
+ set --append commands ($cmd -help | sed -e '1,/Commands:/d' -e 's/=/=\t/' -e 's/(=/=/' -e '/^ [a-z]/ p' -e d | string replace -r '\ \ ' '' | string replace -r '=.*' '')
+ else
+ set --append commands ($cmd -help | sed -e '1,/Commands:/d' -e 's/=/=\t/' -e 's/(=/=/' -e '/^ [a-z]/ p' -e d | string replace -r '\ \ ' '')
+ end
+
+ for command in $commands
+ echo $command
+ end
+end
+
+# Add options
+complete \
+ --command bitcoin-tx \
+ --condition "not __fish_bitcoin_seen_cmd (__fish_bitcoin_tx_get_commands --commandsonly)" \
+ --arguments "(__fish_bitcoin_tx_get_options)" \
+ --no-files
+
+# Add commands
+complete \
+ --command bitcoin-tx \
+ --arguments "(__fish_bitcoin_tx_get_commands)" \
+ --no-files
+
+# Add file completions for load and set commands
+complete \
+ --command bitcoin-tx \
+ --condition 'string match --regex -- "(load|set)=" (commandline -pt)' \
+ --force-files
diff --git a/contrib/completions/fish/bitcoin-util.fish b/contrib/completions/fish/bitcoin-util.fish
new file mode 100644
index 0000000000..0650bf2cb6
--- /dev/null
+++ b/contrib/completions/fish/bitcoin-util.fish
@@ -0,0 +1,38 @@
+# Disable files from being included in completions by default
+complete --command bitcoin-util --no-files
+
+# Extract options
+function __fish_bitcoin_util_get_options
+ set --local cmd (commandline -opc)[1]
+ set --local options
+
+ set --append options ($cmd -help 2>&1 | string match -r '^ -.*' | string replace -r ' -' '-' | string replace -r '=.*' '=')
+
+ for option in $options
+ echo $option
+ end
+end
+
+# Extract commands
+function __fish_bitcoin_util_get_commands
+ set --local cmd (commandline -opc)[1]
+ set --local commands
+
+ set --append commands ($cmd -help | sed -e '1,/Commands:/d' -e 's/=/=\t/' -e 's/(=/=/' -e '/^ [a-z]/ p' -e d | string replace -r '\ \ ' '')
+ for command in $commands
+ echo $command
+ end
+end
+
+# Add options
+complete \
+ --command bitcoin-util \
+ --condition "not __fish_seen_subcommand_from (__fish_bitcoin_util_get_commands)" \
+ --arguments "(__fish_bitcoin_util_get_options)"
+
+# Add commands
+complete \
+ --command bitcoin-util \
+ --condition "not __fish_seen_subcommand_from (__fish_bitcoin_util_get_commands)" \
+ --arguments "(__fish_bitcoin_util_get_commands)"
+
diff --git a/contrib/completions/fish/bitcoin-wallet.fish b/contrib/completions/fish/bitcoin-wallet.fish
new file mode 100644
index 0000000000..82d8277c9b
--- /dev/null
+++ b/contrib/completions/fish/bitcoin-wallet.fish
@@ -0,0 +1,35 @@
+# Disable files from being included in completions by default
+complete --command bitcoin-wallet --no-files
+
+# Extract options
+function __fish_bitcoin_wallet_get_options
+ set --local cmd (commandline -opc)[1]
+ for option in ($cmd -help 2>&1 | string match -r '^ -.*' | string replace -r ' -' '-' | string replace -r '=.*' '=')
+ echo $option
+ end
+end
+
+# Extract commands
+function __fish_bitcoin_wallet_get_commands
+ set --local cmd (commandline -opc)[1]
+ for command in ($cmd -help | sed -e '1,/Commands:/d' -e 's/=/=\t/' -e 's/(=/=/' -e '/^ [a-z]/ p' -e d | string replace -r '\ \ ' '')
+ echo $command
+ end
+end
+
+# Add options
+complete \
+ --command bitcoin-wallet \
+ --condition "not __fish_seen_subcommand_from (__fish_bitcoin_wallet_get_commands)" \
+ --arguments "(__fish_bitcoin_wallet_get_options)"
+
+# Add commands
+complete \
+ --command bitcoin-wallet \
+ --condition "not __fish_seen_subcommand_from (__fish_bitcoin_wallet_get_commands)" \
+ --arguments "(__fish_bitcoin_wallet_get_commands)"
+
+# Add file completions for load and set commands
+complete --command bitcoin-wallet \
+ --condition "string match -r -- '(dumpfile|datadir)*=' (commandline -pt)" \
+ --force-files
diff --git a/contrib/completions/fish/bitcoind.fish b/contrib/completions/fish/bitcoind.fish
new file mode 100644
index 0000000000..fa245ae17f
--- /dev/null
+++ b/contrib/completions/fish/bitcoind.fish
@@ -0,0 +1,35 @@
+# Disable files from being included in completions by default
+complete --command bitcoind --no-files
+
+# Extract options
+function __fish_bitcoind_get_options
+ argparse 'nofiles' -- $argv
+ set --local cmd (commandline -opc)[1]
+ set --local options
+
+ if set -q _flag_nofiles
+ set --append options ($cmd -help-debug | string match -r '^ -.*' | string replace -r ' -' '-' | string replace -r '=.*' '=' | string match --invert -r '^.*=$')
+ else
+ set --append options ($cmd -help-debug | string match -r '^ -.*' | string replace -r ' -' '-' | string replace -r '=.*' '=' | string match -r '^.*=$')
+ end
+
+ for option in $options
+ echo $option
+ end
+end
+
+
+# Add options with file completion
+complete \
+ --command bitcoind \
+ --arguments "(__fish_bitcoind_get_options)"
+# Enable file completions only if the commandline now contains a `*.=` style option
+complete --command bitcoind \
+ --condition 'string match --regex -- ".*=" (commandline -pt)' \
+ --force-files
+
+# Add options without file completion
+complete \
+ --command bitcoind \
+ --arguments "(__fish_bitcoind_get_options --nofiles)"
+
diff --git a/doc/release-notes-25934.md b/doc/release-notes-25934.md
new file mode 100644
index 0000000000..b4f1ae0d3c
--- /dev/null
+++ b/doc/release-notes-25934.md
@@ -0,0 +1,8 @@
+Low-level changes
+=================
+
+RPC
+---
+
+- RPC `listsinceblock` now accepts an optional `label` argument
+ to fetch incoming transactions having the specified label. (#25934) \ No newline at end of file
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index e31ca81a08..27c426b811 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -661,9 +661,9 @@ private:
*/
bool MaybeSendGetHeaders(CNode& pfrom, const CBlockLocator& locator, Peer& peer) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
/** Potentially fetch blocks from this peer upon receipt of a new headers tip */
- void HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, const CBlockIndex* pindexLast);
+ void HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, const CBlockIndex& last_header);
/** Update peer state based on received headers message */
- void UpdatePeerStateForReceivedHeaders(CNode& pfrom, const CBlockIndex *pindexLast, bool received_new_header, bool may_have_more_headers);
+ void UpdatePeerStateForReceivedHeaders(CNode& pfrom, const CBlockIndex& last_header, bool received_new_header, bool may_have_more_headers);
void SendBlockTransactions(CNode& pfrom, Peer& peer, const CBlock& block, const BlockTransactionsRequest& req);
@@ -2622,22 +2622,21 @@ bool PeerManagerImpl::MaybeSendGetHeaders(CNode& pfrom, const CBlockLocator& loc
}
/*
- * Given a new headers tip ending in pindexLast, potentially request blocks towards that tip.
+ * Given a new headers tip ending in last_header, potentially request blocks towards that tip.
* We require that the given tip have at least as much work as our tip, and for
* our current tip to be "close to synced" (see CanDirectFetch()).
*/
-void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, const CBlockIndex* pindexLast)
+void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, const CBlockIndex& last_header)
{
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
LOCK(cs_main);
CNodeState *nodestate = State(pfrom.GetId());
- if (CanDirectFetch() && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainWork <= pindexLast->nChainWork) {
-
+ if (CanDirectFetch() && last_header.IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainWork <= last_header.nChainWork) {
std::vector<const CBlockIndex*> vToFetch;
- const CBlockIndex *pindexWalk = pindexLast;
- // Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
+ const CBlockIndex* pindexWalk{&last_header};
+ // Calculate all the blocks we'd need to switch to last_header, up to a limit.
while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
!IsBlockRequested(pindexWalk->GetBlockHash()) &&
@@ -2653,8 +2652,8 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c
// direct fetch and rely on parallel download instead.
if (!m_chainman.ActiveChain().Contains(pindexWalk)) {
LogPrint(BCLog::NET, "Large reorg, won't direct fetch to %s (%d)\n",
- pindexLast->GetBlockHash().ToString(),
- pindexLast->nHeight);
+ last_header.GetBlockHash().ToString(),
+ last_header.nHeight);
} else {
std::vector<CInv> vGetData;
// Download as much as possible, from earliest to latest.
@@ -2671,14 +2670,15 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c
}
if (vGetData.size() > 1) {
LogPrint(BCLog::NET, "Downloading blocks toward %s (%d) via headers direct fetch\n",
- pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
+ last_header.GetBlockHash().ToString(),
+ last_header.nHeight);
}
if (vGetData.size() > 0) {
if (!m_ignore_incoming_txs &&
nodestate->m_provides_cmpctblocks &&
vGetData.size() == 1 &&
mapBlocksInFlight.size() == 1 &&
- pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN)) {
+ last_header.pprev->IsValid(BLOCK_VALID_CHAIN)) {
// In any case, we want to download using a compact block, not a regular one
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
}
@@ -2689,12 +2689,12 @@ void PeerManagerImpl::HeadersDirectFetchBlocks(CNode& pfrom, const Peer& peer, c
}
/**
- * Given receipt of headers from a peer ending in pindexLast, along with
+ * Given receipt of headers from a peer ending in last_header, along with
* whether that header was new and whether the headers message was full,
* update the state we keep for the peer.
*/
void PeerManagerImpl::UpdatePeerStateForReceivedHeaders(CNode& pfrom,
- const CBlockIndex *pindexLast, bool received_new_header, bool may_have_more_headers)
+ const CBlockIndex& last_header, bool received_new_header, bool may_have_more_headers)
{
LOCK(cs_main);
CNodeState *nodestate = State(pfrom.GetId());
@@ -2703,14 +2703,13 @@ void PeerManagerImpl::UpdatePeerStateForReceivedHeaders(CNode& pfrom,
}
nodestate->nUnconnectingHeaders = 0;
- assert(pindexLast);
- UpdateBlockAvailability(pfrom.GetId(), pindexLast->GetBlockHash());
+ UpdateBlockAvailability(pfrom.GetId(), last_header.GetBlockHash());
// From here, pindexBestKnownBlock should be guaranteed to be non-null,
// because it is set in UpdateBlockAvailability. Some nullptr checks
// are still present, however, as belt-and-suspenders.
- if (received_new_header && pindexLast->nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {
+ if (received_new_header && last_header.nChainWork > m_chainman.ActiveChain().Tip()->nChainWork) {
nodestate->m_last_block_announcement = GetTime();
}
@@ -2876,7 +2875,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
return;
}
}
- Assume(pindexLast);
+ assert(pindexLast);
// Consider fetching more headers if we are not using our headers-sync mechanism.
if (nCount == MAX_HEADERS_RESULTS && !have_headers_sync) {
@@ -2887,10 +2886,10 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
}
}
- UpdatePeerStateForReceivedHeaders(pfrom, pindexLast, received_new_header, nCount == MAX_HEADERS_RESULTS);
+ UpdatePeerStateForReceivedHeaders(pfrom, *pindexLast, received_new_header, nCount == MAX_HEADERS_RESULTS);
// Consider immediately downloading blocks.
- HeadersDirectFetchBlocks(pfrom, peer, pindexLast);
+ HeadersDirectFetchBlocks(pfrom, peer, *pindexLast);
return;
}
diff --git a/src/rest.cpp b/src/rest.cpp
index a10d8a433f..033e93468e 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -305,8 +305,10 @@ static bool rest_block(const std::any& context,
if (chainman.m_blockman.IsBlockPruned(pblockindex))
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
- if (!ReadBlockFromDisk(block, pblockindex, chainman.GetParams().GetConsensus()))
- return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
+ }
+
+ if (!ReadBlockFromDisk(block, pblockindex, chainman.GetParams().GetConsensus())) {
+ return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
}
switch (rf) {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 9c8d19722b..784fb64d36 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -181,7 +181,8 @@ UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIn
case TxVerbosity::SHOW_DETAILS:
case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
CBlockUndo blockUndo;
- const bool have_undo{WITH_LOCK(::cs_main, return !blockman.IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))};
+ const bool is_not_pruned{WITH_LOCK(::cs_main, return !blockman.IsBlockPruned(blockindex))};
+ const bool have_undo{is_not_pruned && UndoReadFromDisk(blockUndo, blockindex)};
for (size_t i = 0; i < block.vtx.size(); ++i) {
const CTransactionRef& tx = block.vtx.at(i);
@@ -579,34 +580,38 @@ static RPCHelpMan getblockheader()
};
}
-static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblockindex)
{
- AssertLockHeld(::cs_main);
CBlock block;
- if (blockman.IsBlockPruned(pblockindex)) {
- throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
+ {
+ LOCK(cs_main);
+ if (blockman.IsBlockPruned(pblockindex)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
+ }
}
if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) {
// Block not found on disk. This could be because we have the block
// header in our index but not yet have the block or did not accept the
- // block.
+ // block. Or if the block was pruned right after we released the lock above.
throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
}
return block;
}
-static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex* pblockindex)
{
- AssertLockHeld(::cs_main);
CBlockUndo blockUndo;
// The Genesis block does not have undo data
if (pblockindex->nHeight == 0) return blockUndo;
- if (blockman.IsBlockPruned(pblockindex)) {
- throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
+ {
+ LOCK(cs_main);
+ if (blockman.IsBlockPruned(pblockindex)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
+ }
}
if (!UndoReadFromDisk(blockUndo, pblockindex)) {
@@ -721,7 +726,6 @@ static RPCHelpMan getblock()
}
}
- CBlock block;
const CBlockIndex* pblockindex;
const CBlockIndex* tip;
ChainstateManager& chainman = EnsureAnyChainman(request.context);
@@ -733,10 +737,10 @@ static RPCHelpMan getblock()
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
-
- block = GetBlockChecked(chainman.m_blockman, pblockindex);
}
+ const CBlock block{GetBlockChecked(chainman.m_blockman, pblockindex)};
+
if (verbosity <= 0)
{
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
@@ -1797,7 +1801,6 @@ static RPCHelpMan getblockstats()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
ChainstateManager& chainman = EnsureAnyChainman(request.context);
- LOCK(cs_main);
const CBlockIndex& pindex{*CHECK_NONFATAL(ParseHashOrHeight(request.params[0], chainman))};
std::set<std::string> stats;
diff --git a/src/rpc/txoutproof.cpp b/src/rpc/txoutproof.cpp
index cd8b49bfe1..8c5468634d 100644
--- a/src/rpc/txoutproof.cpp
+++ b/src/rpc/txoutproof.cpp
@@ -84,13 +84,13 @@ static RPCHelpMan gettxoutproof()
g_txindex->BlockUntilSyncedToCurrentChain();
}
- LOCK(cs_main);
-
if (pblockindex == nullptr) {
const CTransactionRef tx = GetTransaction(/*block_index=*/nullptr, /*mempool=*/nullptr, *setTxids.begin(), chainman.GetConsensus(), hashBlock);
if (!tx || hashBlock.IsNull()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
}
+
+ LOCK(cs_main);
pblockindex = chainman.m_blockman.LookupBlockIndex(hashBlock);
if (!pblockindex) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt");
diff --git a/src/validation.cpp b/src/validation.cpp
index 2380c5b925..d7017248fc 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2360,8 +2360,6 @@ bool Chainstate::FlushStateToDisk(
{
LOCK(cs_main);
assert(this->CanFlushToDisk());
- static std::chrono::microseconds nLastWrite{0};
- static std::chrono::microseconds nLastFlush{0};
std::set<int> setFilesToPrune;
bool full_flush_completed = false;
@@ -2415,20 +2413,20 @@ bool Chainstate::FlushStateToDisk(
}
const auto nNow = GetTime<std::chrono::microseconds>();
// Avoid writing/flushing immediately after startup.
- if (nLastWrite.count() == 0) {
- nLastWrite = nNow;
+ if (m_last_write.count() == 0) {
+ m_last_write = nNow;
}
- if (nLastFlush.count() == 0) {
- nLastFlush = nNow;
+ if (m_last_flush.count() == 0) {
+ m_last_flush = nNow;
}
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
bool fCacheLarge = mode == FlushStateMode::PERIODIC && cache_state >= CoinsCacheSizeState::LARGE;
// The cache is over the limit, we have to write now.
bool fCacheCritical = mode == FlushStateMode::IF_NEEDED && cache_state >= CoinsCacheSizeState::CRITICAL;
// It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash.
- bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow > nLastWrite + DATABASE_WRITE_INTERVAL;
+ bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow > m_last_write + DATABASE_WRITE_INTERVAL;
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
- bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + DATABASE_FLUSH_INTERVAL;
+ bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > m_last_flush + DATABASE_FLUSH_INTERVAL;
// Combine all conditions that result in a full cache flush.
fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
// Write blocks and block index to disk.
@@ -2458,7 +2456,7 @@ bool Chainstate::FlushStateToDisk(
UnlinkPrunedFiles(setFilesToPrune);
}
- nLastWrite = nNow;
+ m_last_write = nNow;
}
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
if (fDoFullFlush && !CoinsTip().GetBestBlock().IsNull()) {
@@ -2476,7 +2474,7 @@ bool Chainstate::FlushStateToDisk(
// Flush the chainstate (which may refer to block index entries).
if (!CoinsTip().Flush())
return AbortNode(state, "Failed to write to coin database");
- nLastFlush = nNow;
+ m_last_flush = nNow;
full_flush_completed = true;
TRACE5(utxocache, flush,
(int64_t)(GetTimeMicros() - nNow.count()), // in microseconds (µs)
diff --git a/src/validation.h b/src/validation.h
index a080d12fe2..00f7265793 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -743,6 +743,9 @@ private:
void UpdateTip(const CBlockIndex* pindexNew)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ std::chrono::microseconds m_last_write{0};
+ std::chrono::microseconds m_last_flush{0};
+
friend ChainstateManager;
};
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index 667cd929d1..02a1ac5ea1 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -316,7 +316,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
*/
template <class Vec>
static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nMinDepth, bool fLong,
- Vec& ret, const isminefilter& filter_ismine, const std::string* filter_label,
+ Vec& ret, const isminefilter& filter_ismine, const std::optional<std::string>& filter_label,
bool include_change = false)
EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
@@ -329,7 +329,7 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
bool involvesWatchonly = CachedTxIsFromMe(wallet, wtx, ISMINE_WATCH_ONLY);
// Sent
- if (!filter_label)
+ if (!filter_label.has_value())
{
for (const COutputEntry& s : listSent)
{
@@ -362,7 +362,7 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
if (address_book_entry) {
label = address_book_entry->GetLabel();
}
- if (filter_label && label != *filter_label) {
+ if (filter_label.has_value() && label != filter_label.value()) {
continue;
}
UniValue entry(UniValue::VOBJ);
@@ -485,10 +485,10 @@ RPCHelpMan listtransactions()
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- const std::string* filter_label = nullptr;
+ std::optional<std::string> filter_label;
if (!request.params[0].isNull() && request.params[0].get_str() != "*") {
- filter_label = &request.params[0].get_str();
- if (filter_label->empty()) {
+ filter_label = request.params[0].get_str();
+ if (filter_label.value().empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Label argument must be a valid label name or \"*\".");
}
}
@@ -552,6 +552,7 @@ RPCHelpMan listsinceblock()
{"include_removed", RPCArg::Type::BOOL, RPCArg::Default{true}, "Show transactions that were removed due to a reorg in the \"removed\" array\n"
"(not guaranteed to work on pruned nodes)"},
{"include_change", RPCArg::Type::BOOL, RPCArg::Default{false}, "Also add entries for change outputs.\n"},
+ {"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Return only incoming transactions paying to addresses with the specified label.\n"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -634,6 +635,11 @@ RPCHelpMan listsinceblock()
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
bool include_change = (!request.params[4].isNull() && request.params[4].get_bool());
+ std::optional<std::string> filter_label;
+ if (!request.params[5].isNull()) {
+ filter_label = request.params[5].get_str();
+ }
+
int depth = height ? wallet.GetLastBlockHeight() + 1 - *height : -1;
UniValue transactions(UniValue::VARR);
@@ -642,7 +648,7 @@ RPCHelpMan listsinceblock()
const CWalletTx& tx = pairWtx.second;
if (depth == -1 || abs(wallet.GetTxDepthInMainChain(tx)) < depth) {
- ListTransactions(wallet, tx, 0, true, transactions, filter, /*filter_label=*/nullptr, /*include_change=*/include_change);
+ ListTransactions(wallet, tx, 0, true, transactions, filter, filter_label, include_change);
}
}
@@ -659,7 +665,7 @@ RPCHelpMan listsinceblock()
if (it != wallet.mapWallet.end()) {
// We want all transactions regardless of confirmation count to appear here,
// even negative confirmation ones, hence the big negative.
- ListTransactions(wallet, it->second, -100000000, true, removed, filter, /*filter_label=*/nullptr, /*include_change=*/include_change);
+ ListTransactions(wallet, it->second, -100000000, true, removed, filter, filter_label, include_change);
}
}
blockId = block.hashPrevBlock;
@@ -777,7 +783,7 @@ RPCHelpMan gettransaction()
WalletTxToJSON(*pwallet, wtx, entry);
UniValue details(UniValue::VARR);
- ListTransactions(*pwallet, wtx, 0, false, details, filter, /*filter_label=*/nullptr);
+ ListTransactions(*pwallet, wtx, 0, false, details, filter, /*filter_label=*/std::nullopt);
entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
diff --git a/src/zmq/zmqpublishnotifier.cpp b/src/zmq/zmqpublishnotifier.cpp
index eaf3455296..c785a929d3 100644
--- a/src/zmq/zmqpublishnotifier.cpp
+++ b/src/zmq/zmqpublishnotifier.cpp
@@ -248,18 +248,14 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
const Consensus::Params& consensusParams = Params().GetConsensus();
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
- {
- LOCK(cs_main);
- CBlock block;
- if(!ReadBlockFromDisk(block, pindex, consensusParams))
- {
- zmqError("Can't read block from disk");
- return false;
- }
-
- ss << block;
+ CBlock block;
+ if (!ReadBlockFromDisk(block, pindex, consensusParams)) {
+ zmqError("Can't read block from disk");
+ return false;
}
+ ss << block;
+
return SendZmqMessage(MSG_RAWBLOCK, &(*ss.begin()), ss.size());
}
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index b0cbcf4edf..0357a6f281 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -62,7 +62,7 @@ def small_txpuzzle_randfee(
unconflist.append({"txid": txid, "vout": 0, "value": total_in - amount - fee})
unconflist.append({"txid": txid, "vout": 1, "value": amount})
- return (tx.serialize().hex(), fee)
+ return (tx.get_vsize(), fee)
def check_raw_estimates(node, fees_seen):
@@ -158,7 +158,7 @@ class EstimateFeeTest(BitcoinTestFramework):
random.shuffle(self.confutxo)
for _ in range(random.randrange(100 - 50, 100 + 50)):
from_index = random.randint(1, 2)
- (txhex, fee) = small_txpuzzle_randfee(
+ (tx_bytes, fee) = small_txpuzzle_randfee(
self.wallet,
self.nodes[from_index],
self.confutxo,
@@ -167,7 +167,7 @@ class EstimateFeeTest(BitcoinTestFramework):
min_fee,
min_fee,
)
- tx_kbytes = (len(txhex) // 2) / 1000.0
+ tx_kbytes = tx_bytes / 1000.0
self.fees_per_kb.append(float(fee) / tx_kbytes)
self.sync_mempools(wait=0.1)
mined = mining_node.getblock(self.generate(mining_node, 1)[0], True)["tx"]
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index ecdfb7d0e3..62e9c5ba97 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -49,6 +49,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
self.test_desc()
self.test_send_to_self()
self.test_op_return()
+ self.test_label()
def test_no_blockhash(self):
self.log.info("Test no blockhash")
@@ -465,6 +466,20 @@ class ListSinceBlockTest(BitcoinTestFramework):
assert 'address' not in op_ret_tx
+ def test_label(self):
+ self.log.info('Test passing "label" argument fetches incoming transactions having the specified label')
+ new_addr = self.nodes[1].getnewaddress(label="new_addr", address_type="bech32")
+
+ self.nodes[2].sendtoaddress(address=new_addr, amount="0.001")
+ self.generate(self.nodes[2], 1)
+
+ for label in ["new_addr", ""]:
+ new_addr_transactions = self.nodes[1].listsinceblock(label=label)["transactions"]
+ assert_equal(len(new_addr_transactions), 1)
+ assert_equal(new_addr_transactions[0]["label"], label)
+ if label == "new_addr":
+ assert_equal(new_addr_transactions[0]["address"], new_addr)
+
if __name__ == '__main__':
ListSinceBlockTest().main()