Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
710118d
test: introduce VARINT (de)serialization routines
theStack Mar 4, 2025
422bbb1
test: introduce output amount (de)compression routines
theStack Mar 4, 2025
4c186bf
qa: use a clearer and documented amount error in malleated snapshot
darosior Feb 19, 2025
75e3429
qa: correct off-by-one in utxo snapshot fuzz target
darosior Feb 19, 2025
9a41c80
test util: split up ConnectBlock from MineBlock
darosior Feb 20, 2025
65120b2
fuzz: sanity check hardcoded snapshot in utxo_snapshot target
darosior Feb 20, 2025
e776ffd
qa: delete one "bad snapshot data" case in feature_assumeutxo.py
darosior Oct 3, 2025
1e2cc35
qa: introduce a MAX_SEQUENCE_NONFINAL constant to the functional test…
darosior Dec 18, 2025
9645d3b
qa: timelock coinbase transactions created in functional tests
darosior Apr 21, 2025
d658da4
contrib: timelock coinbase transactions in signet miner
darosior Apr 21, 2025
42d7f31
qa: timelock coinbase transactions created in fuzz targets
darosior Apr 21, 2025
ed2bf65
qa: use prev height as nLockTime for coinbase txs created in unit tests
darosior Apr 21, 2025
f6d7895
miner: timelock coinbase transactions
darosior Feb 19, 2025
2f5d5a3
qa: sanity check mined block have their coinbase timelocked to height
darosior Mar 27, 2025
22d44b4
chainparams: encapsulate deployment configuration logic
darosior Sep 29, 2025
ea29a48
chainparams: make deployment configuration available on all test netw…
darosior Sep 29, 2025
9868638
qa: use NORMAL_GBT_REQUEST_PARAMS consistently
darosior Apr 30, 2026
c1e4911
======= Consensus Cleanup BEGINS HERE =======
darosior Oct 14, 2025
6ada502
chainparams: add versionbits deployment for BIP 54
darosior Apr 30, 2026
6fe3788
scripted-diff: rename MAX_TX_LEGACY_SIGOPS to MAX_TX_BIP54_SIGOPS
darosior Jan 20, 2026
32f390a
moveonly: move CheckSigopsBIP54 from policy to consensus
darosior Sep 4, 2025
c243eb1
validation: make BIP54 sigops check consensus-critical
darosior Oct 14, 2025
a9e16b1
qa: add to utilities a version of SignSignature for Taproot inputs
darosior Sep 16, 2025
cf783ef
qa: extensive unit tests for BIP54 legacy sigops limit
darosior Sep 22, 2025
4b4ff59
fuzz: add a fuzz target for the BIP54 sigops check
darosior Sep 17, 2025
2be1784
scripted-diff: rename testnet4 timewarp constant
darosior Jan 31, 2025
a4fddb6
miner: update a timewarp comment to refer specifically to BIP 54
darosior Feb 4, 2026
7169bbe
validation: prevent timewarp attacks with a 2h grace period
darosior Sep 5, 2025
8a4d91c
validation: prevent negative difficulty adjustment intervals
darosior Jan 31, 2025
a617c8f
qa: BIP54 test vectors for timewarp and Murch-Zawy
darosior Sep 29, 2025
d5636ab
validation: enforce that coinbase transactions are timelocked to bloc…
darosior Mar 10, 2025
21cd39b
qa: BIP54 test vectors for restrictions on coinbase transactions
darosior Oct 1, 2025
149e368
Avoid creating <= 64-byte transactions in most functional tests.
TheBlueMatt Feb 25, 2019
da6f59e
[test] Separate 64B and 63B tx size tests
ajtowns Mar 28, 2023
e4fcb09
validation: make 64 bytes transactions invalid
darosior Mar 11, 2025
9aa3c37
qa: unit tests for BIP54 rule on 64-byte transactions (with JSON test…
darosior Sep 22, 2025
75e08f9
scripted-diff: qa: rename constant for BIP54 sigops limit
darosior Oct 16, 2025
70051d0
qa: end-to-end test all BIP54 mitigations
darosior Oct 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions contrib/signet/miner
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ PATH_BASE_TEST_FUNCTIONAL = os.path.abspath(os.path.join(PATH_BASE_CONTRIB_SIGNE
sys.path.insert(0, PATH_BASE_TEST_FUNCTIONAL)

from test_framework.blocktools import get_witness_script, script_BIP34_coinbase_height # noqa: E402
from test_framework.messages import CBlock, CBlockHeader, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, from_binary, from_hex, ser_string, ser_uint256, tx_from_hex # noqa: E402
from test_framework.messages import CBlock, CBlockHeader, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, from_binary, from_hex, ser_string, ser_uint256, tx_from_hex, MAX_SEQUENCE_NONFINAL # noqa: E402
from test_framework.psbt import PSBT, PSBTMap, PSBT_GLOBAL_UNSIGNED_TX, PSBT_IN_FINAL_SCRIPTSIG, PSBT_IN_FINAL_SCRIPTWITNESS, PSBT_IN_NON_WITNESS_UTXO, PSBT_IN_SIGHASH_TYPE # noqa: E402
from test_framework.script import CScript, CScriptOp # noqa: E402

Expand Down Expand Up @@ -102,7 +102,8 @@ def generate_psbt(tmpl, reward_spk, *, blocktime=None, poolid=None):
scriptSig = CScript(b"" + scriptSig + CScriptOp.encode_op_pushdata(poolid))

cbtx = CTransaction()
cbtx.vin = [CTxIn(COutPoint(0, 0xffffffff), scriptSig, 0xffffffff)]
cbtx.nLockTime = tmpl["height"] - 1
cbtx.vin = [CTxIn(COutPoint(0, 0xffffffff), scriptSig, MAX_SEQUENCE_NONFINAL)]
cbtx.vout = [CTxOut(tmpl["coinbasevalue"], reward_spk)]
cbtx.vin[0].nSequence = 2**32-2
cbtx.rehash()
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL
common/system.cpp
common/url.cpp
compressor.cpp
consensus/tx_verify.cpp
core_read.cpp
core_write.cpp
deploymentinfo.cpp
Expand Down Expand Up @@ -222,7 +223,6 @@ add_library(bitcoin_node STATIC EXCLUDE_FROM_ALL
bip324.cpp
blockencodings.cpp
blockfilter.cpp
consensus/tx_verify.cpp
dbwrapper.cpp
deploymentstatus.cpp
flatfile.cpp
Expand Down
81 changes: 53 additions & 28 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,8 @@

using util::SplitString;

void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& options)
{
if (!args.GetArgs("-signetseednode").empty()) {
options.seeds.emplace(args.GetArgs("-signetseednode"));
}
if (!args.GetArgs("-signetchallenge").empty()) {
const auto signet_challenge = args.GetArgs("-signetchallenge");
if (signet_challenge.size() != 1) {
throw std::runtime_error("-signetchallenge cannot be multiple values.");
}
const auto val{TryParseHex<uint8_t>(signet_challenge[0])};
if (!val) {
throw std::runtime_error(strprintf("-signetchallenge must be hex, not '%s'.", signet_challenge[0]));
}
options.challenge.emplace(*val);
}
}

void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& options)
static void HandleDeploymentArgs(const ArgsManager& args, CChainParams::DeploymentOptions& options)
{
if (auto value = args.GetBoolArg("-fastprune")) options.fastprune = *value;
if (HasTestOption(args, "bip94")) options.enforce_bip94 = true;

for (const std::string& arg : args.GetArgs("-testactivationheight")) {
const auto found{arg.find('@')};
if (found == std::string::npos) {
Expand Down Expand Up @@ -100,6 +79,43 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti
}
}

void ReadMainNetArgs(const ArgsManager& args, CChainParams::MainNetOptions& options)
{
HandleDeploymentArgs(args, options.dep_opts);
}

void ReadTestNetArgs(const ArgsManager& args, CChainParams::TestNetOptions& options)
{
HandleDeploymentArgs(args, options.dep_opts);
}

void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& options)
{
if (!args.GetArgs("-signetseednode").empty()) {
options.seeds.emplace(args.GetArgs("-signetseednode"));
}
if (!args.GetArgs("-signetchallenge").empty()) {
const auto signet_challenge = args.GetArgs("-signetchallenge");
if (signet_challenge.size() != 1) {
throw std::runtime_error("-signetchallenge cannot be multiple values.");
}
const auto val{TryParseHex<uint8_t>(signet_challenge[0])};
if (!val) {
throw std::runtime_error(strprintf("-signetchallenge must be hex, not '%s'.", signet_challenge[0]));
}
options.challenge.emplace(*val);
}
HandleDeploymentArgs(args, options.dep_opts);
}

void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& options)
{
if (auto value = args.GetBoolArg("-fastprune")) options.fastprune = *value;
if (HasTestOption(args, "bip94")) options.enforce_bip94 = true;

HandleDeploymentArgs(args, options.dep_opts);
}

static std::unique_ptr<const CChainParams> globalChainParams;

const CChainParams &Params() {
Expand All @@ -110,12 +126,21 @@ const CChainParams &Params() {
std::unique_ptr<const CChainParams> CreateChainParams(const ArgsManager& args, const ChainType chain)
{
switch (chain) {
case ChainType::MAIN:
return CChainParams::Main();
case ChainType::TESTNET:
return CChainParams::TestNet();
case ChainType::TESTNET4:
return CChainParams::TestNet4();
case ChainType::MAIN: {
auto opts = CChainParams::MainNetOptions{};
ReadMainNetArgs(args, opts);
return CChainParams::Main(opts);
}
case ChainType::TESTNET: {
auto opts = CChainParams::TestNetOptions{};
ReadTestNetArgs(args, opts);
return CChainParams::TestNet(opts);
}
case ChainType::TESTNET4: {
auto opts = CChainParams::TestNetOptions{};
ReadTestNetArgs(args, opts);
return CChainParams::TestNet4(opts);
}
case ChainType::SIGNET: {
auto opts = CChainParams::SigNetOptions{};
ReadSigNetArgs(args, opts);
Expand Down
4 changes: 2 additions & 2 deletions src/chainparamsbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-chain=<chain>", "Use the chain <chain> (default: main). Allowed values: " LIST_CHAIN_NAMES, 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("-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("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (test-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-testnet", "Use the testnet3 chain. Equivalent to -chain=test. Support for testnet3 is deprecated and will be removed in an upcoming release. Consider moving to testnet4 now by using -testnet4.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-testnet4", "Use the testnet4 chain. Equivalent to -chain=testnet4.", 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("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (test-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);
argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS);
Expand Down
17 changes: 16 additions & 1 deletion src/consensus/consensus.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,26 @@ static const size_t MIN_SERIALIZABLE_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR *
/** Interpret sequence numbers as relative lock-time constraints. */
static constexpr unsigned int LOCKTIME_VERIFY_SEQUENCE = (1 << 0);

/**
* Under BIP54, the first block in a difficulty adjustment period must not be more than 2
* hours (7200 seconds) earlier than the last block of the previous period.
*/
static constexpr int64_t MAX_TIMEWARP_BIP54{2 * 60 * 60};

/**
* Maximum number of seconds that the timestamp of the first
* block of a difficulty adjustment period is allowed to
* be earlier than the last block of the previous period (BIP94).
*/
static constexpr int64_t MAX_TIMEWARP = 600;
static constexpr int64_t MAX_TIMEWARP_TESTNET4 = 600;

/** The maximum number of potentially executed legacy signature operations in a single tx */
static constexpr unsigned int MAX_TX_BIP54_SIGOPS{2'500};

/**
* 64-byte transactions are invalid (BIP 54) due to serious flaws in the Merkle tree algorithm
* that make it so that such transactions may be re-interpreted as inner tree nodes.
*/
static constexpr unsigned int INVALID_TX_NONWITNESS_SIZE{64};

#endif // BITCOIN_CONSENSUS_CONSENSUS_H
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_
enum DeploymentPos : uint16_t {
DEPLOYMENT_TESTDUMMY,
DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342)
DEPLOYMENT_CONSENSUSCLEANUP, // Deployment of BIP 54
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
Expand Down
32 changes: 31 additions & 1 deletion src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,44 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
return nSigOps;
}

bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
bool Consensus::CheckSigopsBIP54(const CTransaction& tx, const CCoinsViewCache& inputs)
{
Assert(!tx.IsCoinBase());

unsigned int sigops{0};
for (const auto& txin: tx.vin) {
const auto& prev_txo{inputs.AccessCoin(txin.prevout).out};

// Unlike the existing block wide sigop limit which counts sigops present in the block
// itself (including the scriptPubKey which is not executed until spending later), BIP54
// counts sigops in the block where they are potentially executed (only).
// This means sigops in the spent scriptPubKey count toward the limit.
// `fAccurate` means correctly accounting sigops for CHECKMULTISIGs(VERIFY) with 16 pubkeys
// or fewer. This method of accounting was introduced by BIP16, and BIP54 reuses it.
// The GetSigOpCount call on the previous scriptPubKey counts both bare and P2SH sigops.
sigops += txin.scriptSig.GetSigOpCount(/*fAccurate=*/true);
sigops += prev_txo.scriptPubKey.GetSigOpCount(txin.scriptSig);

if (sigops > MAX_TX_BIP54_SIGOPS) {
return false;
}
}

return true;
}

bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, bool enforce_bip54)
{
// are the actual inputs available?
if (!inputs.HaveInputs(tx)) {
return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, "bad-txns-inputs-missingorspent",
strprintf("%s: inputs missing/spent", __func__));
}

if (enforce_bip54 && !Consensus::CheckSigopsBIP54(tx, inputs)) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-legacy-sigops", "too many legacy sigops (BIP54)");
}

CAmount nValueIn = 0;
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
const COutPoint &prevout = tx.vin[i].prevout;
Expand Down
8 changes: 7 additions & 1 deletion src/consensus/tx_verify.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ class TxValidationState;
/** Transaction validation functions */

namespace Consensus {
/**
* Check the total number of non-witness sigops across the whole transaction, as per BIP54.
*/
bool CheckSigopsBIP54(const CTransaction& tx, const CCoinsViewCache& inputs);

/**
* Check whether all inputs of this transaction are valid (no double spends and amounts)
* This does not modify the UTXO set. This does not check scripts and sigs.
* @param[out] txfee Set to the transaction fee if successful.
* @param[in] enforce_bip54 Whether to perform the BIP54 sigops check.
* Preconditions: tx.IsCoinBase() is false.
*/
[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, bool enforce_bip54);
} // namespace Consensus

/** Auxiliary functions for transaction validation (ideally should not be exposed) */
Expand Down
4 changes: 4 additions & 0 deletions src/deploymentinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
/*.name =*/ "taproot",
/*.gbt_force =*/ true,
},
{
/*.name =*/ "consensuscleanup",
/*.gbt_force =*/ false,
},
};

std::string DeploymentName(Consensus::BuriedDeployment dep)
Expand Down
10 changes: 10 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,16 @@ bool AppInitParameterInteraction(const ArgsManager& args)
}
}

// Prevent setting deployment parameters on mainnet.
if (chainparams.GetChainType() == ChainType::MAIN) {
if (args.IsArgSet("-testactivationheight")) {
return InitError(_("The -testactivationheight option may not be used on mainnet."));
}
if (args.IsArgSet("-vbparams")) {
return InitError(_("The -vbparams option may not be used on mainnet."));
}
}

// Also report errors from parsing before daemonization
{
kernel::Notifications notifications{};
Expand Down
Loading
Loading