aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac6
-rw-r--r--doc/release-notes.md6
-rw-r--r--src/Makefile.test.include1
-rw-r--r--src/Makefile.test_util.include1
-rw-r--r--src/addrman.cpp24
-rw-r--r--src/addrman.h18
-rw-r--r--src/chain.h24
-rw-r--r--src/chainparams.cpp45
-rw-r--r--src/chainparamsbase.cpp2
-rw-r--r--src/policy/rbf.cpp45
-rw-r--r--src/policy/rbf.h11
-rw-r--r--src/rpc/blockchain.cpp84
-rw-r--r--src/rpc/external_signer.cpp7
-rw-r--r--src/rpc/mining.cpp9
-rw-r--r--src/rpc/misc.cpp10
-rw-r--r--src/rpc/net.cpp12
-rw-r--r--src/rpc/rawtransaction.cpp58
-rw-r--r--src/test/fuzz/addrman.cpp11
-rw-r--r--src/test/fuzz/data_stream.cpp30
-rw-r--r--src/test/fuzz/utxo_snapshot.cpp1
-rw-r--r--src/test/txvalidationcache_tests.cpp9
-rw-r--r--src/test/util/chainstate.h54
-rw-r--r--src/test/util/setup_common.cpp29
-rw-r--r--src/test/util/setup_common.h19
-rw-r--r--src/test/validation_chainstate_tests.cpp76
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp66
-rw-r--r--src/test/validation_flush_tests.cpp4
-rw-r--r--src/timedata.cpp8
-rw-r--r--src/txmempool.cpp34
-rw-r--r--src/txmempool.h25
-rw-r--r--src/util/rbf.h6
-rw-r--r--src/validation.cpp170
-rw-r--r--src/validation.h46
-rw-r--r--src/wallet/rpcwallet.cpp89
-rwxr-xr-xtest/functional/feature_addrman.py11
-rwxr-xr-xtest/functional/feature_bip68_sequence.py10
-rwxr-xr-xtest/functional/feature_block.py5
-rwxr-xr-xtest/functional/feature_cltv.py5
-rwxr-xr-xtest/functional/feature_csv_activation.py5
-rwxr-xr-xtest/functional/feature_dersig.py10
-rwxr-xr-xtest/functional/feature_nulldummy.py2
-rwxr-xr-xtest/functional/feature_presegwit_node_upgrade.py6
-rwxr-xr-xtest/functional/feature_rbf.py71
-rwxr-xr-xtest/functional/feature_segwit.py6
-rwxr-xr-xtest/functional/mempool_persist.py60
-rwxr-xr-xtest/functional/p2p_addr_relay.py14
-rwxr-xr-xtest/functional/p2p_addrfetch.py13
-rwxr-xr-xtest/functional/p2p_addrv2_relay.py9
-rwxr-xr-xtest/functional/p2p_segwit.py43
-rwxr-xr-xtest/functional/rpc_blockchain.py12
-rwxr-xr-xtest/functional/rpc_net.py12
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py3
-rw-r--r--test/functional/test_framework/blocktools.py5
-rwxr-xr-xtest/functional/test_framework/p2p.py2
-rw-r--r--test/functional/test_framework/util.py11
-rw-r--r--test/functional/test_framework/wallet.py11
-rwxr-xr-xtest/get_previous_releases.py1
-rw-r--r--test/sanitizer_suppressions/ubsan2
58 files changed, 809 insertions, 560 deletions
diff --git a/configure.ac b/configure.ac
index 0dc480e6c1..e30159f91b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -568,13 +568,17 @@ AX_CHECK_COMPILE_FLAG([-march=armv8-a+crc+crypto],[[ARM_CRC_CXXFLAGS="-march=arm
TEMP_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS $ARM_CRC_CXXFLAGS"
-AC_MSG_CHECKING(for ARM CRC32 intrinsics)
+AC_MSG_CHECKING(for AArch64 CRC32 intrinsics)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <arm_acle.h>
#include <arm_neon.h>
]],[[
+#ifdef __aarch64__
__crc32cb(0, 0); __crc32ch(0, 0); __crc32cw(0, 0); __crc32cd(0, 0);
vmull_p64(0, 0);
+#else
+#error "crc32c library does not support hardware acceleration on 32-bit ARM"
+#endif
]])],
[ AC_MSG_RESULT(yes); enable_arm_crc=yes; ],
[ AC_MSG_RESULT(no)]
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 915bda2ea3..0918adb8c2 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -113,10 +113,8 @@ Tests
-----
- For the `regtest` network the activation heights of several softforks were
- changed.
- * BIP 34 (blockheight in coinbase) from 500 to 2 (#16333)
- * BIP 66 (DERSIG) from 1251 to 102 (#22632)
- * BIP 65 (CLTV) from 1351 to 111 (#21862)
+ set to block height 1. They can be changed by the runtime setting
+ `-testactivationheight=name@height`. (#22818)
Credits
=======
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index a85a359960..be63214c23 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -232,7 +232,6 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/crypto_hkdf_hmac_sha256_l32.cpp \
test/fuzz/crypto_poly1305.cpp \
test/fuzz/cuckoocache.cpp \
- test/fuzz/data_stream.cpp \
test/fuzz/decode_tx.cpp \
test/fuzz/descriptor_parse.cpp \
test/fuzz/deserialize.cpp \
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index 85e50ebf70..0a3b99e7d2 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -9,6 +9,7 @@ EXTRA_LIBRARIES += \
TEST_UTIL_H = \
test/util/blockfilter.h \
+ test/util/chainstate.h \
test/util/logging.h \
test/util/mining.h \
test/util/net.h \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index a1e8cb1bf1..7c6b8fe64d 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -386,7 +386,12 @@ void CAddrMan::Unserialize(Stream& s_)
LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n", nLostUnk, nLost);
}
- Check();
+ const int check_code{ForceCheckAddrman()};
+ if (check_code != 0) {
+ throw std::ios_base::failure(strprintf(
+ "Corrupt data. Consistency check failed with code %s",
+ check_code));
+ }
}
// explicit instantiation
@@ -743,13 +748,24 @@ CAddrInfo CAddrMan::Select_(bool newOnly) const
}
}
-int CAddrMan::Check_() const
+void CAddrMan::Check() const
{
AssertLockHeld(cs);
// Run consistency checks 1 in m_consistency_check_ratio times if enabled
- if (m_consistency_check_ratio == 0) return 0;
- if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return 0;
+ if (m_consistency_check_ratio == 0) return;
+ if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return;
+
+ const int err{ForceCheckAddrman()};
+ if (err) {
+ LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
+ assert(false);
+ }
+}
+
+int CAddrMan::ForceCheckAddrman() const
+{
+ AssertLockHeld(cs);
LogPrint(BCLog::ADDRMAN, "Addrman checks started: new %i, tried %i, total %u\n", nNew, nTried, vRandom.size());
diff --git a/src/addrman.h b/src/addrman.h
index 0885231ebc..7dd8528bef 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -391,20 +391,12 @@ private:
//! Return a random to-be-evicted tried table address.
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
- //! Consistency check
- void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs)
- {
- AssertLockHeld(cs);
-
- const int err = Check_();
- if (err) {
- LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
- assert(false);
- }
- }
+ //! Consistency check, taking into account m_consistency_check_ratio. Will std::abort if an inconsistency is detected.
+ void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs);
- //! Perform consistency check. Returns an error code or zero.
- int Check_() const EXCLUSIVE_LOCKS_REQUIRED(cs);
+ //! Perform consistency check, regardless of m_consistency_check_ratio.
+ //! @returns an error code or zero.
+ int ForceCheckAddrman() const EXCLUSIVE_LOCKS_REQUIRED(cs);
/**
* Return all or many randomly selected addresses, optionally by network.
diff --git a/src/chain.h b/src/chain.h
index 84a3a4e1e7..365a7f79b6 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -126,7 +126,15 @@ enum BlockStatus: uint32_t {
BLOCK_FAILED_CHILD = 64, //!< descends from failed block
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
- BLOCK_OPT_WITNESS = 128, //!< block data in blk*.data was received with a witness-enforcing client
+ BLOCK_OPT_WITNESS = 128, //!< block data in blk*.dat was received with a witness-enforcing client
+
+ /**
+ * If set, this indicates that the block index entry is assumed-valid.
+ * Certain diagnostics will be skipped in e.g. CheckBlockIndex().
+ * It almost certainly means that the block's full validation is pending
+ * on a background chainstate. See `doc/assumeutxo.md`.
+ */
+ BLOCK_ASSUMED_VALID = 256,
};
/** The block chain is a tree shaped structure starting with the
@@ -300,14 +308,24 @@ public:
return ((nStatus & BLOCK_VALID_MASK) >= nUpTo);
}
+ //! @returns true if the block is assumed-valid; this means it is queued to be
+ //! validated by a background chainstate.
+ bool IsAssumedValid() const { return nStatus & BLOCK_ASSUMED_VALID; }
+
//! Raise the validity level of this block index entry.
//! Returns true if the validity was changed.
bool RaiseValidity(enum BlockStatus nUpTo)
{
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
- if (nStatus & BLOCK_FAILED_MASK)
- return false;
+ if (nStatus & BLOCK_FAILED_MASK) return false;
+
if ((nStatus & BLOCK_VALID_MASK) < nUpTo) {
+ // If this block had been marked assumed-valid and we're raising
+ // its validity to a certain point, there is no longer an assumption.
+ if (nStatus & BLOCK_ASSUMED_VALID && nUpTo >= BLOCK_VALID_SCRIPTS) {
+ nStatus &= ~BLOCK_ASSUMED_VALID;
+ }
+
nStatus = (nStatus & ~BLOCK_VALID_MASK) | nUpTo;
return true;
}
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 4cc37560a3..fdee64c529 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -390,12 +390,12 @@ public:
consensus.signet_challenge.clear();
consensus.nSubsidyHalvingInterval = 150;
consensus.BIP16Exception = uint256();
- consensus.BIP34Height = 2; // BIP34 activated on regtest (Block at height 1 not enforced for testing purposes)
+ consensus.BIP34Height = 1; // Always active unless overridden
consensus.BIP34Hash = uint256();
- consensus.BIP65Height = 111; // BIP65 activated on regtest (Block at height 110 and earlier not enforced for testing purposes)
- consensus.BIP66Height = 102; // BIP66 activated on regtest (Block at height 101 and earlier not enforced for testing purposes)
- consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests)
- consensus.SegwitHeight = 0; // SEGWIT is always activated on regtest unless overridden
+ consensus.BIP65Height = 1; // Always active unless overridden
+ consensus.BIP66Height = 1; // Always active unless overridden
+ consensus.CSVHeight = 1; // Always active unless overridden
+ consensus.SegwitHeight = 1; // Always active unless overridden
consensus.MinBIP9WarningHeight = 0;
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
@@ -487,15 +487,38 @@ public:
void UpdateActivationParametersFromArgs(const ArgsManager& args);
};
-void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
+static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& consensus)
{
- if (args.IsArgSet("-segwitheight")) {
- int64_t height = args.GetArg("-segwitheight", consensus.SegwitHeight);
- if (height < 0 || height >= std::numeric_limits<int>::max()) {
- throw std::runtime_error(strprintf("Activation height %ld for segwit is out of valid range.", height));
+ for (const std::string& arg : args.GetArgs("-testactivationheight")) {
+ const auto found{arg.find('@')};
+ if (found == std::string::npos) {
+ throw std::runtime_error(strprintf("Invalid format (%s) for -testactivationheight=name@height.", arg));
+ }
+ const auto name{arg.substr(0, found)};
+ const auto value{arg.substr(found + 1)};
+ int32_t height;
+ if (!ParseInt32(value, &height) || height < 0 || height >= std::numeric_limits<int>::max()) {
+ throw std::runtime_error(strprintf("Invalid height value (%s) for -testactivationheight=name@height.", arg));
+ }
+ if (name == "segwit") {
+ consensus.SegwitHeight = int{height};
+ } else if (name == "bip34") {
+ consensus.BIP34Height = int{height};
+ } else if (name == "dersig") {
+ consensus.BIP66Height = int{height};
+ } else if (name == "cltv") {
+ consensus.BIP65Height = int{height};
+ } else if (name == "csv") {
+ consensus.CSVHeight = int{height};
+ } else {
+ throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg));
}
- consensus.SegwitHeight = static_cast<int>(height);
}
+}
+
+void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
+{
+ MaybeUpdateHeights(args, consensus);
if (!args.IsArgSet("-vbparams")) return;
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index 79c1bc25bc..dc484f5c03 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -20,7 +20,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: main, test, signet, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
- argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp
index 15527afb8a..7ac2e22006 100644
--- a/src/policy/rbf.cpp
+++ b/src/policy/rbf.cpp
@@ -48,17 +48,19 @@ RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
}
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
- CTxMemPool& pool,
- const CTxMemPool::setEntries& iters_conflicting,
- CTxMemPool::setEntries& all_conflicts)
+ CTxMemPool& pool,
+ const CTxMemPool::setEntries& iters_conflicting,
+ CTxMemPool::setEntries& all_conflicts)
{
AssertLockHeld(pool.cs);
const uint256 txid = tx.GetHash();
uint64_t nConflictingCount = 0;
for (const auto& mi : iters_conflicting) {
nConflictingCount += mi->GetCountWithDescendants();
- // This potentially overestimates the number of actual descendants but we just want to be
- // conservative to avoid doing too much work.
+ // BIP125 Rule #5: don't consider replacing more than MAX_BIP125_REPLACEMENT_CANDIDATES
+ // entries from the mempool. This potentially overestimates the number of actual
+ // descendants (i.e. if multiple conflicts share a descendant, it will be counted multiple
+ // times), but we just want to be conservative to avoid doing too much work.
if (nConflictingCount > MAX_BIP125_REPLACEMENT_CANDIDATES) {
return strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
txid.ToString(),
@@ -66,8 +68,7 @@ std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
MAX_BIP125_REPLACEMENT_CANDIDATES);
}
}
- // If not too many to replace, then calculate the set of
- // transactions that would have to be evicted
+ // Calculate the set of all transactions that would have to be evicted.
for (CTxMemPool::txiter it : iters_conflicting) {
pool.CalculateDescendants(it, all_conflicts);
}
@@ -81,19 +82,19 @@ std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
AssertLockHeld(pool.cs);
std::set<uint256> parents_of_conflicts;
for (const auto& mi : iters_conflicting) {
- for (const CTxIn &txin : mi->GetTx().vin) {
+ for (const CTxIn& txin : mi->GetTx().vin) {
parents_of_conflicts.insert(txin.prevout.hash);
}
}
for (unsigned int j = 0; j < tx.vin.size(); j++) {
- // We don't want to accept replacements that require low feerate junk to be mined first.
- // Ideally we'd keep track of the ancestor feerates and make the decision based on that, but
- // for now requiring all new inputs to be confirmed works.
+ // BIP125 Rule #2: We don't want to accept replacements that require low feerate junk to be
+ // mined first. Ideally we'd keep track of the ancestor feerates and make the decision
+ // based on that, but for now requiring all new inputs to be confirmed works.
//
// Note that if you relax this to make RBF a little more useful, this may break the
- // CalculateMempoolAncestors RBF relaxation, above. See the comment above the first
- // CalculateMempoolAncestors call for more info.
+ // CalculateMempoolAncestors RBF relaxation which subtracts the conflict count/size from the
+ // descendant limit.
if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) {
// Rather than check the UTXO set - potentially expensive - it's cheaper to just check
// if the new input refers to a tx that's in the mempool.
@@ -111,7 +112,7 @@ std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries&
const uint256& txid)
{
for (CTxMemPool::txiter ancestorIt : ancestors) {
- const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
+ const uint256& hashAncestor = ancestorIt->GetTx().GetHash();
if (direct_conflicts.count(hashAncestor)) {
return strprintf("%s spends conflicting transaction %s",
txid.ToString(),
@@ -150,24 +151,26 @@ std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& i
std::optional<std::string> PaysForRBF(CAmount original_fees,
CAmount replacement_fees,
size_t replacement_vsize,
+ CFeeRate relay_fee,
const uint256& txid)
{
- // The replacement must pay greater fees than the transactions it
- // replaces - if we did the bandwidth used by those conflicting
- // transactions would not be paid for.
+ // BIP125 Rule #3: The replacement fees must be greater than or equal to fees of the
+ // transactions it replaces, otherwise the bandwidth used by those conflicting transactions
+ // would not be paid for.
if (replacement_fees < original_fees) {
return strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees));
}
- // Finally in addition to paying more fees than the conflicts the
- // new transaction must pay for its own bandwidth.
+ // BIP125 Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS
+ // vector where attackers can cause a transaction to be replaced (and relayed) repeatedly by
+ // increasing the fee by tiny amounts.
CAmount additional_fees = replacement_fees - original_fees;
- if (additional_fees < ::incrementalRelayFee.GetFee(replacement_vsize)) {
+ if (additional_fees < relay_fee.GetFee(replacement_vsize)) {
return strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
txid.ToString(),
FormatMoney(additional_fees),
- FormatMoney(::incrementalRelayFee.GetFee(replacement_vsize)));
+ FormatMoney(relay_fee.GetFee(replacement_vsize)));
}
return std::nullopt;
}
diff --git a/src/policy/rbf.h b/src/policy/rbf.h
index 56468a09b2..be8c2e5b8b 100644
--- a/src/policy/rbf.h
+++ b/src/policy/rbf.h
@@ -5,7 +5,12 @@
#ifndef BITCOIN_POLICY_RBF_H
#define BITCOIN_POLICY_RBF_H
+#include <primitives/transaction.h>
#include <txmempool.h>
+#include <uint256.h>
+
+#include <optional>
+#include <string>
/** Maximum number of transactions that can be replaced by BIP125 RBF (Rule #5). This includes all
* mempool conflicts and their descendants. */
@@ -48,14 +53,14 @@ RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx);
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx, CTxMemPool& pool,
const CTxMemPool::setEntries& iters_conflicting,
CTxMemPool::setEntries& all_conflicts)
- EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
+ EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
/** BIP125 Rule #2: "The replacement transaction may only include an unconfirmed input if that input
* was included in one of the original transactions."
* @returns error message if Rule #2 is broken, otherwise std::nullopt. */
std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx, const CTxMemPool& pool,
const CTxMemPool::setEntries& iters_conflicting)
- EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
+ EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
/** Check the intersection between two sets of transactions (a set of mempool entries and a set of
* txids) to make sure they are disjoint.
@@ -84,12 +89,14 @@ std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& i
* @param[in] original_fees Total modified fees of original transaction(s).
* @param[in] replacement_fees Total modified fees of replacement transaction(s).
* @param[in] replacement_vsize Total virtual size of replacement transaction(s).
+ * @param[in] relay_fee The node's minimum feerate for transaction relay.
* @param[in] txid Transaction ID, included in the error message if violation occurs.
* @returns error string if fees are insufficient, otherwise std::nullopt.
*/
std::optional<std::string> PaysForRBF(CAmount original_fees,
CAmount replacement_fees,
size_t replacement_vsize,
+ CFeeRate relay_fee,
const uint256& txid);
#endif // BITCOIN_POLICY_RBF_H
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 909019d796..08d21e0f71 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1115,11 +1115,11 @@ static RPCHelpMan gettxoutsetinfo()
{RPCResult::Type::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"},
{RPCResult::Type::STR_HEX, "hash_serialized_2", /* optional */ true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
{RPCResult::Type::STR_HEX, "muhash", /* optional */ true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
- {RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
- {RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
+ {RPCResult::Type::NUM, "transactions", /* optional */ true, "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
+ {RPCResult::Type::NUM, "disk_size", /* optional */ true, "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of coins in the UTXO set"},
- {RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"},
- {RPCResult::Type::OBJ, "block_info", "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
+ {RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", /* optional */ true, "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"},
+ {RPCResult::Type::OBJ, "block_info", /* optional */ true, "Info on amounts in the block at this block height (only available if coinstatsindex is used)",
{
{RPCResult::Type::STR_AMOUNT, "prevout_spent", "Total amount of all prevouts spent in this block"},
{RPCResult::Type::STR_AMOUNT, "coinbase", "Coinbase subsidy amount of this block"},
@@ -1436,32 +1436,32 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
{RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"},
{RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"},
- {RPCResult::Type::NUM, "pruneheight", "lowest-height complete block stored (only present if pruning is enabled)"},
- {RPCResult::Type::BOOL, "automatic_pruning", "whether automatic pruning is enabled (only present if pruning is enabled)"},
- {RPCResult::Type::NUM, "prune_target_size", "the target size used by pruning (only present if automatic pruning is enabled)"},
+ {RPCResult::Type::NUM, "pruneheight", /* optional */ true, "lowest-height complete block stored (only present if pruning is enabled)"},
+ {RPCResult::Type::BOOL, "automatic_pruning", /* optional */ true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
+ {RPCResult::Type::NUM, "prune_target_size", /* optional */ true, "the target size used by pruning (only present if automatic pruning is enabled)"},
{RPCResult::Type::OBJ_DYN, "softforks", "status of softforks",
{
{RPCResult::Type::OBJ, "xxxx", "name of the softfork",
{
{RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
- {RPCResult::Type::OBJ, "bip9", "status of bip9 softforks (only for \"bip9\" type)",
+ {RPCResult::Type::OBJ, "bip9", /* optional */ true, "status of bip9 softforks (only for \"bip9\" type)",
{
{RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
- {RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
+ {RPCResult::Type::NUM, "bit", /* optional */ true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
{RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
{RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
{RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
- {RPCResult::Type::OBJ, "statistics", "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
+ {RPCResult::Type::OBJ, "statistics", /* optional */ true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
{
{RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
- {RPCResult::Type::NUM, "threshold", "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
+ {RPCResult::Type::NUM, "threshold", /* optional */ true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
{RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
{RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
- {RPCResult::Type::BOOL, "possible", "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
+ {RPCResult::Type::BOOL, "possible", /* optional */ true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
}},
}},
- {RPCResult::Type::NUM, "height", "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
+ {RPCResult::Type::NUM, "height", /* optional */ true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
{RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
}},
}},
@@ -1971,11 +1971,11 @@ static RPCHelpMan getblockstats()
RPCResult{
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::NUM, "avgfee", "Average fee in the block"},
- {RPCResult::Type::NUM, "avgfeerate", "Average feerate (in satoshis per virtual byte)"},
- {RPCResult::Type::NUM, "avgtxsize", "Average transaction size"},
- {RPCResult::Type::STR_HEX, "blockhash", "The block hash (to check for potential reorgs)"},
- {RPCResult::Type::ARR_FIXED, "feerate_percentiles", "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)",
+ {RPCResult::Type::NUM, "avgfee", /* optional */ true, "Average fee in the block"},
+ {RPCResult::Type::NUM, "avgfeerate", /* optional */ true, "Average feerate (in satoshis per virtual byte)"},
+ {RPCResult::Type::NUM, "avgtxsize", /* optional */ true, "Average transaction size"},
+ {RPCResult::Type::STR_HEX, "blockhash", /* optional */ true, "The block hash (to check for potential reorgs)"},
+ {RPCResult::Type::ARR_FIXED, "feerate_percentiles", /* optional */ true, "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)",
{
{RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"},
{RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"},
@@ -1983,30 +1983,30 @@ static RPCHelpMan getblockstats()
{RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"},
{RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"},
}},
- {RPCResult::Type::NUM, "height", "The height of the block"},
- {RPCResult::Type::NUM, "ins", "The number of inputs (excluding coinbase)"},
- {RPCResult::Type::NUM, "maxfee", "Maximum fee in the block"},
- {RPCResult::Type::NUM, "maxfeerate", "Maximum feerate (in satoshis per virtual byte)"},
- {RPCResult::Type::NUM, "maxtxsize", "Maximum transaction size"},
- {RPCResult::Type::NUM, "medianfee", "Truncated median fee in the block"},
- {RPCResult::Type::NUM, "mediantime", "The block median time past"},
- {RPCResult::Type::NUM, "mediantxsize", "Truncated median transaction size"},
- {RPCResult::Type::NUM, "minfee", "Minimum fee in the block"},
- {RPCResult::Type::NUM, "minfeerate", "Minimum feerate (in satoshis per virtual byte)"},
- {RPCResult::Type::NUM, "mintxsize", "Minimum transaction size"},
- {RPCResult::Type::NUM, "outs", "The number of outputs"},
- {RPCResult::Type::NUM, "subsidy", "The block subsidy"},
- {RPCResult::Type::NUM, "swtotal_size", "Total size of all segwit transactions"},
- {RPCResult::Type::NUM, "swtotal_weight", "Total weight of all segwit transactions"},
- {RPCResult::Type::NUM, "swtxs", "The number of segwit transactions"},
- {RPCResult::Type::NUM, "time", "The block time"},
- {RPCResult::Type::NUM, "total_out", "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"},
- {RPCResult::Type::NUM, "total_size", "Total size of all non-coinbase transactions"},
- {RPCResult::Type::NUM, "total_weight", "Total weight of all non-coinbase transactions"},
- {RPCResult::Type::NUM, "totalfee", "The fee total"},
- {RPCResult::Type::NUM, "txs", "The number of transactions (including coinbase)"},
- {RPCResult::Type::NUM, "utxo_increase", "The increase/decrease in the number of unspent outputs"},
- {RPCResult::Type::NUM, "utxo_size_inc", "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
+ {RPCResult::Type::NUM, "height", /* optional */ true, "The height of the block"},
+ {RPCResult::Type::NUM, "ins", /* optional */ true, "The number of inputs (excluding coinbase)"},
+ {RPCResult::Type::NUM, "maxfee", /* optional */ true, "Maximum fee in the block"},
+ {RPCResult::Type::NUM, "maxfeerate", /* optional */ true, "Maximum feerate (in satoshis per virtual byte)"},
+ {RPCResult::Type::NUM, "maxtxsize", /* optional */ true, "Maximum transaction size"},
+ {RPCResult::Type::NUM, "medianfee", /* optional */ true, "Truncated median fee in the block"},
+ {RPCResult::Type::NUM, "mediantime", /* optional */ true, "The block median time past"},
+ {RPCResult::Type::NUM, "mediantxsize", /* optional */ true, "Truncated median transaction size"},
+ {RPCResult::Type::NUM, "minfee", /* optional */ true, "Minimum fee in the block"},
+ {RPCResult::Type::NUM, "minfeerate", /* optional */ true, "Minimum feerate (in satoshis per virtual byte)"},
+ {RPCResult::Type::NUM, "mintxsize", /* optional */ true, "Minimum transaction size"},
+ {RPCResult::Type::NUM, "outs", /* optional */ true, "The number of outputs"},
+ {RPCResult::Type::NUM, "subsidy", /* optional */ true, "The block subsidy"},
+ {RPCResult::Type::NUM, "swtotal_size", /* optional */ true, "Total size of all segwit transactions"},
+ {RPCResult::Type::NUM, "swtotal_weight", /* optional */ true, "Total weight of all segwit transactions"},
+ {RPCResult::Type::NUM, "swtxs", /* optional */ true, "The number of segwit transactions"},
+ {RPCResult::Type::NUM, "time", /* optional */ true, "The block time"},
+ {RPCResult::Type::NUM, "total_out", /* optional */ true, "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"},
+ {RPCResult::Type::NUM, "total_size", /* optional */ true, "Total size of all non-coinbase transactions"},
+ {RPCResult::Type::NUM, "total_weight", /* optional */ true, "Total weight of all non-coinbase transactions"},
+ {RPCResult::Type::NUM, "totalfee", /* optional */ true, "The fee total"},
+ {RPCResult::Type::NUM, "txs", /* optional */ true, "The number of transactions (including coinbase)"},
+ {RPCResult::Type::NUM, "utxo_increase", /* optional */ true, "The increase/decrease in the number of unspent outputs"},
+ {RPCResult::Type::NUM, "utxo_size_inc", /* optional */ true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"},
}},
RPCExamples{
HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") +
diff --git a/src/rpc/external_signer.cpp b/src/rpc/external_signer.cpp
index 6ec2b1a07f..60ec15e904 100644
--- a/src/rpc/external_signer.cpp
+++ b/src/rpc/external_signer.cpp
@@ -24,8 +24,11 @@ static RPCHelpMan enumeratesigners()
{
{RPCResult::Type::ARR, "signers", /* optional */ false, "",
{
- {RPCResult::Type::STR_HEX, "masterkeyfingerprint", "Master key fingerprint"},
- {RPCResult::Type::STR, "name", "Device name"},
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "fingerprint", "Master key fingerprint"},
+ {RPCResult::Type::STR, "name", "Device name"},
+ }},
},
}
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 27cbb3a702..eb2d5b9c5f 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -553,6 +553,10 @@ static RPCHelpMan getblocktemplate()
{
{RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"},
}},
+ {RPCResult::Type::ARR, "capabilities", "",
+ {
+ {RPCResult::Type::STR, "value", "A supported feature, for example 'proposal'"},
+ }},
{RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
{RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
{RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
@@ -586,11 +590,12 @@ static RPCHelpMan getblocktemplate()
{RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"},
{RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"},
{RPCResult::Type::NUM, "sizelimit", "limit of block size"},
- {RPCResult::Type::NUM, "weightlimit", "limit of block weight"},
+ {RPCResult::Type::NUM, "weightlimit", /* optional */ true, "limit of block weight"},
{RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
{RPCResult::Type::STR, "bits", "compressed target of next block"},
{RPCResult::Type::NUM, "height", "The height of the next block"},
- {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"},
+ {RPCResult::Type::STR_HEX, "signet_challenge", /* optional */ true, "Only on signet"},
+ {RPCResult::Type::STR_HEX, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"},
}},
},
RPCExamples{
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 14b0e5a984..dff2129980 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -44,10 +44,10 @@ static RPCHelpMan validateaddress()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"},
- {RPCResult::Type::STR, "address", "The bitcoin address validated"},
- {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address"},
- {RPCResult::Type::BOOL, "isscript", "If the key is a script"},
- {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address"},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The bitcoin address validated"},
+ {RPCResult::Type::STR_HEX, "scriptPubKey", /* optional */ true, "The hex-encoded scriptPubKey generated by the address"},
+ {RPCResult::Type::BOOL, "isscript", /* optional */ true, "If the key is a script"},
+ {RPCResult::Type::BOOL, "iswitness", /* optional */ true, "If the address is a witness address"},
{RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program"},
{RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program"},
{RPCResult::Type::STR, "error", /* optional */ true, "Error message, if any"},
@@ -717,7 +717,7 @@ static RPCHelpMan getindexinfo()
{"index_name", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Filter results for an index with a specific name."},
},
RPCResult{
- RPCResult::Type::OBJ, "", "", {
+ RPCResult::Type::OBJ_DYN, "", "", {
{
RPCResult::Type::OBJ, "name", "The name of the index",
{
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 227eec722f..a9bee33c5d 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -116,10 +116,10 @@ static RPCHelpMan getpeerinfo()
{
{RPCResult::Type::NUM, "id", "Peer index"},
{RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
- {RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"},
- {RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"},
+ {RPCResult::Type::STR, "addrbind", /* optional */ true, "(ip:port) Bind address of the connection to the peer"},
+ {RPCResult::Type::STR, "addrlocal", /* optional */ true, "(ip:port) Local address as reported by the peer"},
{RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/* append_unroutable */ true), ", ") + ")"},
- {RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n"
+ {RPCResult::Type::NUM, "mapped_as", /* optional */ true, "The AS in the BGP route to the peer used for diversifying\n"
"peer selection (only available if the asmap config flag is set)"},
{RPCResult::Type::STR_HEX, "services", "The services offered"},
{RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form",
@@ -135,9 +135,9 @@ static RPCHelpMan getpeerinfo()
{RPCResult::Type::NUM, "bytesrecv", "The total bytes received"},
{RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"},
{RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"},
- {RPCResult::Type::NUM, "pingtime", "ping time (if available)"},
- {RPCResult::Type::NUM, "minping", "minimum observed ping time (if any at all)"},
- {RPCResult::Type::NUM, "pingwait", "ping wait (if non-zero)"},
+ {RPCResult::Type::NUM, "pingtime", /* optional */ true, "ping time (if available)"},
+ {RPCResult::Type::NUM, "minping", /* optional */ true, "minimum observed ping time (if any at all)"},
+ {RPCResult::Type::NUM, "pingwait", /* optional */ true, "ping wait (if non-zero)"},
{RPCResult::Type::NUM, "version", "The peer version, such as 70001"},
{RPCResult::Type::STR, "subver", "The string version"},
{RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"},
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 00e77d89e5..737abf6ca9 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -94,7 +94,7 @@ static RPCHelpMan getrawtransaction()
RPCResult{"if verbose is set to true",
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "in_active_chain", "Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)"},
+ {RPCResult::Type::BOOL, "in_active_chain", /* optional */ true, "Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)"},
{RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for 'txid'"},
{RPCResult::Type::STR_HEX, "txid", "The transaction id (same as provided)"},
{RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"},
@@ -115,7 +115,7 @@ static RPCHelpMan getrawtransaction()
{RPCResult::Type::STR_HEX, "hex", "hex"},
}},
{RPCResult::Type::NUM, "sequence", "The script sequence number"},
- {RPCResult::Type::ARR, "txinwitness", "",
+ {RPCResult::Type::ARR, "txinwitness", /* optional */ true, "",
{
{RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"},
}},
@@ -141,10 +141,10 @@ static RPCHelpMan getrawtransaction()
}},
}},
}},
- {RPCResult::Type::STR_HEX, "blockhash", "the block hash"},
- {RPCResult::Type::NUM, "confirmations", "The confirmations"},
- {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME},
- {RPCResult::Type::NUM, "time", "Same as \"blocktime\""},
+ {RPCResult::Type::STR_HEX, "blockhash", /* optional */ true, "the block hash"},
+ {RPCResult::Type::NUM, "confirmations", /* optional */ true, "The confirmations"},
+ {RPCResult::Type::NUM_TIME, "blocktime", /* optional */ true, "The block time expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::NUM, "time", /* optional */ true, "Same as \"blocktime\""},
}
},
},
@@ -470,14 +470,15 @@ static RPCHelpMan decoderawtransaction()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
- {RPCResult::Type::NUM, "vout", "The output number"},
- {RPCResult::Type::OBJ, "scriptSig", "The script",
+ {RPCResult::Type::STR_HEX, "coinbase", /* optional */ true, ""},
+ {RPCResult::Type::STR_HEX, "txid", /* optional */ true, "The transaction id"},
+ {RPCResult::Type::NUM, "vout", /* optional */ true, "The output number"},
+ {RPCResult::Type::OBJ, "scriptSig", /* optional */ true, "The script",
{
{RPCResult::Type::STR, "asm", "asm"},
{RPCResult::Type::STR_HEX, "hex", "hex"},
}},
- {RPCResult::Type::ARR, "txinwitness", "",
+ {RPCResult::Type::ARR, "txinwitness", /* optional */ true, "",
{
{RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"},
}},
@@ -559,8 +560,8 @@ static RPCHelpMan decodescript()
{
{RPCResult::Type::STR, "address", "bitcoin address"},
}},
- {RPCResult::Type::STR, "p2sh", "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"},
- {RPCResult::Type::OBJ, "segwit", "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)",
+ {RPCResult::Type::STR, "p2sh", /* optional */ true, "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"},
+ {RPCResult::Type::OBJ, "segwit", /* optional */ true, "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)",
{
{RPCResult::Type::STR, "asm", "String representation of the script public key"},
{RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"},
@@ -772,6 +773,10 @@ static RPCHelpMan signrawtransactionwithkey()
{
{RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
{RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
+ {RPCResult::Type::ARR, "witness", "",
+ {
+ {RPCResult::Type::STR_HEX, "witness", ""},
+ }},
{RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
{RPCResult::Type::NUM, "sequence", "Script sequence number"},
{RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
@@ -909,15 +914,15 @@ static RPCHelpMan testmempoolaccept()
{
{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
- {RPCResult::Type::STR, "package-error", "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
- {RPCResult::Type::BOOL, "allowed", "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate."
+ {RPCResult::Type::STR, "package-error", /* optional */ true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."},
+ {RPCResult::Type::BOOL, "allowed", /* optional */ true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. "
"If not present, the tx was not fully validated due to a failure in another tx in the list."},
- {RPCResult::Type::NUM, "vsize", "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"},
- {RPCResult::Type::OBJ, "fees", "Transaction fees (only present if 'allowed' is true)",
+ {RPCResult::Type::NUM, "vsize", /* optional */ true, "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"},
+ {RPCResult::Type::OBJ, "fees", /* optional */ true, "Transaction fees (only present if 'allowed' is true)",
{
{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT},
}},
- {RPCResult::Type::STR, "reject-reason", "Rejection string (only present when 'allowed' is false)"},
+ {RPCResult::Type::STR, "reject-reason", /* optional */ true, "Rejection string (only present when 'allowed' is false)"},
}},
}
},
@@ -1056,7 +1061,7 @@ static RPCHelpMan decodepsbt()
{RPCResult::Type::STR, "asm", "The asm"},
{RPCResult::Type::STR_HEX, "hex", "The hex"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
- {RPCResult::Type::STR, "address"," Bitcoin address if there is one"},
+ {RPCResult::Type::STR, "address", /*optional=*/true, "Bitcoin address if there is one"},
}},
}},
{RPCResult::Type::OBJ_DYN, "partial_signatures", /* optional */ true, "",
@@ -1078,22 +1083,23 @@ static RPCHelpMan decodepsbt()
}},
{RPCResult::Type::ARR, "bip32_derivs", /* optional */ true, "",
{
- {RPCResult::Type::OBJ, "pubkey", /* optional */ true, "The public key with the derivation path as the value.",
+ {RPCResult::Type::OBJ, "", "",
{
+ {RPCResult::Type::STR, "pubkey", "The public key with the derivation path as the value."},
{RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"},
{RPCResult::Type::STR, "path", "The path"},
}},
}},
- {RPCResult::Type::OBJ, "final_scriptsig", /* optional */ true, "",
+ {RPCResult::Type::OBJ, "final_scriptSig", /* optional */ true, "",
{
{RPCResult::Type::STR, "asm", "The asm"},
{RPCResult::Type::STR, "hex", "The hex"},
}},
- {RPCResult::Type::ARR, "final_scriptwitness", "",
+ {RPCResult::Type::ARR, "final_scriptwitness", /* optional */ true, "",
{
{RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"},
}},
- {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields",
+ {RPCResult::Type::OBJ_DYN, "unknown", /* optional */ true, "The unknown global fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
}},
@@ -1124,7 +1130,7 @@ static RPCHelpMan decodepsbt()
{RPCResult::Type::STR, "path", "The path"},
}},
}},
- {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields",
+ {RPCResult::Type::OBJ_DYN, "unknown", /* optional */ true, "The unknown global fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
}},
@@ -1398,8 +1404,8 @@ static RPCHelpMan finalizepsbt()
RPCResult{
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction if not extracted"},
- {RPCResult::Type::STR_HEX, "hex", "The hex-encoded network transaction if extracted"},
+ {RPCResult::Type::STR, "psbt", /* optional */ true, "The base64-encoded partially signed transaction if not extracted"},
+ {RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The hex-encoded network transaction if extracted"},
{RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
}
},
@@ -1791,7 +1797,7 @@ static RPCHelpMan analyzepsbt()
RPCResult {
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::ARR, "inputs", "",
+ {RPCResult::Type::ARR, "inputs", /* optional */ true, "",
{
{RPCResult::Type::OBJ, "", "",
{
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 95c5a99c1b..45ee778b87 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -23,6 +23,17 @@ void initialize_addrman()
SelectParams(CBaseChainParams::REGTEST);
}
+FUZZ_TARGET_INIT(data_stream_addr_man, initialize_addrman)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
+ CAddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
+ try {
+ ReadFromStream(addr_man, data_stream);
+ } catch (const std::exception&) {
+ }
+}
+
class CAddrManDeterministic : public CAddrMan
{
public:
diff --git a/src/test/fuzz/data_stream.cpp b/src/test/fuzz/data_stream.cpp
deleted file mode 100644
index 323090e041..0000000000
--- a/src/test/fuzz/data_stream.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2020-2021 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <addrdb.h>
-#include <addrman.h>
-#include <net.h>
-#include <test/fuzz/FuzzedDataProvider.h>
-#include <test/fuzz/fuzz.h>
-#include <test/fuzz/util.h>
-#include <test/util/setup_common.h>
-
-#include <cstdint>
-#include <vector>
-
-void initialize_data_stream_addr_man()
-{
- static const auto testing_setup = MakeNoLogFileContext<>();
-}
-
-FUZZ_TARGET_INIT(data_stream_addr_man, initialize_data_stream_addr_man)
-{
- FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
- CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
- CAddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
- try {
- ReadFromStream(addr_man, data_stream);
- } catch (const std::exception&) {
- }
-}
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp
index 6f2bc081c6..8d2a06f11a 100644
--- a/src/test/fuzz/utxo_snapshot.cpp
+++ b/src/test/fuzz/utxo_snapshot.cpp
@@ -4,6 +4,7 @@
#include <chainparams.h>
#include <consensus/validation.h>
+#include <node/utxo_snapshot.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 1924ea55b1..afb3ad0cfd 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -13,6 +13,11 @@
#include <boost/test/unit_test.hpp>
+struct Dersig100Setup : public TestChain100Setup {
+ Dersig100Setup()
+ : TestChain100Setup{{"-testactivationheight=dersig@102"}} {}
+};
+
bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
const CCoinsViewCache& inputs, unsigned int flags, bool cacheSigStore,
bool cacheFullScriptStore, PrecomputedTransactionData& txdata,
@@ -20,7 +25,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
-BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
+BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
{
// Make sure skipping validation of transactions that were
// validated going into the memory pool does not allow
@@ -153,7 +158,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
}
}
-BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
+BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
{
// Test that passing CheckInputScripts with one set of script flags doesn't imply
// that we would pass again with a different set of flags.
diff --git a/src/test/util/chainstate.h b/src/test/util/chainstate.h
new file mode 100644
index 0000000000..81ea4c38f5
--- /dev/null
+++ b/src/test/util/chainstate.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+//
+#ifndef BITCOIN_TEST_UTIL_CHAINSTATE_H
+#define BITCOIN_TEST_UTIL_CHAINSTATE_H
+
+#include <clientversion.h>
+#include <fs.h>
+#include <node/context.h>
+#include <node/utxo_snapshot.h>
+#include <rpc/blockchain.h>
+#include <validation.h>
+
+#include <univalue.h>
+
+#include <boost/test/unit_test.hpp>
+
+const auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
+
+/**
+ * Create and activate a UTXO snapshot, optionally providing a function to
+ * malleate the snapshot.
+ */
+template<typename F = decltype(NoMalleation)>
+static bool
+CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
+{
+ // Write out a snapshot to the test's tempdir.
+ //
+ int height;
+ WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
+ fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
+ FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
+ CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
+
+ UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile);
+ BOOST_TEST_MESSAGE(
+ "Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write());
+
+ // Read the written snapshot in and then activate it.
+ //
+ FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
+ CAutoFile auto_infile{infile, SER_DISK, CLIENT_VERSION};
+ SnapshotMetadata metadata;
+ auto_infile >> metadata;
+
+ malleation(auto_infile, metadata);
+
+ return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory*/ true);
+}
+
+
+#endif // BITCOIN_TEST_UTIL_CHAINSTATE_H
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index cabc4b3b49..97e614379c 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -205,7 +205,8 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
}
}
-TestChain100Setup::TestChain100Setup()
+TestChain100Setup::TestChain100Setup(const std::vector<const char*>& extra_args)
+ : TestingSetup{CBaseChainParams::REGTEST, extra_args}
{
SetMockTime(1598887952);
constexpr std::array<unsigned char, 32> vchKey = {
@@ -234,11 +235,14 @@ void TestChain100Setup::mineBlocks(int num_blocks)
}
}
-CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
+CBlock TestChain100Setup::CreateBlock(
+ const std::vector<CMutableTransaction>& txns,
+ const CScript& scriptPubKey,
+ CChainState& chainstate)
{
const CChainParams& chainparams = Params();
CTxMemPool empty_pool;
- CBlock block = BlockAssembler(m_node.chainman->ActiveChainstate(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
+ CBlock block = BlockAssembler(chainstate, empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
@@ -248,6 +252,20 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
+ return block;
+}
+
+CBlock TestChain100Setup::CreateAndProcessBlock(
+ const std::vector<CMutableTransaction>& txns,
+ const CScript& scriptPubKey,
+ CChainState* chainstate)
+{
+ if (!chainstate) {
+ chainstate = &Assert(m_node.chainman)->ActiveChainstate();
+ }
+
+ const CChainParams& chainparams = Params();
+ const CBlock block = this->CreateBlock(txns, scriptPubKey, *chainstate);
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr);
@@ -304,11 +322,6 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
return mempool_txn;
}
-TestChain100Setup::~TestChain100Setup()
-{
- gArgs.ForceSetArg("-segwitheight", "0");
-}
-
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) const
{
return FromTx(MakeTransactionRef(tx));
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index 5d12dc2323..7518cdb042 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -113,15 +113,26 @@ class CScript;
/**
* Testing fixture that pre-creates a 100-block REGTEST-mode block chain
*/
-struct TestChain100Setup : public RegTestingSetup {
- TestChain100Setup();
+struct TestChain100Setup : public TestingSetup {
+ TestChain100Setup(const std::vector<const char*>& extra_args = {});
/**
* Create a new block with just given transactions, coinbase paying to
* scriptPubKey, and try to add it to the current chain.
+ * If no chainstate is specified, default to the active.
*/
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
- const CScript& scriptPubKey);
+ const CScript& scriptPubKey,
+ CChainState* chainstate = nullptr);
+
+ /**
+ * Create a new block with just given transactions, coinbase paying to
+ * scriptPubKey.
+ */
+ CBlock CreateBlock(
+ const std::vector<CMutableTransaction>& txns,
+ const CScript& scriptPubKey,
+ CChainState& chainstate);
//! Mine a series of new blocks on the active chain.
void mineBlocks(int num_blocks);
@@ -145,8 +156,6 @@ struct TestChain100Setup : public RegTestingSetup {
CAmount output_amount = CAmount(1 * COIN),
bool submit = true);
- ~TestChain100Setup();
-
std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions
CKey coinbaseKey; // private/public key needed to spend coinbase transactions
};
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 315ef22599..726c9ebbb8 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -2,10 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
+#include <chainparams.h>
#include <random.h>
#include <uint256.h>
#include <consensus/validation.h>
#include <sync.h>
+#include <rpc/blockchain.h>
+#include <test/util/chainstate.h>
#include <test/util/setup_common.h>
#include <validation.h>
@@ -73,4 +76,77 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
WITH_LOCK(::cs_main, manager.Unload());
}
+//! Test UpdateTip behavior for both active and background chainstates.
+//!
+//! When run on the background chainstate, UpdateTip should do a subset
+//! of what it does for the active chainstate.
+BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
+{
+ ChainstateManager& chainman = *Assert(m_node.chainman);
+ uint256 curr_tip = ::g_best_block;
+
+ // Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
+ // be found.
+ mineBlocks(10);
+
+ // After adding some blocks to the tip, best block should have changed.
+ BOOST_CHECK(::g_best_block != curr_tip);
+
+ BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(m_node, m_path_root));
+
+ // Ensure our active chain is the snapshot chainstate.
+ BOOST_CHECK(chainman.IsSnapshotActive());
+
+ curr_tip = ::g_best_block;
+
+ // Mine a new block on top of the activated snapshot chainstate.
+ mineBlocks(1); // Defined in TestChain100Setup.
+
+ // After adding some blocks to the snapshot tip, best block should have changed.
+ BOOST_CHECK(::g_best_block != curr_tip);
+
+ curr_tip = ::g_best_block;
+
+ CChainState* background_cs;
+
+ BOOST_CHECK_EQUAL(chainman.GetAll().size(), 2);
+ for (CChainState* cs : chainman.GetAll()) {
+ if (cs != &chainman.ActiveChainstate()) {
+ background_cs = cs;
+ }
+ }
+ BOOST_CHECK(background_cs);
+
+ // Create a block to append to the validation chain.
+ std::vector<CMutableTransaction> noTxns;
+ CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ CBlock validation_block = this->CreateBlock(noTxns, scriptPubKey, *background_cs);
+ auto pblock = std::make_shared<const CBlock>(validation_block);
+ BlockValidationState state;
+ CBlockIndex* pindex = nullptr;
+ const CChainParams& chainparams = Params();
+ bool newblock = false;
+
+ // TODO: much of this is inlined from ProcessNewBlock(); just reuse PNB()
+ // once it is changed to support multiple chainstates.
+ {
+ LOCK(::cs_main);
+ bool checked = CheckBlock(*pblock, state, chainparams.GetConsensus());
+ BOOST_CHECK(checked);
+ bool accepted = background_cs->AcceptBlock(
+ pblock, state, &pindex, true, nullptr, &newblock);
+ BOOST_CHECK(accepted);
+ }
+ // UpdateTip is called here
+ bool block_added = background_cs->ActivateBestChain(state, pblock);
+
+ // Ensure tip is as expected
+ BOOST_CHECK_EQUAL(background_cs->m_chain.Tip()->GetBlockHash(), validation_block.GetHash());
+
+ // g_best_block should be unchanged after adding a block to the background
+ // validation chain.
+ BOOST_CHECK(block_added);
+ BOOST_CHECK_EQUAL(curr_tip, ::g_best_block);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index 0bd378631b..be9e05a65e 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -8,13 +8,13 @@
#include <random.h>
#include <rpc/blockchain.h>
#include <sync.h>
+#include <test/util/chainstate.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <validation.h>
#include <validationinterface.h>
#include <tinyformat.h>
-#include <univalue.h>
#include <vector>
@@ -44,7 +44,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
BOOST_CHECK(!manager.IsSnapshotActive());
BOOST_CHECK(!manager.IsSnapshotValidated());
- BOOST_CHECK(!manager.IsBackgroundIBD(&c1));
auto all = manager.GetAll();
BOOST_CHECK_EQUAL_COLLECTIONS(all.begin(), all.end(), chainstates.begin(), chainstates.end());
@@ -57,9 +56,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
auto exp_tip = c1.m_chain.Tip();
BOOST_CHECK_EQUAL(active_tip, exp_tip);
- auto& validated_cs = manager.ValidatedChainstate();
- BOOST_CHECK_EQUAL(&validated_cs, &c1);
-
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
// Create a snapshot-based chainstate.
@@ -81,8 +77,8 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
BOOST_CHECK(manager.IsSnapshotActive());
BOOST_CHECK(!manager.IsSnapshotValidated());
- BOOST_CHECK(manager.IsBackgroundIBD(&c1));
- BOOST_CHECK(!manager.IsBackgroundIBD(&c2));
+ BOOST_CHECK_EQUAL(&c2, &manager.ActiveChainstate());
+ BOOST_CHECK(&c1 != &manager.ActiveChainstate());
auto all2 = manager.GetAll();
BOOST_CHECK_EQUAL_COLLECTIONS(all2.begin(), all2.end(), chainstates.begin(), chainstates.end());
@@ -99,16 +95,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
// CCoinsViewCache instances.
BOOST_CHECK(exp_tip != exp_tip2);
- auto& validated_cs2 = manager.ValidatedChainstate();
- BOOST_CHECK_EQUAL(&validated_cs2, &c1);
-
- auto& validated_chain = manager.ValidatedChain();
- BOOST_CHECK_EQUAL(&validated_chain, &c1.m_chain);
-
- auto validated_tip = manager.ValidatedTip();
- exp_tip = c1.m_chain.Tip();
- BOOST_CHECK_EQUAL(validated_tip, exp_tip);
-
// Let scheduler events finish running to avoid accessing memory that is going to be unloaded
SyncWithValidationInterfaceQueue();
@@ -168,36 +154,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
}
-auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
-
-template<typename F = decltype(NoMalleation)>
-static bool
-CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
-{
- // Write out a snapshot to the test's tempdir.
- //
- int height;
- WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
- fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
- FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
- CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
-
- UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile);
- BOOST_TEST_MESSAGE(
- "Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write());
-
- // Read the written snapshot in and then activate it.
- //
- FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
- CAutoFile auto_infile{infile, SER_DISK, CLIENT_VERSION};
- SnapshotMetadata metadata;
- auto_infile >> metadata;
-
- malleation(auto_infile, metadata);
-
- return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory*/ true);
-}
-
//! Test basic snapshot activation.
BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
{
@@ -321,27 +277,27 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
{
LOCK(::cs_main);
size_t coins_in_active{0};
- size_t coins_in_ibd{0};
- size_t coins_missing_ibd{0};
+ size_t coins_in_background{0};
+ size_t coins_missing_from_background{0};
for (CChainState* chainstate : chainman.GetAll()) {
BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
CCoinsViewCache& coinscache = chainstate->CoinsTip();
- bool is_ibd = chainman.IsBackgroundIBD(chainstate);
+ bool is_background = chainstate != &chainman.ActiveChainstate();
for (CTransactionRef& txn : m_coinbase_txns) {
COutPoint op{txn->GetHash(), 0};
if (coinscache.HaveCoin(op)) {
- (is_ibd ? coins_in_ibd : coins_in_active)++;
- } else if (is_ibd) {
- coins_missing_ibd++;
+ (is_background ? coins_in_background : coins_in_active)++;
+ } else if (is_background) {
+ coins_missing_from_background++;
}
}
}
BOOST_CHECK_EQUAL(coins_in_active, initial_total_coins + new_coins);
- BOOST_CHECK_EQUAL(coins_in_ibd, initial_total_coins);
- BOOST_CHECK_EQUAL(coins_missing_ibd, new_coins);
+ BOOST_CHECK_EQUAL(coins_in_background, initial_total_coins);
+ BOOST_CHECK_EQUAL(coins_missing_from_background, new_coins);
}
// Snapshot should refuse to load after one has already loaded.
diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp
index 22aafcaa6c..9136c497ea 100644
--- a/src/test/validation_flush_tests.cpp
+++ b/src/test/validation_flush_tests.cpp
@@ -9,7 +9,7 @@
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, BasicTestingSetup)
+BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, ChainTestingSetup)
//! Test utilities for detecting when we need to flush the coins cache based
//! on estimated memory usage.
@@ -20,7 +20,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
{
CTxMemPool mempool;
BlockManager blockman{};
- CChainState chainstate{&mempool, blockman};
+ CChainState chainstate{&mempool, blockman, *Assert(m_node.chainman)};
chainstate.InitCoinsDB(/*cache_size_bytes*/ 1 << 10, /*in_memory*/ true, /*should_wipe*/ false);
WITH_LOCK(::cs_main, chainstate.InitCoinsCache(1 << 10));
diff --git a/src/timedata.cpp b/src/timedata.cpp
index 354092752d..f53fbe231b 100644
--- a/src/timedata.cpp
+++ b/src/timedata.cpp
@@ -11,6 +11,7 @@
#include <netaddress.h>
#include <node/ui_interface.h>
#include <sync.h>
+#include <tinyformat.h>
#include <util/system.h>
#include <util/translation.h>
#include <warnings.h>
@@ -98,11 +99,12 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
}
if (LogAcceptCategory(BCLog::NET)) {
+ std::string log_message{"time data samples: "};
for (const int64_t n : vSorted) {
- LogPrint(BCLog::NET, "%+d ", n); /* Continued */
+ log_message += strprintf("%+d ", n);
}
- LogPrint(BCLog::NET, "| "); /* Continued */
- LogPrint(BCLog::NET, "nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset / 60);
+ log_message += strprintf("| median offset = %+d (%+d minutes)", nTimeOffset, nTimeOffset / 60);
+ LogPrint(BCLog::NET, "%s\n", log_message);
}
}
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 3cf62f3c0e..5a93f30c8a 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -21,23 +21,23 @@
#include <cmath>
#include <optional>
-CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
- int64_t _nTime, unsigned int _entryHeight,
- bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp)
- : tx(_tx), nFee(_nFee), nTxWeight(GetTransactionWeight(*tx)), nUsageSize(RecursiveDynamicUsage(tx)), nTime(_nTime), entryHeight(_entryHeight),
- spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
-{
- nCountWithDescendants = 1;
- nSizeWithDescendants = GetTxSize();
- nModFeesWithDescendants = nFee;
-
- feeDelta = 0;
-
- nCountWithAncestors = 1;
- nSizeWithAncestors = GetTxSize();
- nModFeesWithAncestors = nFee;
- nSigOpCostWithAncestors = sigOpCost;
-}
+CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
+ int64_t time, unsigned int entry_height,
+ bool spends_coinbase, int64_t sigops_cost, LockPoints lp)
+ : tx{tx},
+ nFee{fee},
+ nTxWeight(GetTransactionWeight(*tx)),
+ nUsageSize{RecursiveDynamicUsage(tx)},
+ nTime{time},
+ entryHeight{entry_height},
+ spendsCoinbase{spends_coinbase},
+ sigOpCost{sigops_cost},
+ lockPoints{lp},
+ nSizeWithDescendants{GetTxSize()},
+ nModFeesWithDescendants{nFee},
+ nSizeWithAncestors{GetTxSize()},
+ nModFeesWithAncestors{nFee},
+ nSigOpCostWithAncestors{sigOpCost} {}
void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta)
{
diff --git a/src/txmempool.h b/src/txmempool.h
index d1308aeeed..27ee0628a7 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -37,19 +37,16 @@ extern RecursiveMutex cs_main;
/** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */
static const uint32_t MEMPOOL_HEIGHT = 0x7FFFFFFF;
-struct LockPoints
-{
+struct LockPoints {
// Will be set to the blockchain height and median time past
// values that would be necessary to satisfy all relative locktime
// constraints (BIP68) of this tx given our view of block chain history
- int height;
- int64_t time;
+ int height{0};
+ int64_t time{0};
// As long as the current chain descends from the highest height block
// containing one of the inputs used in the calculation, then the cached
// values are still valid even after a reorg.
- CBlockIndex* maxInputBlock;
-
- LockPoints() : height(0), time(0), maxInputBlock(nullptr) { }
+ CBlockIndex* maxInputBlock{nullptr};
};
struct CompareIteratorByHash {
@@ -98,27 +95,27 @@ private:
const unsigned int entryHeight; //!< Chain height when entering the mempool
const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase
const int64_t sigOpCost; //!< Total sigop cost
- int64_t feeDelta; //!< Used for determining the priority of the transaction for mining in a block
+ int64_t feeDelta{0}; //!< Used for determining the priority of the transaction for mining in a block
LockPoints lockPoints; //!< Track the height and time at which tx was final
// Information about descendants of this transaction that are in the
// mempool; if we remove this transaction we must remove all of these
// descendants as well.
- uint64_t nCountWithDescendants; //!< number of descendant transactions
+ uint64_t nCountWithDescendants{1}; //!< number of descendant transactions
uint64_t nSizeWithDescendants; //!< ... and size
CAmount nModFeesWithDescendants; //!< ... and total fees (all including us)
// Analogous statistics for ancestor transactions
- uint64_t nCountWithAncestors;
+ uint64_t nCountWithAncestors{1};
uint64_t nSizeWithAncestors;
CAmount nModFeesWithAncestors;
int64_t nSigOpCostWithAncestors;
public:
- CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
- int64_t _nTime, unsigned int _entryHeight,
- bool spendsCoinbase,
- int64_t nSigOpsCost, LockPoints lp);
+ CTxMemPoolEntry(const CTransactionRef& tx, CAmount fee,
+ int64_t time, unsigned int entry_height,
+ bool spends_coinbase,
+ int64_t sigops_cost, LockPoints lp);
const CTransaction& GetTx() const { return *this->tx; }
CTransactionRef GetSharedTx() const { return this->tx; }
diff --git a/src/util/rbf.h b/src/util/rbf.h
index 6d44a2cb83..aa522d8bfb 100644
--- a/src/util/rbf.h
+++ b/src/util/rbf.h
@@ -9,7 +9,7 @@
class CTransaction;
-static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
+static constexpr uint32_t MAX_BIP125_RBF_SEQUENCE{0xfffffffd};
/** Check whether the sequence numbers on this transaction are signaling opt-in to replace-by-fee,
* according to BIP 125. Allow opt-out of transaction replacement by setting nSequence >
@@ -17,7 +17,7 @@ static const uint32_t MAX_BIP125_RBF_SEQUENCE = 0xfffffffd;
*
* SEQUENCE_FINAL-1 is picked to still allow use of nLockTime by non-replaceable transactions. All
* inputs rather than just one is for the sake of multi-party protocols, where we don't want a single
-* party to be able to disable replacement. */
-bool SignalsOptInRBF(const CTransaction &tx);
+* party to be able to disable replacement by opting out in their own input. */
+bool SignalsOptInRBF(const CTransaction& tx);
#endif // BITCOIN_UTIL_RBF_H
diff --git a/src/validation.cpp b/src/validation.cpp
index cc87f98913..51cb1d373c 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -24,6 +24,7 @@
#include <node/blockstorage.h>
#include <node/coinstats.h>
#include <node/ui_interface.h>
+#include <node/utxo_snapshot.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <policy/settings.h>
@@ -771,39 +772,43 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// pathological case by making sure setConflicts and setAncestors don't
// intersect.
if (const auto err_string{EntriesAndTxidsDisjoint(setAncestors, setConflicts, hash)}) {
+ // We classify this as a consensus error because a transaction depending on something it
+ // conflicts with would be inconsistent.
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spends-conflicting-tx", *err_string);
}
- // If we don't hold the lock allConflicting might be incomplete; the
- // subsequent RemoveStaged() and addUnchecked() calls don't guarantee
- // mempool consistency for us.
fReplacementTransaction = setConflicts.size();
- if (fReplacementTransaction)
- {
+ if (fReplacementTransaction) {
CFeeRate newFeeRate(nModifiedFees, nSize);
+ // It's possible that the replacement pays more fees than its direct conflicts but not more
+ // than all conflicts (i.e. the direct conflicts have high-fee descendants). However, if the
+ // replacement doesn't pay more fees than its direct conflicts, then we can be sure it's not
+ // more economically rational to mine. Before we go digging through the mempool for all
+ // transactions that would need to be removed (direct conflicts and all descendants), check
+ // that the replacement transaction pays more than its direct conflicts.
if (const auto err_string{PaysMoreThanConflicts(setIterConflicting, newFeeRate, hash)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
- // Calculate all conflicting entries and enforce Rule #5.
+ // Calculate all conflicting entries and enforce BIP125 Rule #5.
if (const auto err_string{GetEntriesForConflicts(tx, m_pool, setIterConflicting, allConflicting)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"too many potential replacements", *err_string);
}
- // Enforce Rule #2.
+ // Enforce BIP125 Rule #2.
if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, setIterConflicting)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
"replacement-adds-unconfirmed", *err_string);
}
- // Check if it's economically rational to mine this transaction rather
- // than the ones it replaces. Enforce Rules #3 and #4.
+ // Check if it's economically rational to mine this transaction rather than the ones it
+ // replaces and pays for its own relay fees. Enforce BIP125 Rules #3 and #4.
for (CTxMemPool::txiter it : allConflicting) {
nConflictingFees += it->GetModifiedFee();
nConflictingSize += it->GetTxSize();
}
- if (const auto err_string{PaysForRBF(nConflictingFees, nModifiedFees, nSize, hash)}) {
+ if (const auto err_string{PaysForRBF(nConflictingFees, nModifiedFees, nSize, ::incrementalRelayFee, hash)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
}
@@ -1090,10 +1095,15 @@ void CoinsViews::InitCache()
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
}
-CChainState::CChainState(CTxMemPool* mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash)
+CChainState::CChainState(
+ CTxMemPool* mempool,
+ BlockManager& blockman,
+ ChainstateManager& chainman,
+ std::optional<uint256> from_snapshot_blockhash)
: m_mempool(mempool),
m_params(::Params()),
m_blockman(blockman),
+ m_chainman(chainman),
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
void CChainState::InitCoinsDB(
@@ -1567,7 +1577,6 @@ static int64_t nTimeForks = 0;
static int64_t nTimeVerify = 0;
static int64_t nTimeConnect = 0;
static int64_t nTimeIndex = 0;
-static int64_t nTimeCallbacks = 0;
static int64_t nTimeTotal = 0;
static int64_t nBlocksTotal = 0;
@@ -1872,9 +1881,6 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal);
- int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5;
- LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO, nTimeCallbacks * MILLI / nBlocksTotal);
-
TRACE7(validation, block_connected,
block.GetHash().ToString().c_str(),
pindex->nHeight,
@@ -2089,8 +2095,42 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
res += warn;
}
+static void UpdateTipLog(
+ const CCoinsViewCache& coins_tip,
+ const CBlockIndex* tip,
+ const CChainParams& params,
+ const std::string& func_name,
+ const std::string& prefix,
+ const std::string& warning_messages) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+{
+
+ AssertLockHeld(::cs_main);
+ LogPrintf("%s%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n",
+ prefix, func_name,
+ tip->GetBlockHash().ToString(), tip->nHeight, tip->nVersion,
+ log(tip->nChainWork.getdouble()) / log(2.0), (unsigned long)tip->nChainTx,
+ FormatISO8601DateTime(tip->GetBlockTime()),
+ GuessVerificationProgress(params.TxData(), tip),
+ coins_tip.DynamicMemoryUsage() * (1.0 / (1 << 20)),
+ coins_tip.GetCacheSize(),
+ !warning_messages.empty() ? strprintf(" warning='%s'", warning_messages) : "");
+}
+
void CChainState::UpdateTip(const CBlockIndex* pindexNew)
{
+ const auto& coins_tip = this->CoinsTip();
+
+ // The remainder of the function isn't relevant if we are not acting on
+ // the active chainstate, so return if need be.
+ if (this != &m_chainman.ActiveChainstate()) {
+ // Only log every so often so that we don't bury log messages at the tip.
+ constexpr int BACKGROUND_LOG_INTERVAL = 2000;
+ if (pindexNew->nHeight % BACKGROUND_LOG_INTERVAL == 0) {
+ UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "[background validation] ", "");
+ }
+ return;
+ }
+
// New best block
if (m_mempool) {
m_mempool->AddTransactionsUpdated(1);
@@ -2118,12 +2158,7 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew)
}
}
}
- LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
- pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
- log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
- FormatISO8601DateTime(pindexNew->GetBlockTime()),
- GuessVerificationProgress(m_params.TxData(), pindexNew), this->CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), this->CoinsTip().GetCacheSize(),
- !warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
+ UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "", warning_messages.original);
}
/** Disconnect m_chain's tip.
@@ -3620,7 +3655,9 @@ bool BlockManager::LoadBlockIndex(
pindex->nStatus |= BLOCK_FAILED_CHILD;
setDirtyBlockIndex.insert(pindex);
}
- if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr)) {
+ if (pindex->IsAssumedValid() ||
+ (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
+ (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
block_index_candidates.insert(pindex);
}
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
@@ -4199,12 +4236,33 @@ void CChainState::CheckBlockIndex()
while (pindex != nullptr) {
nNodes++;
if (pindexFirstInvalid == nullptr && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex;
- if (pindexFirstMissing == nullptr && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex;
+ // Assumed-valid index entries will not have data since we haven't downloaded the
+ // full block yet.
+ if (pindexFirstMissing == nullptr && !(pindex->nStatus & BLOCK_HAVE_DATA) && !pindex->IsAssumedValid()) {
+ pindexFirstMissing = pindex;
+ }
if (pindexFirstNeverProcessed == nullptr && pindex->nTx == 0) pindexFirstNeverProcessed = pindex;
if (pindex->pprev != nullptr && pindexFirstNotTreeValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex;
- if (pindex->pprev != nullptr && pindexFirstNotTransactionsValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex;
- if (pindex->pprev != nullptr && pindexFirstNotChainValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex;
- if (pindex->pprev != nullptr && pindexFirstNotScriptsValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex;
+
+ if (pindex->pprev != nullptr && !pindex->IsAssumedValid()) {
+ // Skip validity flag checks for BLOCK_ASSUMED_VALID index entries, since these
+ // *_VALID_MASK flags will not be present for index entries we are temporarily assuming
+ // valid.
+ if (pindexFirstNotTransactionsValid == nullptr &&
+ (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) {
+ pindexFirstNotTransactionsValid = pindex;
+ }
+
+ if (pindexFirstNotChainValid == nullptr &&
+ (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) {
+ pindexFirstNotChainValid = pindex;
+ }
+
+ if (pindexFirstNotScriptsValid == nullptr &&
+ (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) {
+ pindexFirstNotScriptsValid = pindex;
+ }
+ }
// Begin: actual consistency checks.
if (pindex->pprev == nullptr) {
@@ -4215,7 +4273,9 @@ void CChainState::CheckBlockIndex()
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
- if (!fHavePruned) {
+ // Unless these indexes are assumed valid and pending block download on a
+ // background chainstate.
+ if (!fHavePruned && !pindex->IsAssumedValid()) {
// If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0
assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0));
assert(pindexFirstMissing == pindexFirstNeverProcessed);
@@ -4224,7 +4284,16 @@ void CChainState::CheckBlockIndex()
if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0);
}
if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA);
- assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
+ if (pindex->IsAssumedValid()) {
+ // Assumed-valid blocks should have some nTx value.
+ assert(pindex->nTx > 0);
+ // Assumed-valid blocks should connect to the main chain.
+ assert((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE);
+ } else {
+ // Otherwise there should only be an nTx value if we have
+ // actually seen a block's transactions.
+ assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
+ }
// All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveTxsDownloaded().
assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded());
assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded());
@@ -4241,11 +4310,17 @@ void CChainState::CheckBlockIndex()
}
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && pindexFirstNeverProcessed == nullptr) {
if (pindexFirstInvalid == nullptr) {
+ const bool is_active = this == &m_chainman.ActiveChainstate();
+
// If this block sorts at least as good as the current tip and
// is valid and we have all data for its parents, it must be in
// setBlockIndexCandidates. m_chain.Tip() must also be there
// even if some data has been pruned.
- if (pindexFirstMissing == nullptr || pindex == m_chain.Tip()) {
+ //
+ // Don't perform this check for the background chainstate since
+ // its setBlockIndexCandidates shouldn't have some entries (i.e. those past the
+ // snapshot block) which do exist in the block index for the active chainstate.
+ if (is_active && (pindexFirstMissing == nullptr || pindex == m_chain.Tip())) {
assert(setBlockIndexCandidates.count(pindex));
}
// If some parent is missing, then it could be that this block was in
@@ -4580,7 +4655,7 @@ CChainState& ChainstateManager::InitializeChainstate(
if (to_modify) {
throw std::logic_error("should not be overwriting a chainstate");
}
- to_modify.reset(new CChainState(mempool, m_blockman, snapshot_blockhash));
+ to_modify.reset(new CChainState(mempool, m_blockman, *this, snapshot_blockhash));
// Snapshot chainstates and initial IBD chaintates always become active.
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
@@ -4649,8 +4724,9 @@ bool ChainstateManager::ActivateSnapshot(
static_cast<size_t>(current_coinsdb_cache_size * IBD_CACHE_PERC));
}
- auto snapshot_chainstate = WITH_LOCK(::cs_main, return std::make_unique<CChainState>(
- /* mempool */ nullptr, m_blockman, base_blockhash));
+ auto snapshot_chainstate = WITH_LOCK(::cs_main,
+ return std::make_unique<CChainState>(
+ /* mempool */ nullptr, m_blockman, *this, base_blockhash));
{
LOCK(::cs_main);
@@ -4853,11 +4929,25 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// Fake nChainTx so that GuessVerificationProgress reports accurately
index->nChainTx = index->pprev ? index->pprev->nChainTx + index->nTx : 1;
+ // Mark unvalidated block index entries beneath the snapshot base block as assumed-valid.
+ if (!index->IsValid(BLOCK_VALID_SCRIPTS)) {
+ // This flag will be removed once the block is fully validated by a
+ // background chainstate.
+ index->nStatus |= BLOCK_ASSUMED_VALID;
+ }
+
// Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
// won't ask to rewind the entire assumed-valid chain on startup.
if (index->pprev && DeploymentActiveAt(*index, ::Params().GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
index->nStatus |= BLOCK_OPT_WITNESS;
}
+
+ setDirtyBlockIndex.insert(index);
+ // Changes to the block index will be flushed to disk after this call
+ // returns in `ActivateSnapshot()`, when `MaybeRebalanceCaches()` is
+ // called, since we've added a snapshot chainstate and therefore will
+ // have to downsize the IBD chainstate, which will result in a call to
+ // `FlushStateToDisk(ALWAYS)`.
}
assert(index);
@@ -4882,22 +4972,6 @@ bool ChainstateManager::IsSnapshotActive() const
return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get();
}
-CChainState& ChainstateManager::ValidatedChainstate() const
-{
- LOCK(::cs_main);
- if (m_snapshot_chainstate && IsSnapshotValidated()) {
- return *m_snapshot_chainstate.get();
- }
- assert(m_ibd_chainstate);
- return *m_ibd_chainstate.get();
-}
-
-bool ChainstateManager::IsBackgroundIBD(CChainState* chainstate) const
-{
- LOCK(::cs_main);
- return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get());
-}
-
void ChainstateManager::Unload()
{
for (CChainState* chainstate : this->GetAll()) {
diff --git a/src/validation.h b/src/validation.h
index 078b988052..b2282828ce 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -14,16 +14,10 @@
#include <arith_uint256.h>
#include <attributes.h>
#include <chain.h>
-#include <coins.h>
-#include <consensus/validation.h>
-#include <crypto/common.h> // for ReadLE64
#include <fs.h>
-#include <node/utxo_snapshot.h>
#include <policy/feerate.h>
#include <policy/packages.h>
-#include <protocol.h> // For CMessageHeader::MessageStartChars
#include <script/script_error.h>
-#include <serialize.h>
#include <sync.h>
#include <txdb.h>
#include <txmempool.h> // For CTxMemPool::cs
@@ -44,18 +38,13 @@
#include <vector>
class CChainState;
-class BlockValidationState;
class CBlockTreeDB;
-class CBlockUndo;
class CChainParams;
struct CCheckpointData;
-class CInv;
-class CConnman;
-class CScriptCheck;
class CTxMemPool;
class ChainstateManager;
+class SnapshotMetadata;
struct ChainTxData;
-
struct DisconnectedBlockTransactions;
struct PrecomputedTransactionData;
struct LockPoints;
@@ -111,6 +100,7 @@ extern RecursiveMutex cs_main;
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
extern Mutex g_best_block_mutex;
extern std::condition_variable g_best_block_cv;
+/** Used to notify getblocktemplate RPC of new tips. */
extern uint256 g_best_block;
/** Whether there are dedicated script-checking threads running.
* False indicates all script checking is done on the main threadMessageHandler thread.
@@ -595,9 +585,15 @@ public:
//! CChainState instances.
BlockManager& m_blockman;
+ //! The chainstate manager that owns this chainstate. The reference is
+ //! necessary so that this instance can check whether it is the active
+ //! chainstate within deeply nested method calls.
+ ChainstateManager& m_chainman;
+
explicit CChainState(
CTxMemPool* mempool,
BlockManager& blockman,
+ ChainstateManager& chainman,
std::optional<uint256> from_snapshot_blockhash = std::nullopt);
/**
@@ -634,9 +630,10 @@ public:
const std::optional<uint256> m_from_snapshot_blockhash;
/**
- * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
- * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
- * missing the data for the block.
+ * The set of all CBlockIndex entries with either BLOCK_VALID_TRANSACTIONS (for
+ * itself and all ancestors) *or* BLOCK_ASSUMED_VALID (if using background
+ * chainstates) and as good as our current tip or better. Entries may be failed,
+ * though, and pruning nodes may be missing the data for the block.
*/
std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
@@ -848,12 +845,6 @@ private:
* *Background IBD chainstate*: an IBD chainstate for which the
* IBD process is happening in the background while use of the
* active (snapshot) chainstate allows the rest of the system to function.
- *
- * *Validated chainstate*: the most-work chainstate which has been validated
- * locally via initial block download. This will be the snapshot chainstate
- * if a snapshot was loaded and all blocks up to the snapshot starting point
- * have been downloaded and validated (via background validation), otherwise
- * it will be the IBD chainstate.
*/
class ChainstateManager
{
@@ -972,19 +963,6 @@ public:
//! Is there a snapshot in use and has it been fully validated?
bool IsSnapshotValidated() const { return m_snapshot_validated; }
- //! @returns true if this chainstate is being used to validate an active
- //! snapshot in the background.
- bool IsBackgroundIBD(CChainState* chainstate) const;
-
- //! Return the most-work chainstate that has been fully validated.
- //!
- //! During background validation of a snapshot, this is the IBD chain. After
- //! background validation has completed, this is the snapshot chain.
- CChainState& ValidatedChainstate() const;
-
- CChain& ValidatedChain() const { return ValidatedChainstate().m_chain; }
- CBlockIndex* ValidatedTip() const { return ValidatedChain().Tip(); }
-
/**
* Process an incoming block. This only returns after the best known valid
* block is made active. Note that it does not, however, guarantee that the
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 7d194ae262..77757e79a3 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -472,7 +472,7 @@ static RPCHelpMan sendtoaddress()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
- {RPCResult::Type::STR, "fee reason", "The transaction fee reason."}
+ {RPCResult::Type::STR, "fee_reason", "The transaction fee reason."}
},
},
},
@@ -899,7 +899,7 @@ static RPCHelpMan sendmany()
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
"the number of addresses."},
- {RPCResult::Type::STR, "fee reason", "The transaction fee reason."}
+ {RPCResult::Type::STR, "fee_reason", "The transaction fee reason."}
},
},
},
@@ -1213,7 +1213,7 @@ static RPCHelpMan listreceivedbyaddress()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
+ {RPCResult::Type::BOOL, "involvesWatchonly", /* optional */ true, "Only returns true if imported addresses were involved in transaction"},
{RPCResult::Type::STR, "address", "The receiving address"},
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
@@ -1261,7 +1261,7 @@ static RPCHelpMan listreceivedbylabel()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
+ {RPCResult::Type::BOOL, "involvesWatchonly", /* optional */ true, "Only returns true if imported addresses were involved in transaction"},
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount received by addresses with this label"},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
{RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
@@ -1388,20 +1388,24 @@ static const std::vector<RPCResult> TransactionDescriptionString()
{
return{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n"
"transaction conflicted that many blocks ago."},
- {RPCResult::Type::BOOL, "generated", "Only present if transaction only input is a coinbase one."},
- {RPCResult::Type::BOOL, "trusted", "Only present if we consider transaction to be trusted and so safe to spend from."},
- {RPCResult::Type::STR_HEX, "blockhash", "The block hash containing the transaction."},
- {RPCResult::Type::NUM, "blockheight", "The block height containing the transaction."},
- {RPCResult::Type::NUM, "blockindex", "The index of the transaction in the block that includes it."},
- {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME + "."},
+ {RPCResult::Type::BOOL, "generated", /* optional */ true, "Only present if transaction only input is a coinbase one."},
+ {RPCResult::Type::BOOL, "trusted", /* optional */ true, "Only present if we consider transaction to be trusted and so safe to spend from."},
+ {RPCResult::Type::STR_HEX, "blockhash", /* optional */ true, "The block hash containing the transaction."},
+ {RPCResult::Type::NUM, "blockheight", /* optional */ true, "The block height containing the transaction."},
+ {RPCResult::Type::NUM, "blockindex", /* optional */ true, "The index of the transaction in the block that includes it."},
+ {RPCResult::Type::NUM_TIME, "blocktime", /* optional */ true, "The block time expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
{RPCResult::Type::ARR, "walletconflicts", "Conflicting transaction ids.",
{
{RPCResult::Type::STR_HEX, "txid", "The transaction id."},
}},
+ {RPCResult::Type::STR_HEX, "replaced_by_txid", /* optional */ true, "The txid if this tx was replaced."},
+ {RPCResult::Type::STR_HEX, "replaces_txid", /* optional */ true, "The txid if the tx replaces one."},
+ {RPCResult::Type::STR, "comment", /* optional */ true, ""},
+ {RPCResult::Type::STR, "to", /* optional */ true, "If a comment to is associated with the transaction."},
{RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
- {RPCResult::Type::STR, "comment", "If a comment is associated with the transaction, only present if not empty."},
+ {RPCResult::Type::STR, "comment", /* optional */ true, "If a comment is associated with the transaction, only present if not empty."},
{RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
"may be unknown for unconfirmed transactions not in the mempool"}};
}
@@ -1423,7 +1427,7 @@ static RPCHelpMan listtransactions()
{
{RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
{
- {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::BOOL, "involvesWatchonly", /* optional */ true, "Only returns true if imported addresses were involved in transaction."},
{RPCResult::Type::STR, "address", "The bitcoin address of the transaction."},
{RPCResult::Type::STR, "category", "The transaction category.\n"
"\"send\" Transactions sent.\n"
@@ -1433,14 +1437,14 @@ static RPCHelpMan listtransactions()
"\"orphan\" Orphaned coinbase transactions received."},
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
"for all other categories"},
- {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
+ {RPCResult::Type::STR, "label", /* optional */ true, "A comment for the address/transaction, if any"},
{RPCResult::Type::NUM, "vout", "the vout value"},
- {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
"'send' category of transactions."},
},
TransactionDescriptionString()),
{
- {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ {RPCResult::Type::BOOL, "abandoned", /* optional */ true, "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
"'send' category of transactions."},
})},
}
@@ -1537,7 +1541,7 @@ static RPCHelpMan listsinceblock()
{
{RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
{
- {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::BOOL, "involvesWatchonly", /* optional */ true, "Only returns true if imported addresses were involved in transaction."},
{RPCResult::Type::STR, "address", "The bitcoin address of the transaction."},
{RPCResult::Type::STR, "category", "The transaction category.\n"
"\"send\" Transactions sent.\n"
@@ -1548,18 +1552,17 @@ static RPCHelpMan listsinceblock()
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
"for all other categories"},
{RPCResult::Type::NUM, "vout", "the vout value"},
- {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
"'send' category of transactions."},
},
TransactionDescriptionString()),
{
- {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ {RPCResult::Type::BOOL, "abandoned", /* optional */ true, "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
"'send' category of transactions."},
- {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
- {RPCResult::Type::STR, "to", "If a comment to is associated with the transaction."},
+ {RPCResult::Type::STR, "label", /* optional */ true, "A comment for the address/transaction, if any"},
})},
}},
- {RPCResult::Type::ARR, "removed", "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
+ {RPCResult::Type::ARR, "removed", /* optional */ true, "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
"Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count."
, {{RPCResult::Type::ELISION, "", ""},}},
{RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain, or the genesis hash if the referenced block does not exist yet. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"},
@@ -1672,7 +1675,7 @@ static RPCHelpMan gettransaction()
RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
{
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
- {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
"'send' category of transactions."},
},
TransactionDescriptionString()),
@@ -1681,8 +1684,8 @@ static RPCHelpMan gettransaction()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
- {RPCResult::Type::STR, "address", "The bitcoin address involved in the transaction."},
+ {RPCResult::Type::BOOL, "involvesWatchonly", /* optional */ true, "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::STR, "address", /* optional */ true, "The bitcoin address involved in the transaction."},
{RPCResult::Type::STR, "category", "The transaction category.\n"
"\"send\" Transactions sent.\n"
"\"receive\" Non-coinbase transactions received.\n"
@@ -1690,16 +1693,16 @@ static RPCHelpMan gettransaction()
"\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
"\"orphan\" Orphaned coinbase transactions received."},
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
- {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
+ {RPCResult::Type::STR, "label", /* optional */ true, "A comment for the address/transaction, if any"},
{RPCResult::Type::NUM, "vout", "the vout value"},
- {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
+ {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
"'send' category of transactions."},
- {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ {RPCResult::Type::BOOL, "abandoned", /* optional */ true, "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
"'send' category of transactions."},
}},
}},
{RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"},
- {RPCResult::Type::OBJ, "decoded", "Optional, the decoded transaction (only present when `verbose` is passed)",
+ {RPCResult::Type::OBJ, "decoded", /* optional */ true, "The decoded transaction (only present when `verbose` is passed)",
{
{RPCResult::Type::ELISION, "", "Equivalent to the RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed."},
}},
@@ -2362,9 +2365,9 @@ static RPCHelpMan getbalances()
{RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
{RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
{RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
- {RPCResult::Type::STR_AMOUNT, "used", "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
+ {RPCResult::Type::STR_AMOUNT, "used", /* optional */ true, "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
}},
- {RPCResult::Type::OBJ, "watchonly", "watchonly balances (not present if wallet does not watch anything)",
+ {RPCResult::Type::OBJ, "watchonly", /* optional */ true, "watchonly balances (not present if wallet does not watch anything)",
{
{RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
{RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
@@ -2431,9 +2434,9 @@ static RPCHelpMan getwalletinfo()
{RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
{RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
{RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
- {RPCResult::Type::NUM_TIME, "keypoololdest", "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool. Legacy wallets only."},
+ {RPCResult::Type::NUM_TIME, "keypoololdest", /* optional */ true, "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool. Legacy wallets only."},
{RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
- {RPCResult::Type::NUM, "keypoolsize_hd_internal", "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
+ {RPCResult::Type::NUM, "keypoolsize_hd_internal", /* optional */ true, "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
{RPCResult::Type::NUM_TIME, "unlocked_until", /* optional */ true, "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked (only present for passphrase-encrypted wallets)"},
{RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kvB"},
{RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "the Hash160 of the HD seed (only present when HD is enabled)"},
@@ -2955,20 +2958,20 @@ static RPCHelpMan listunspent()
{
{RPCResult::Type::STR_HEX, "txid", "the transaction id"},
{RPCResult::Type::NUM, "vout", "the vout value"},
- {RPCResult::Type::STR, "address", "the bitcoin address"},
- {RPCResult::Type::STR, "label", "The associated label, or \"\" for the default label"},
+ {RPCResult::Type::STR, "address", /* optional */ true, "the bitcoin address"},
+ {RPCResult::Type::STR, "label", /* optional */ true, "The associated label, or \"\" for the default label"},
{RPCResult::Type::STR, "scriptPubKey", "the script key"},
{RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
{RPCResult::Type::NUM, "ancestorcount", /* optional */ true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
{RPCResult::Type::NUM, "ancestorsize", /* optional */ true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
{RPCResult::Type::STR_AMOUNT, "ancestorfees", /* optional */ true, "The total fees of in-mempool ancestors (including this one) with fee deltas used for mining priority in " + CURRENCY_ATOM + " (if transaction is in the mempool)"},
- {RPCResult::Type::STR_HEX, "redeemScript", "The redeemScript if scriptPubKey is P2SH"},
- {RPCResult::Type::STR, "witnessScript", "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
+ {RPCResult::Type::STR_HEX, "redeemScript", /* optional */ true, "The redeemScript if scriptPubKey is P2SH"},
+ {RPCResult::Type::STR, "witnessScript", /* optional */ true, "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
{RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
{RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
- {RPCResult::Type::BOOL, "reused", "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
- {RPCResult::Type::STR, "desc", "(only when solvable) A descriptor for spending this output"},
+ {RPCResult::Type::BOOL, "reused", /* optional */ true, "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
+ {RPCResult::Type::STR, "desc", /* optional */ true, "(only when solvable) A descriptor for spending this output"},
{RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions\n"
"from outside keys and unconfirmed replacement transactions are considered unsafe\n"
"and are not eligible for spending by fundrawtransaction and sendtoaddress."},
@@ -3442,6 +3445,10 @@ RPCHelpMan signrawtransactionwithwallet()
{
{RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
{RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
+ {RPCResult::Type::ARR, "witness", "",
+ {
+ {RPCResult::Type::STR_HEX, "witness", ""},
+ }},
{RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
{RPCResult::Type::NUM, "sequence", "Script sequence number"},
{RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
@@ -4188,9 +4195,9 @@ static RPCHelpMan send()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
- {RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of the number of addresses."},
- {RPCResult::Type::STR_HEX, "hex", "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
- {RPCResult::Type::STR, "psbt", "If more signatures are needed, or if add_to_wallet is false, the base64-encoded (partially) signed transaction"}
+ {RPCResult::Type::STR_HEX, "txid", /* optional */ true, "The transaction id for the send. Only 1 transaction is created regardless of the number of addresses."},
+ {RPCResult::Type::STR_HEX, "hex", /* optional */ true, "If add_to_wallet is false, the hex-encoded raw transaction with signature(s)"},
+ {RPCResult::Type::STR, "psbt", /* optional */ true, "If more signatures are needed, or if add_to_wallet is false, the base64-encoded (partially) signed transaction"}
}
},
RPCExamples{""
diff --git a/test/functional/feature_addrman.py b/test/functional/feature_addrman.py
index 42afd74ac9..55d3e48c64 100755
--- a/test/functional/feature_addrman.py
+++ b/test/functional/feature_addrman.py
@@ -19,6 +19,7 @@ def serialize_addrman(
format=1,
lowest_compatible=3,
net_magic="regtest",
+ bucket_key=1,
len_new=None,
len_tried=None,
mock_checksum=None,
@@ -29,7 +30,7 @@ def serialize_addrman(
r = MAGIC_BYTES[net_magic]
r += struct.pack("B", format)
r += struct.pack("B", INCOMPATIBILITY_BASE + lowest_compatible)
- r += ser_uint256(1)
+ r += ser_uint256(bucket_key)
r += struct.pack("i", len_new or len(new))
r += struct.pack("i", len_tried or len(tried))
ADDRMAN_NEW_BUCKET_COUNT = 1 << 10
@@ -119,6 +120,14 @@ class AddrmanTest(BitcoinTestFramework):
match=ErrorMatch.FULL_REGEX,
)
+ self.log.info("Check that corrupt addrman cannot be read (failed check)")
+ self.stop_node(0)
+ write_addrman(peers_dat, bucket_key=0)
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg=init_error("Corrupt data. Consistency check failed with code -16: .*"),
+ match=ErrorMatch.FULL_REGEX,
+ )
+
self.log.info("Check that missing addrman is recreated")
self.stop_node(0)
os.remove(peers_dat)
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py
index 09cda8444a..ee2c71cd42 100755
--- a/test/functional/feature_bip68_sequence.py
+++ b/test/functional/feature_bip68_sequence.py
@@ -41,8 +41,14 @@ class BIP68Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.extra_args = [
- ["-acceptnonstdtxn=1"],
- ["-acceptnonstdtxn=0"],
+ [
+ '-testactivationheight=csv@432',
+ "-acceptnonstdtxn=1",
+ ],
+ [
+ '-testactivationheight=csv@432',
+ "-acceptnonstdtxn=0",
+ ],
]
def skip_test_if_missing_module(self):
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 777787ed32..b06ea8542b 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -82,7 +82,10 @@ class FullBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [['-acceptnonstdtxn=1']] # This is a consensus block test, we don't care about tx policy
+ self.extra_args = [[
+ '-acceptnonstdtxn=1', # This is a consensus block test, we don't care about tx policy
+ '-testactivationheight=bip34@2',
+ ]]
def run_test(self):
node = self.nodes[0] # convenience reference to the node
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index 2c3ef9b88b..3dc858f5d2 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -8,7 +8,6 @@ Test that the CHECKLOCKTIMEVERIFY soft-fork activates.
"""
from test_framework.blocktools import (
- CLTV_HEIGHT,
create_block,
create_coinbase,
)
@@ -76,10 +75,14 @@ def cltv_validate(tx, height):
cltv_modify_tx(tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
+CLTV_HEIGHT = 111
+
+
class BIP65Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [[
+ f'-testactivationheight=cltv@{CLTV_HEIGHT}',
'-whitelist=noban@127.0.0.1',
'-par=1', # Use only one script thread to get the exact reject reason for testing
'-acceptnonstdtxn=1', # cltv_invalidate is nonstandard
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index d2b3fe45d1..5255b13bd1 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -41,7 +41,6 @@ from itertools import product
import time
from test_framework.blocktools import (
- CSV_ACTIVATION_HEIGHT,
create_block,
create_coinbase,
)
@@ -89,12 +88,16 @@ def all_rlt_txs(txs):
return [tx['tx'] for tx in txs]
+CSV_ACTIVATION_HEIGHT = 432
+
+
class BIP68_112_113Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
self.extra_args = [[
'-whitelist=noban@127.0.0.1',
+ f'-testactivationheight=csv@{CSV_ACTIVATION_HEIGHT}',
'-par=1', # Use only one script thread to get the exact reject reason for testing
]]
self.supports_cli = False
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index 595d26611a..28aff1f2f9 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -8,7 +8,6 @@ Test the DERSIG soft-fork activation on regtest.
"""
from test_framework.blocktools import (
- DERSIG_HEIGHT,
create_block,
create_coinbase,
)
@@ -42,10 +41,14 @@ def unDERify(tx):
tx.vin[0].scriptSig = CScript(newscript)
+DERSIG_HEIGHT = 102
+
+
class BIP66Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [[
+ f'-testactivationheight=dersig@{DERSIG_HEIGHT}',
'-whitelist=noban@127.0.0.1',
'-par=1', # Use only one script thread to get the exact log msg for testing
]]
@@ -83,7 +86,6 @@ class BIP66Test(BitcoinTestFramework):
tip = self.nodes[0].getbestblockhash()
block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
block = create_block(int(tip, 16), create_coinbase(DERSIG_HEIGHT - 1), block_time)
- block.nVersion = 2
block.vtx.append(spendtx)
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
@@ -110,7 +112,7 @@ class BIP66Test(BitcoinTestFramework):
peer.sync_with_ping()
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
- block.nVersion = 3
+ block.nVersion = 4
spendtx = self.create_tx(self.coinbase_txids[1])
unDERify(spendtx)
@@ -139,7 +141,7 @@ class BIP66Test(BitcoinTestFramework):
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
peer.sync_with_ping()
- self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
+ self.log.info("Test that a block with a DERSIG-compliant transaction is accepted")
block.vtx[1] = self.create_tx(self.coinbase_txids[1])
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index 96984e1e64..217a38050d 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -52,7 +52,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
# This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
# normal segwit activation here (and don't use the default always-on behaviour).
self.extra_args = [[
- f'-segwitheight={COINBASE_MATURITY + 5}',
+ f'-testactivationheight=segwit@{COINBASE_MATURITY + 5}',
'-addresstype=legacy',
'-par=1', # Use only one script thread to get the exact reject reason for testing
]]
diff --git a/test/functional/feature_presegwit_node_upgrade.py b/test/functional/feature_presegwit_node_upgrade.py
index fd6b8620d4..aac42d4dbf 100755
--- a/test/functional/feature_presegwit_node_upgrade.py
+++ b/test/functional/feature_presegwit_node_upgrade.py
@@ -16,7 +16,7 @@ class SegwitUpgradeTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
- self.extra_args = [["-segwitheight=10"]]
+ self.extra_args = [["-testactivationheight=segwit@10"]]
def run_test(self):
"""A pre-segwit node with insufficiently validated blocks needs to redownload blocks"""
@@ -37,14 +37,14 @@ class SegwitUpgradeTest(BitcoinTestFramework):
# Restarting the node (with segwit activation height set to 5) should result in a shutdown
# because the blockchain consists of 3 insufficiently validated blocks per segwit consensus rules.
node.assert_start_raises_init_error(
- extra_args=["-segwitheight=5"],
+ extra_args=["-testactivationheight=segwit@5"],
expected_msg=": Witness data for blocks after height 5 requires "
f"validation. Please restart with -reindex..{os.linesep}"
"Please restart with -reindex or -reindex-chainstate to recover.",
)
# As directed, the user restarts the node with -reindex
- self.start_node(0, extra_args=["-reindex", "-segwitheight=5"])
+ self.start_node(0, extra_args=["-reindex", "-testactivationheight=segwit@5"])
# With the segwit consensus rules, the node is able to validate only up to block 4
assert_equal(node.getblockcount(), 4)
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index b941061963..a6e9cd2ed1 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -7,7 +7,6 @@
from copy import deepcopy
from decimal import Decimal
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
BIP125_SEQUENCE_NUMBER,
COIN,
@@ -18,10 +17,18 @@ from test_framework.messages import (
)
from test_framework.script import CScript, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
-from test_framework.script_util import DUMMY_P2WPKH_SCRIPT, DUMMY_2_P2WPKH_SCRIPT
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_raises_rpc_error,
+)
+from test_framework.script_util import (
+ DUMMY_P2WPKH_SCRIPT,
+ DUMMY_2_P2WPKH_SCRIPT,
+)
from test_framework.wallet import MiniWallet
+
MAX_REPLACEMENT_LIMIT = 100
class ReplaceByFeeTest(BitcoinTestFramework):
def set_test_params(self):
@@ -89,29 +96,23 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def make_utxo(self, node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
"""Create a txout with a given amount and scriptPubKey
- Mines coins as needed.
+ Assumes that MiniWallet has enough funds to cover the amount and the fixed fee
+ (from it's internal utxos, the one with the largest value is taken).
confirmed - txouts created will be confirmed in the blockchain;
unconfirmed otherwise.
"""
- fee = 1 * COIN
- while node.getbalance() < satoshi_round((amount + fee) / COIN):
- self.generate(node, COINBASE_MATURITY)
-
- new_addr = node.getnewaddress()
- txid = node.sendtoaddress(new_addr, satoshi_round((amount + fee) / COIN))
- tx1 = node.getrawtransaction(txid, 1)
- txid = int(txid, 16)
- i, _ = next(filter(lambda vout: new_addr == vout[1]['scriptPubKey']['address'], enumerate(tx1['vout'])))
-
- tx2 = CTransaction()
- tx2.vin = [CTxIn(COutPoint(txid, i))]
- tx2.vout = [CTxOut(amount, scriptPubKey)]
- tx2.rehash()
-
- signed_tx = node.signrawtransactionwithwallet(tx2.serialize().hex())
-
- txid = node.sendrawtransaction(signed_tx['hex'], 0)
+ # MiniWallet only supports sweeping utxos to its own internal scriptPubKey, so in
+ # order to create an output with arbitrary amount/scriptPubKey, we have to add it
+ # manually after calling the create_self_transfer method. The MiniWallet output's
+ # nValue has to be adapted accordingly (amount and fee deduction). To keep things
+ # simple, we use a fixed fee of 1000 Satoshis here.
+ fee = 1000
+ tx = self.wallet.create_self_transfer(from_node=node, fee_rate=0, mempool_valid=False)['tx']
+ assert_greater_than(tx.vout[0].nValue, amount + fee)
+ tx.vout[0].nValue -= (amount + fee) # change output -> MiniWallet
+ tx.vout.append(CTxOut(amount, scriptPubKey)) # desired output -> to be returned
+ txid = self.wallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex())
# If requested, ensure txouts are confirmed.
if confirmed:
@@ -124,7 +125,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert new_size < mempool_size
mempool_size = new_size
- return COutPoint(int(txid, 16), 0)
+ return COutPoint(int(txid, 16), 1)
def test_simple_doublespend(self):
"""Simple doublespend"""
@@ -161,14 +162,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_doublespend_chain(self):
"""Doublespend of a long chain"""
- initial_nValue = 50 * COIN
+ initial_nValue = 5 * COIN
tx0_outpoint = self.make_utxo(self.nodes[0], initial_nValue)
prevout = tx0_outpoint
remaining_value = initial_nValue
chain_txids = []
- while remaining_value > 10 * COIN:
- remaining_value -= 1 * COIN
+ while remaining_value > 1 * COIN:
+ remaining_value -= int(0.1 * COIN)
tx = CTransaction()
tx.vin = [CTxIn(prevout, nSequence=0)]
tx.vout = [CTxOut(remaining_value, CScript([1, OP_DROP] * 15 + [1]))]
@@ -178,10 +179,10 @@ class ReplaceByFeeTest(BitcoinTestFramework):
prevout = COutPoint(int(txid, 16), 0)
# Whether the double-spend is allowed is evaluated by including all
- # child fees - 40 BTC - so this attempt is rejected.
+ # child fees - 4 BTC - so this attempt is rejected.
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(initial_nValue - 30 * COIN, DUMMY_P2WPKH_SCRIPT)]
+ dbl_tx.vout = [CTxOut(initial_nValue - 3 * COIN, DUMMY_P2WPKH_SCRIPT)]
dbl_tx_hex = dbl_tx.serialize().hex()
# This will raise an exception due to insufficient fee
@@ -190,7 +191,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Accepted with sufficient fee
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
+ dbl_tx.vout = [CTxOut(int(0.1 * COIN), DUMMY_P2WPKH_SCRIPT)]
dbl_tx_hex = dbl_tx.serialize().hex()
self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
@@ -201,10 +202,10 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_doublespend_tree(self):
"""Doublespend of a big tree of transactions"""
- initial_nValue = 50 * COIN
+ initial_nValue = 5 * COIN
tx0_outpoint = self.make_utxo(self.nodes[0], initial_nValue)
- def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _total_txs=None):
+ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.00001 * COIN, _total_txs=None):
if _total_txs is None:
_total_txs = [0]
if _total_txs[0] >= max_txs:
@@ -235,7 +236,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
_total_txs=_total_txs):
yield x
- fee = int(0.0001 * COIN)
+ fee = int(0.00001 * COIN)
n = MAX_REPLACEMENT_LIMIT
tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
assert_equal(len(tree_txs), n)
@@ -248,10 +249,10 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# This will raise an exception due to insufficient fee
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
- # 1 BTC fee is enough
+ # 0.1 BTC fee is enough
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(initial_nValue - fee * n - 1 * COIN, DUMMY_P2WPKH_SCRIPT)]
+ dbl_tx.vout = [CTxOut(initial_nValue - fee * n - int(0.1 * COIN), DUMMY_P2WPKH_SCRIPT)]
dbl_tx_hex = dbl_tx.serialize().hex()
self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
@@ -264,7 +265,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Try again, but with more total transactions than the "max txs
# double-spent at once" anti-DoS limit.
for n in (MAX_REPLACEMENT_LIMIT + 1, MAX_REPLACEMENT_LIMIT * 2):
- fee = int(0.0001 * COIN)
+ fee = int(0.00001 * COIN)
tx0_outpoint = self.make_utxo(self.nodes[0], initial_nValue)
tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
assert_equal(len(tree_txs), n)
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 2b79b3284c..79254546f1 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -78,18 +78,18 @@ class SegWitTest(BitcoinTestFramework):
[
"-acceptnonstdtxn=1",
"-rpcserialversion=0",
- "-segwitheight=432",
+ "-testactivationheight=segwit@432",
"-addresstype=legacy",
],
[
"-acceptnonstdtxn=1",
"-rpcserialversion=1",
- "-segwitheight=432",
+ "-testactivationheight=segwit@432",
"-addresstype=legacy",
],
[
"-acceptnonstdtxn=1",
- "-segwitheight=432",
+ "-testactivationheight=segwit@432",
"-addresstype=legacy",
],
]
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index 56f7cbe6a5..015876cbbf 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -46,6 +46,7 @@ from test_framework.util import (
assert_greater_than_or_equal,
assert_raises_rpc_error,
)
+from test_framework.wallet import MiniWallet
class MempoolPersistTest(BitcoinTestFramework):
@@ -53,15 +54,26 @@ class MempoolPersistTest(BitcoinTestFramework):
self.num_nodes = 3
self.extra_args = [[], ["-persistmempool=0"], []]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
+ self.mini_wallet = MiniWallet(self.nodes[2])
+ self.mini_wallet.rescan_utxos()
+ if self.is_sqlite_compiled():
+ self.nodes[2].createwallet(
+ wallet_name="watch",
+ descriptors=True,
+ disable_private_keys=True,
+ load_on_startup=False,
+ )
+ wallet_watch = self.nodes[2].get_wallet_rpc("watch")
+ assert_equal([{'success': True}], wallet_watch.importdescriptors([{'desc': self.mini_wallet.get_descriptor(), 'timestamp': 0}]))
+
self.log.debug("Send 5 transactions from node2 (to its own address)")
tx_creation_time_lower = int(time.time())
for _ in range(5):
- last_txid = self.nodes[2].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("10"))
- node2_balance = self.nodes[2].getbalance()
+ last_txid = self.mini_wallet.send_self_transfer(from_node=self.nodes[2])["txid"]
+ if self.is_sqlite_compiled():
+ self.nodes[2].syncwithvalidationinterfacequeue() # Flush mempool to wallet
+ node2_balance = wallet_watch.getbalance()
self.sync_all()
tx_creation_time_higher = int(time.time())
@@ -82,16 +94,16 @@ class MempoolPersistTest(BitcoinTestFramework):
assert_equal(total_fee_old, self.nodes[0].getmempoolinfo()['total_fee'])
assert_equal(total_fee_old, sum(v['fees']['base'] for k, v in self.nodes[0].getrawmempool(verbose=True).items()))
- tx_creation_time = self.nodes[0].getmempoolentry(txid=last_txid)['time']
+ last_entry = self.nodes[0].getmempoolentry(txid=last_txid)
+ tx_creation_time = last_entry['time']
assert_greater_than_or_equal(tx_creation_time, tx_creation_time_lower)
assert_greater_than_or_equal(tx_creation_time_higher, tx_creation_time)
# disconnect nodes & make a txn that remains in the unbroadcast set.
self.disconnect_nodes(0, 1)
- assert(len(self.nodes[0].getpeerinfo()) == 0)
- assert(len(self.nodes[0].p2ps) == 0)
- self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("12"))
- self.connect_nodes(0, 2)
+ assert_equal(len(self.nodes[0].getpeerinfo()), 0)
+ assert_equal(len(self.nodes[0].p2ps), 0)
+ self.mini_wallet.send_self_transfer(from_node=self.nodes[0])
self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.")
self.stop_nodes()
@@ -111,17 +123,19 @@ class MempoolPersistTest(BitcoinTestFramework):
fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees']
assert_equal(fees['base'] + Decimal('0.00001000'), fees['modified'])
- self.log.debug('Verify time is loaded correctly')
- assert_equal(tx_creation_time, self.nodes[0].getmempoolentry(txid=last_txid)['time'])
+ self.log.debug('Verify all fields are loaded correctly')
+ assert_equal(last_entry, self.nodes[0].getmempoolentry(txid=last_txid))
# Verify accounting of mempool transactions after restart is correct
- self.nodes[2].syncwithvalidationinterfacequeue() # Flush mempool to wallet
- assert_equal(node2_balance, self.nodes[2].getbalance())
+ if self.is_sqlite_compiled():
+ self.nodes[2].loadwallet("watch")
+ wallet_watch = self.nodes[2].get_wallet_rpc("watch")
+ self.nodes[2].syncwithvalidationinterfacequeue() # Flush mempool to wallet
+ assert_equal(node2_balance, wallet_watch.getbalance())
- # start node0 with wallet disabled so wallet transactions don't get resubmitted
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
self.stop_nodes()
- self.start_node(0, extra_args=["-persistmempool=0", "-disablewallet"])
+ self.start_node(0, extra_args=["-persistmempool=0"])
assert self.nodes[0].getmempoolinfo()["loaded"]
assert_equal(len(self.nodes[0].getrawmempool()), 0)
@@ -164,18 +178,18 @@ class MempoolPersistTest(BitcoinTestFramework):
# ensure node0 doesn't have any connections
# make a transaction that will remain in the unbroadcast set
- assert(len(node0.getpeerinfo()) == 0)
- assert(len(node0.p2ps) == 0)
- node0.sendtoaddress(self.nodes[1].getnewaddress(), Decimal("12"))
+ assert_equal(len(node0.getpeerinfo()), 0)
+ assert_equal(len(node0.p2ps), 0)
+ self.mini_wallet.send_self_transfer(from_node=node0)
# shutdown, then startup with wallet disabled
- self.stop_nodes()
- self.start_node(0, extra_args=["-disablewallet"])
+ self.restart_node(0, extra_args=["-disablewallet"])
# check that txn gets broadcast due to unbroadcast logic
conn = node0.add_p2p_connection(P2PTxInvStore())
- node0.mockscheduler(16*60) # 15 min + 1 for buffer
+ node0.mockscheduler(16 * 60) # 15 min + 1 for buffer
self.wait_until(lambda: len(conn.get_invs()) == 1)
-if __name__ == '__main__':
+
+if __name__ == "__main__":
MempoolPersistTest().main()
diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py
index e93698b08e..15b90fa61f 100755
--- a/test/functional/p2p_addr_relay.py
+++ b/test/functional/p2p_addr_relay.py
@@ -6,22 +6,22 @@
Test addr relay
"""
+import random
+import time
+
from test_framework.messages import (
CAddress,
- NODE_NETWORK,
- NODE_WITNESS,
msg_addr,
msg_getaddr,
- msg_verack
+ msg_verack,
)
from test_framework.p2p import (
P2PInterface,
p2p_lock,
+ P2P_SERVICES,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than
-import random
-import time
class AddrReceiver(P2PInterface):
@@ -96,7 +96,7 @@ class AddrTest(BitcoinTestFramework):
for i in range(num):
addr = CAddress()
addr.time = self.mocktime + i
- addr.nServices = NODE_NETWORK | NODE_WITNESS
+ addr.nServices = P2P_SERVICES
addr.ip = f"123.123.123.{self.counter % 256}"
addr.port = 8333 + i
addrs.append(addr)
@@ -111,7 +111,7 @@ class AddrTest(BitcoinTestFramework):
for i in range(num):
addr = CAddress()
addr.time = self.mocktime + i
- addr.nServices = NODE_NETWORK | NODE_WITNESS
+ addr.nServices = P2P_SERVICES
addr.ip = f"{random.randrange(128,169)}.{random.randrange(1,255)}.{random.randrange(1,255)}.{random.randrange(1,255)}"
addr.port = 8333
addrs.append(addr)
diff --git a/test/functional/p2p_addrfetch.py b/test/functional/p2p_addrfetch.py
index 2a0f6432a9..25efd50040 100755
--- a/test/functional/p2p_addrfetch.py
+++ b/test/functional/p2p_addrfetch.py
@@ -8,14 +8,21 @@ Test p2p addr-fetch connections
import time
-from test_framework.messages import msg_addr, CAddress, NODE_NETWORK, NODE_WITNESS
-from test_framework.p2p import P2PInterface, p2p_lock
+from test_framework.messages import (
+ CAddress,
+ msg_addr,
+)
+from test_framework.p2p import (
+ P2PInterface,
+ p2p_lock,
+ P2P_SERVICES,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
ADDR = CAddress()
ADDR.time = int(time.time())
-ADDR.nServices = NODE_NETWORK | NODE_WITNESS
+ADDR.nServices = P2P_SERVICES
ADDR.ip = "192.0.0.8"
ADDR.port = 18444
diff --git a/test/functional/p2p_addrv2_relay.py b/test/functional/p2p_addrv2_relay.py
index 32c1d42b1c..3833c58680 100755
--- a/test/functional/p2p_addrv2_relay.py
+++ b/test/functional/p2p_addrv2_relay.py
@@ -11,10 +11,11 @@ import time
from test_framework.messages import (
CAddress,
msg_addrv2,
- NODE_NETWORK,
- NODE_WITNESS,
)
-from test_framework.p2p import P2PInterface
+from test_framework.p2p import (
+ P2PInterface,
+ P2P_SERVICES,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
@@ -24,7 +25,7 @@ ADDRS = []
for i in range(10):
addr = CAddress()
addr.time = int(time.time()) + i
- addr.nServices = NODE_NETWORK | NODE_WITNESS
+ addr.nServices = P2P_SERVICES
# Add one I2P address at an arbitrary position.
if i == 5:
addr.net = addr.NET_I2P
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index a71f736bd6..aa3b95fc4f 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -43,6 +43,7 @@ from test_framework.messages import (
from test_framework.p2p import (
P2PInterface,
p2p_lock,
+ P2P_SERVICES,
)
from test_framework.script import (
CScript,
@@ -83,10 +84,6 @@ from test_framework.util import (
assert_raises_rpc_error,
)
-# The versionbit bit used to signal activation of SegWit
-VB_WITNESS_BIT = 1
-VB_TOP_BITS = 0x20000000
-
MAX_SIGOP_COST = 80000
SEGWIT_HEIGHT = 120
@@ -196,8 +193,8 @@ class SegWitTest(BitcoinTestFramework):
self.num_nodes = 2
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
- ["-acceptnonstdtxn=1", "-segwitheight={}".format(SEGWIT_HEIGHT), "-whitelist=noban@127.0.0.1"],
- ["-acceptnonstdtxn=0", "-segwitheight={}".format(SEGWIT_HEIGHT)],
+ ["-acceptnonstdtxn=1", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}", "-whitelist=noban@127.0.0.1"],
+ ["-acceptnonstdtxn=0", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}"],
]
self.supports_cli = False
@@ -206,13 +203,13 @@ class SegWitTest(BitcoinTestFramework):
# Helper functions
- def build_next_block(self, version=4):
+ def build_next_block(self):
"""Build a block on top of node0's tip."""
tip = self.nodes[0].getbestblockhash()
height = self.nodes[0].getblockcount() + 1
block_time = self.nodes[0].getblockheader(tip)["mediantime"] + 1
block = create_block(int(tip, 16), create_coinbase(height), block_time)
- block.nVersion = version
+ block.nVersion = 4
block.rehash()
return block
@@ -224,14 +221,14 @@ class SegWitTest(BitcoinTestFramework):
def run_test(self):
# Setup the p2p connections
- # self.test_node sets NODE_WITNESS|NODE_NETWORK
- self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK | NODE_WITNESS)
+ # self.test_node sets P2P_SERVICES, i.e. NODE_WITNESS | NODE_NETWORK
+ self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn(), services=P2P_SERVICES)
# self.old_node sets only NODE_NETWORK
self.old_node = self.nodes[0].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK)
# self.std_node is for testing node1 (fRequireStandard=true)
- self.std_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=NODE_NETWORK | NODE_WITNESS)
+ self.std_node = self.nodes[1].add_p2p_connection(TestP2PConn(), services=P2P_SERVICES)
# self.std_wtx_node is for testing node1 with wtxid relay
- self.std_wtx_node = self.nodes[1].add_p2p_connection(TestP2PConn(wtxidrelay=True), services=NODE_NETWORK | NODE_WITNESS)
+ self.std_wtx_node = self.nodes[1].add_p2p_connection(TestP2PConn(wtxidrelay=True), services=P2P_SERVICES)
assert self.test_node.nServices & NODE_WITNESS != 0
@@ -298,7 +295,7 @@ class SegWitTest(BitcoinTestFramework):
# Mine a block with an anyone-can-spend coinbase,
# let it mature, then try to spend it.
- block = self.build_next_block(version=1)
+ block = self.build_next_block()
block.solve()
self.test_node.send_and_ping(msg_no_witness_block(block)) # make sure the block was processed
txid = block.vtx[0].sha256
@@ -336,8 +333,8 @@ class SegWitTest(BitcoinTestFramework):
tx.rehash()
assert tx.sha256 != tx.calc_sha256(with_witness=True)
- # Construct a segwit-signaling block that includes the transaction.
- block = self.build_next_block(version=(VB_TOP_BITS | (1 << VB_WITNESS_BIT)))
+ # Construct a block that includes the transaction.
+ block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
# Sending witness data before activation is not allowed (anti-spam
# rule).
@@ -364,7 +361,7 @@ class SegWitTest(BitcoinTestFramework):
# test_node has set NODE_WITNESS, so all getdata requests should be for
# witness blocks.
# Test announcing a block via inv results in a getdata, and that
- # announcing a version 4 or random VB block with a header results in a getdata
+ # announcing a block with a header results in a getdata
block1 = self.build_next_block()
block1.solve()
@@ -372,19 +369,13 @@ class SegWitTest(BitcoinTestFramework):
assert self.test_node.last_message["getdata"].inv[0].type == blocktype
test_witness_block(self.nodes[0], self.test_node, block1, True)
- block2 = self.build_next_block(version=4)
+ block2 = self.build_next_block()
block2.solve()
self.test_node.announce_block_and_wait_for_getdata(block2, use_header=True)
assert self.test_node.last_message["getdata"].inv[0].type == blocktype
test_witness_block(self.nodes[0], self.test_node, block2, True)
- block3 = self.build_next_block(version=(VB_TOP_BITS | (1 << 15)))
- block3.solve()
- self.test_node.announce_block_and_wait_for_getdata(block3, use_header=True)
- assert self.test_node.last_message["getdata"].inv[0].type == blocktype
- test_witness_block(self.nodes[0], self.test_node, block3, True)
-
# Check that we can getdata for witness blocks or regular blocks,
# and the right thing happens.
if not self.segwit_active:
@@ -429,7 +420,7 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(rpc_details["weight"], block.get_weight())
# Upgraded node should not ask for blocks from unupgraded
- block4 = self.build_next_block(version=4)
+ block4 = self.build_next_block()
block4.solve()
self.old_node.getdataset = set()
@@ -2017,8 +2008,8 @@ class SegWitTest(BitcoinTestFramework):
@subtest # type: ignore
def test_wtxid_relay(self):
# Use brand new nodes to avoid contamination from earlier tests
- self.wtx_node = self.nodes[0].add_p2p_connection(TestP2PConn(wtxidrelay=True), services=NODE_NETWORK | NODE_WITNESS)
- self.tx_node = self.nodes[0].add_p2p_connection(TestP2PConn(wtxidrelay=False), services=NODE_NETWORK | NODE_WITNESS)
+ self.wtx_node = self.nodes[0].add_p2p_connection(TestP2PConn(wtxidrelay=True), services=P2P_SERVICES)
+ self.tx_node = self.nodes[0].add_p2p_connection(TestP2PConn(wtxidrelay=False), services=P2P_SERVICES)
# Check wtxidrelay feature negotiation message through connecting a new peer
def received_wtxidrelay():
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 0600d8b9c5..b8b5c5a519 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -27,8 +27,6 @@ import subprocess
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from test_framework.blocktools import (
- CLTV_HEIGHT,
- DERSIG_HEIGHT,
create_block,
create_coinbase,
TIME_GENESIS_BLOCK,
@@ -142,11 +140,11 @@ class BlockchainTest(BitcoinTestFramework):
assert_greater_than(res['size_on_disk'], 0)
assert_equal(res['softforks'], {
- 'bip34': {'type': 'buried', 'active': True, 'height': 2},
- 'bip66': {'type': 'buried', 'active': True, 'height': DERSIG_HEIGHT},
- 'bip65': {'type': 'buried', 'active': True, 'height': CLTV_HEIGHT},
- 'csv': {'type': 'buried', 'active': False, 'height': 432},
- 'segwit': {'type': 'buried', 'active': True, 'height': 0},
+ 'bip34': {'type': 'buried', 'active': True, 'height': 1},
+ 'bip66': {'type': 'buried', 'active': True, 'height': 1},
+ 'bip65': {'type': 'buried', 'active': True, 'height': 1},
+ 'csv': {'type': 'buried', 'active': True, 'height': 1},
+ 'segwit': {'type': 'buried', 'active': True, 'height': 1},
'testdummy': {
'type': 'bip9',
'bip9': {
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 3fcca97cb7..0f3bbce54c 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -12,11 +12,10 @@ from itertools import product
import time
from test_framework.blocktools import COINBASE_MATURITY
-from test_framework.p2p import P2PInterface
import test_framework.messages
-from test_framework.messages import (
- NODE_NETWORK,
- NODE_WITNESS,
+from test_framework.p2p import (
+ P2PInterface,
+ P2P_SERVICES,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -189,7 +188,6 @@ class NetTest(BitcoinTestFramework):
def test_getnodeaddresses(self):
self.log.info("Test getnodeaddresses")
self.nodes[0].add_p2p_connection(P2PInterface())
- services = NODE_NETWORK | NODE_WITNESS
# Add an IPv6 address to the address manager.
ipv6_addr = "1233:3432:2434:2343:3234:2345:6546:4534"
@@ -217,7 +215,7 @@ class NetTest(BitcoinTestFramework):
assert_greater_than(10000, len(node_addresses))
for a in node_addresses:
assert_greater_than(a["time"], 1527811200) # 1st June 2018
- assert_equal(a["services"], services)
+ assert_equal(a["services"], P2P_SERVICES)
assert a["address"] in imported_addrs
assert_equal(a["port"], 8333)
assert_equal(a["network"], "ipv4")
@@ -228,7 +226,7 @@ class NetTest(BitcoinTestFramework):
assert_equal(res[0]["address"], ipv6_addr)
assert_equal(res[0]["network"], "ipv6")
assert_equal(res[0]["port"], 8333)
- assert_equal(res[0]["services"], services)
+ assert_equal(res[0]["services"], P2P_SERVICES)
# Test for the absence of onion and I2P addresses.
for network in ["onion", "i2p"]:
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index 8f17b29ff4..18abece253 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -6,7 +6,6 @@
from test_framework.blocktools import (
COINBASE_MATURITY,
- CSV_ACTIVATION_HEIGHT,
)
from test_framework.address import (
script_to_p2sh,
@@ -18,7 +17,6 @@ from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
find_vout_for_address,
- generate_to_height,
)
from test_framework.messages import (
CTxInWitness,
@@ -273,7 +271,6 @@ class SignRawTransactionsTest(BitcoinTestFramework):
getcontext().prec = 8
# Make sure CSV is active
- generate_to_height(self, self.nodes[0], CSV_ACTIVATION_HEIGHT)
assert self.nodes[0].getblockchaininfo()['softforks']['csv']['active']
# Create a P2WSH script with CSV
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 25b36b6a91..6de372cd8e 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -53,11 +53,6 @@ TIME_GENESIS_BLOCK = 1296688602
# Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
COINBASE_MATURITY = 100
-# Soft-fork activation heights
-DERSIG_HEIGHT = 102 # BIP 66
-CLTV_HEIGHT = 111 # BIP 65
-CSV_ACTIVATION_HEIGHT = 432
-
# From BIP141
WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py
index ec563cc290..78c63b57a1 100755
--- a/test/functional/test_framework/p2p.py
+++ b/test/functional/test_framework/p2p.py
@@ -356,7 +356,7 @@ class P2PInterface(P2PConnection):
return create_conn
- def peer_accept_connection(self, *args, services=NODE_NETWORK | NODE_WITNESS, **kwargs):
+ def peer_accept_connection(self, *args, services=P2P_SERVICES, **kwargs):
create_conn = super().peer_accept_connection(*args, **kwargs)
self.peer_connect_send_version(services)
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index d66499dbcb..ea5c641b4a 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -560,17 +560,6 @@ def mine_large_block(test_framework, node, utxos=None):
test_framework.generate(node, 1)
-def generate_to_height(test_framework, node, target_height):
- """Generates blocks until a given target block height has been reached.
- To prevent timeouts, only up to 200 blocks are generated per RPC call.
- Can be used to activate certain soft-forks (e.g. CSV, CLTV)."""
- current_height = node.getblockcount()
- while current_height < target_height:
- nblocks = min(200, target_height - current_height)
- current_height += len(test_framework.generate(node, nblocks))
- assert_equal(node.getblockcount(), target_height)
-
-
def find_vout_for_address(node, txid, addr):
"""
Locate the vout index of the given transaction sending to the
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 2d7f061912..cea59c6d69 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -82,7 +82,7 @@ class MiniWallet:
def rescan_utxos(self):
"""Drop all utxos and rescan the utxo set"""
self._utxos = []
- res = self._test_node.scantxoutset(action="start", scanobjects=[f'raw({self._scriptPubKey.hex()})'])
+ res = self._test_node.scantxoutset(action="start", scanobjects=[self.get_descriptor()])
assert_equal(True, res['success'])
for utxo in res['unspents']:
self._utxos.append({'txid': utxo['txid'], 'vout': utxo['vout'], 'value': utxo['amount']})
@@ -110,12 +110,15 @@ class MiniWallet:
def generate(self, num_blocks):
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
- blocks = self._test_node.generatetodescriptor(num_blocks, f'raw({self._scriptPubKey.hex()})')
+ blocks = self._test_node.generatetodescriptor(num_blocks, self.get_descriptor())
for b in blocks:
cb_tx = self._test_node.getblock(blockhash=b, verbosity=2)['tx'][0]
self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value']})
return blocks
+ def get_descriptor(self):
+ return self._test_node.getdescriptorinfo(f'raw({self._scriptPubKey.hex()})')['descriptor']
+
def get_address(self):
return self._address
@@ -180,8 +183,10 @@ class MiniWallet:
return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex, 'tx': tx}
def sendrawtransaction(self, *, from_node, tx_hex):
- from_node.sendrawtransaction(tx_hex)
+ txid = from_node.sendrawtransaction(tx_hex)
self.scan_tx(from_node.decoderawtransaction(tx_hex))
+ return txid
+
def make_chain(node, address, privkeys, parent_txid, parent_value, n=0, parent_locking_script=None, fee=DEFAULT_FEE):
"""Build a transaction that spends parent_txid.vout[n] and produces one output with
diff --git a/test/get_previous_releases.py b/test/get_previous_releases.py
index e92bb402b5..62fcad04b3 100755
--- a/test/get_previous_releases.py
+++ b/test/get_previous_releases.py
@@ -190,6 +190,7 @@ def check_host(args) -> int:
'aarch64-*-linux*': 'aarch64-linux-gnu',
'x86_64-*-linux*': 'x86_64-linux-gnu',
'x86_64-apple-darwin*': 'osx64',
+ 'aarch64-apple-darwin*': 'osx64',
}
args.platform = ''
for pattern, target in platforms.items():
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index 63e7c57ddb..b52e105a33 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -5,8 +5,6 @@
# names can be used.
# See https://github.com/google/sanitizers/issues/1364
signed-integer-overflow:txmempool.cpp
-# nLastSuccess read from peers.dat might cause an overflow in IsTerrible
-signed-integer-overflow:addrman.cpp
# https://github.com/bitcoin/bitcoin/pull/21798#issuecomment-829180719
signed-integer-overflow:policy/feerate.cpp