BIP54 "Consensus Cleanup" implementation - #99
Conversation
ee557fc to
bc95a67
Compare
bc95a67 to
8d513a0
Compare
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
ariard
left a comment
There was a problem hiding this comment.
Just going over the introduced consensus code fix by fix for now.
98a9a5e to
224ef91
Compare
|
Thanks for kicking CI @ajtowns. I fixed the missing annotations that |
224ef91 to
5e06850
Compare
instagibbs
left a comment
There was a problem hiding this comment.
Focused primarily on BIP compliance, everything looks good to me
| break; | ||
| } | ||
|
|
||
| if (check_bip54 && !Consensus::CheckSigopsBIP54(tx, view)) { |
There was a problem hiding this comment.
Do we want to keep the CheckSigopsBIP54 check in standardness too? (I don't recall what we've done for others)
There was a problem hiding this comment.
I think we do want to keep it for now, that's what i implemented here.
There was a problem hiding this comment.
Alternatively we could have the check in CheckTxInputs. I did not go for that approach because it involves passing it the deployment status of BIP54. But it may be preferable in order to have a consistent error between block and tx submission (currently it would return a non-standard error for tx submission).
There was a problem hiding this comment.
Here is the diff to achieve this. It's clearly more Right, but is bigger and touches more files. What do you think?
Expand diff
diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp
index cd5e3f6680f54..6cf134c772f63 100644
--- a/src/consensus/tx_verify.cpp
+++ b/src/consensus/tx_verify.cpp
@@ -161,6 +161,9 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
return nSigOps;
}
+/**
+ * Check the total number of non-witness sigops across the whole transaction, as per BIP54.
+ */
bool Consensus::CheckSigopsBIP54(const CTransaction& tx, const CCoinsViewCache& inputs)
{
Assert(!tx.IsCoinBase());
@@ -187,7 +190,7 @@ bool Consensus::CheckSigopsBIP54(const CTransaction& tx, const CCoinsViewCache&
return true;
}
-bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
+bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, bool bip54_active)
{
// are the actual inputs available?
if (!inputs.HaveInputs(tx)) {
@@ -195,6 +198,10 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
strprintf("%s: inputs missing/spent", __func__));
}
+ if (bip54_active && !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;
diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h
index f387fed7fd6dd..af1eafca9f112 100644
--- a/src/consensus/tx_verify.h
+++ b/src/consensus/tx_verify.h
@@ -28,9 +28,10 @@ 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] bip54_active 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 bip54_active);
} // namespace Consensus
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 35c9f9cddf2d2..8388bae043e68 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -190,10 +190,6 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
return true; // Coinbases don't use vin normally
}
- if (!Consensus::CheckSigopsBIP54(tx, mapInputs)) {
- return false;
- }
-
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const CTxOut& prev = mapInputs.AccessCoin(tx.vin[i].prevout).out;
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index c435ed91ba793..4a37a315c0314 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -255,7 +255,8 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
// It is not allowed to call CheckTxInputs if CheckTransaction failed
return;
}
- if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out)) {
+ const bool bip54_active{fuzzed_data_provider.ConsumeBool()};
+ if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out, bip54_active)) {
assert(MoneyRange(tx_fee_out));
}
},
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 5f4de9b141d16..f185bf23304ae 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -1021,6 +1021,9 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
BOOST_AUTO_TEST_CASE(max_standard_legacy_sigops)
{
+ TxValidationState state;
+ const int dummy_height{0};
+ CAmount dummy_fee;
CCoinsView coins_dummy;
CCoinsViewCache coins(&coins_dummy);
CKey key;
@@ -1052,7 +1055,8 @@ BOOST_AUTO_TEST_CASE(max_standard_legacy_sigops)
// 2490 sigops is below the limit.
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(tx_max_sigops), coins), 2490);
- BOOST_CHECK(::AreInputsStandard(CTransaction(tx_max_sigops), coins));
+ BOOST_CHECK(Consensus::CheckTxInputs(CTransaction(tx_max_sigops), state, coins, dummy_height, dummy_fee, /*bip54_active=*/true));
+ BOOST_CHECK(state.IsValid());
// Adding one more input will bump this to 2505, hitting the limit.
tx_create.vout.emplace_back(424242, max_sigops_p2sh);
@@ -1064,7 +1068,9 @@ BOOST_AUTO_TEST_CASE(max_standard_legacy_sigops)
AddCoins(coins, CTransaction(tx_create), 0, false);
BOOST_CHECK_GT((p2sh_inputs_count + 1) * MAX_P2SH_SIGOPS, MAX_TX_LEGACY_SIGOPS);
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(tx_max_sigops), coins), 2505);
- BOOST_CHECK(!::AreInputsStandard(CTransaction(tx_max_sigops), coins));
+ BOOST_CHECK(!Consensus::CheckTxInputs(CTransaction(tx_max_sigops), state, coins, dummy_height, dummy_fee, /*bip54_active=*/true));
+ BOOST_CHECK(state.IsInvalid());
+ state = TxValidationState{};
// Now, check the limit can be reached with regular P2PK outputs too. Use a separate
// preparation transaction, to demonstrate spending coins from a single tx is irrelevant.
@@ -1083,7 +1089,8 @@ BOOST_AUTO_TEST_CASE(max_standard_legacy_sigops)
// The transaction now contains exactly 2500 sigops, the check should pass.
BOOST_CHECK_EQUAL(p2sh_inputs_count * MAX_P2SH_SIGOPS + p2pk_inputs_count * 1, MAX_TX_LEGACY_SIGOPS);
- BOOST_CHECK(::AreInputsStandard(CTransaction(tx_max_sigops), coins));
+ BOOST_CHECK(Consensus::CheckTxInputs(CTransaction(tx_max_sigops), state, coins, dummy_height, dummy_fee, /*bip54_active=*/true));
+ BOOST_CHECK(state.IsValid());
// Now, add some Segwit inputs. We add one for each defined Segwit output type. The limit
// is exclusively on non-witness sigops and therefore those should not be counted.
@@ -1099,7 +1106,8 @@ BOOST_AUTO_TEST_CASE(max_standard_legacy_sigops)
// The transaction now still contains exactly 2500 sigops, the check should pass.
AddCoins(coins, CTransaction(tx_create_segwit), 0, false);
- BOOST_REQUIRE(::AreInputsStandard(CTransaction(tx_max_sigops), coins));
+ BOOST_CHECK(Consensus::CheckTxInputs(CTransaction(tx_max_sigops), state, coins, dummy_height, dummy_fee, /*bip54_active=*/true));
+ BOOST_CHECK(state.IsValid());
// Add one more P2PK input. We'll reach the limit.
tx_create_p2pk.vout.emplace_back(212121, p2pk_script);
@@ -1111,7 +1119,8 @@ BOOST_AUTO_TEST_CASE(max_standard_legacy_sigops)
}
AddCoins(coins, CTransaction(tx_create_p2pk), 0, false);
BOOST_CHECK_GT(p2sh_inputs_count * MAX_P2SH_SIGOPS + p2pk_inputs_count * 1, MAX_TX_LEGACY_SIGOPS);
- BOOST_CHECK(!::AreInputsStandard(CTransaction(tx_max_sigops), coins));
+ BOOST_CHECK(!Consensus::CheckTxInputs(CTransaction(tx_max_sigops), state, coins, dummy_height, dummy_fee, /*bip54_active=*/true));
+ BOOST_CHECK(state.IsInvalid());
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 3a5a3fb306d38..4a3dfb35d02ab 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -777,7 +777,7 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
CAmount txfee = 0;
assert(!tx.IsCoinBase());
- assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee));
+ assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee, /*bip54_active=*/true));
for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout);
AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max());
}
diff --git a/src/validation.cpp b/src/validation.cpp
index c1b71643cffd3..e242d78a06be2 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -872,7 +872,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
// The mempool holds txs for the next block, so pass height+1 to CheckTxInputs
- if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees)) {
+ if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees, /*bip54_active=*/true)) {
return false; // state filled in by CheckTxInputs
}
@@ -2642,7 +2642,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
{
CAmount txfee = 0;
TxValidationState tx_state;
- if (!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, txfee)) {
+ if (!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, txfee, /*bip54_active=*/check_bip54)) {
// Any transaction validation failure in ConnectBlock is a block consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(),
@@ -2656,12 +2656,6 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
break;
}
- if (check_bip54 && !Consensus::CheckSigopsBIP54(tx, view)) {
- state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-txns-legacy-sigops",
- "contains a transaction with too many legacy sigops (BIP54)");
- break;
- }
-
// Check that transaction is BIP68 final
// BIP68 lock checks (as opposed to nLockTime checks) must
// be in ConnectBlock because they require the UTXO set
diff --git a/test/functional/mempool_sigoplimit.py b/test/functional/mempool_sigoplimit.py
index afd8cf968b6a8..17ec53edfea90 100755
--- a/test/functional/mempool_sigoplimit.py
+++ b/test/functional/mempool_sigoplimit.py
@@ -183,7 +183,7 @@ def create_bare_multisig_tx(utxo_to_spend=None):
assert_greater_than(2000, tx_parent.get_weight() + tx_child.get_weight())
def test_legacy_sigops_stdness(self):
- self.log.info("Test a transaction with too many legacy sigops in its inputs is non-standard.")
+ self.log.info("Test a transaction with too many legacy sigops in its inputs is invalid.")
# Restart with the default settings
self.restart_node(0)
@@ -204,18 +204,18 @@ def test_legacy_sigops_stdness(self):
outpoints.append(COutPoint(txid, res["sent_vout"]))
self.generate(self.nodes[0], 1)
- # Spending all these outputs at once accounts for 2505 legacy sigops and is non-standard.
+ # Spending all these outputs at once accounts for 2505 legacy sigops and is invalid.
nonstd_tx = CTransaction()
nonstd_tx.vin = [CTxIn(op, CScript([b"", packed_redeem_script])) for op in outpoints]
nonstd_tx.vout = [CTxOut(0, CScript([OP_RETURN, b""]))]
- assert_raises_rpc_error(-26, "bad-txns-nonstandard-inputs", self.nodes[0].sendrawtransaction, nonstd_tx.serialize().hex())
+ assert_raises_rpc_error(-26, "bad-txns-legacy-sigops", self.nodes[0].sendrawtransaction, nonstd_tx.serialize().hex())
- # Spending one less accounts for 2490 legacy sigops and is standard.
+ # Spending one less accounts for 2490 legacy sigops and is valid and standard.
std_tx = deepcopy(nonstd_tx)
std_tx.vin.pop()
self.nodes[0].sendrawtransaction(std_tx.serialize().hex())
- # The non-std transaction can also not be mined now it's been made a consensus rule.
+ # The invalid transaction also cannot appear in a block.
assert_raises_rpc_error(-25, "bad-txns-legacy-sigops", self.generateblock, self.nodes[0], "raw(42)", [nonstd_tx.serialize().hex()])
def run_test(self):There was a problem hiding this comment.
soft preference for having the check in a single place, even if it's slight code churn to get there
There was a problem hiding this comment.
Moved the sigops check to CheckTxInputs in d1d435f3b19f282448c1980918096647fe185e05.
There was a problem hiding this comment.
Hmm. Actually this approach makes it so this now reports a consensus error rather than a standardness one before 54 activation. So it's not clear to me that it's better. cc @instagibbs
ariard
left a comment
There was a problem hiding this comment.
Did a first review on the 64-byte fix, answering opened comments.
|
The consensus changes, with respect to their specification in BIP54, look good to me:
I think this PR is well organised (starting at ---). Each consensus change is first prepared, done in a minimal commit, and then followed by new tests. I'll study the test changes in more detail later. |
babef47 to
19d159e
Compare
ariard
left a comment
There was a problem hiding this comment.
See second comment on nSequence == SEQUENCE_FINAL`.
|
Finished a first round of review of the code for the 4 consensus fixes:
Will review more the fixes at the conceptual level and the breadth / depth of their respective test coverage. |
Sjors
left a comment
There was a problem hiding this comment.
See inline suggestion for renaming the bip_active argument to check_bip54 and making it a bit more clear in the tests if they're checking standardness (pre activation) or consensus (post activation).
The other consensus commit changes for the 19d159ed3757e871f7cc525f2623117d8ffe3498 push (compared to my previous review) look fine.
| @@ -190,10 +190,6 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) | |||
| return true; // Coinbases don't use vin normally | |||
| } | |||
|
|
|||
| if (!Consensus::CheckSigopsBIP54(tx, mapInputs)) { | |||
There was a problem hiding this comment.
In d1d435f validation: make BIP54 sigops check consensus-critical: note to other reviewers: this doesn't prematurely drop the standardness check before activation.
There was a problem hiding this comment.
This does, however, make the BIP54 checks mandatory for the mempool, such that they cannot be disabled by acceptnonstdtxn, aiui. (Which is good: you don't want consensus-invalid txs in your mempool at the point the consensus change activates)
There was a problem hiding this comment.
It does also prematurely turn the error message into a consensus failure one. Changing that to only perform the check when BIP 54 is active would make it so -acceptnonstdtxn would lead to accepting BIP 54 invalid txs in your mempool.
If we think it's important to only return a consensus error after 54 becomes active, then we should go back to duplicating the check instead of having it as part of CheckTxInputs (see this thread for why it was changed #99 (comment)).
There was a problem hiding this comment.
Hmm actually not only that but we should also move Consensus::CheckSigopsBIP54 outside of ValidateInputsStandardness. It would have to be checked once in PreChecks, returning a consensus or stdness error depending on the BIP 54 activation status, and then checked in ConnectBlock conditioned on BIP 54 being active.
It's not clear to me that it's worth it.
There was a problem hiding this comment.
Either way the 64-byte transaction error and the tx-sigops error need to be consistent with each other. In the current version of the PR (darosior#8), one always return a standardness error and the other one always returns a consensus error.
For now i'm switching the stripped size error to be a consensus one as well, but i'm curious to hear what you think.
This encapsulates the soft fork configuration logic as set by the `-testactivationheight` (for buried deployments) and `-vbparams` (for version bits deployments) options which for the moment are regtest-only, in order to make them available on other networks as well in the next commit. Can be reviewed using git's --color-moved option.
19d159e to
f24256f
Compare
… vectors) This adds tests exercising the bounds of the checks on the invalid transaction size, for various types of transactions (legacy, Segwit, bytes in input/output to get to 64 bytes) as well as sanity checking against some known historical violations. Thanks to Chris Stewart for digging up the historical violations to this rule.
It's not a standardness limit anymore, it was made consensus. Thanks to Anthony Towns for the scripted diff script. -BEGIN VERIFY SCRIPT- sed -i 's/MAX_STD_LEGACY_SIGOPS/MAX_TX_BIP54_SIGOPS/g' $(git grep -l MAX_STD_LEGACY_SIGOPS) sed -i 's/signature operations in validating a transaction./signature operations in a single transaction, per BIP54./' test/functional/test_framework/script_util.py -END VERIFY SCRIPT- Co-Authored-by: Anthony Towns <aj@erisian.com.au>
The previously introduced unit tests extensively test the specific implementation of each mitigation. This functional test complements them by end-to-end testing all mitigations. For the added timestamp constraints, it mimicks how they would get exploited (by implementing pseudo timewarp and Murch-Zawy attacks) and demonstrates those exploits are not possible anymore after BIP54 activates.
0d298f5 to
55cd9a5
Compare
Would simply adding the block's coinbase's locktime to the output of verbosity-1 diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index bbe401fcc6c..78062c74e6f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -183,6 +183,7 @@ UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIn
result.pushKV("strippedsize", (int)::GetSerializeSize(TX_NO_WITNESS(block)));
result.pushKV("size", (int)::GetSerializeSize(TX_WITH_WITNESS(block)));
result.pushKV("weight", (int)::GetBlockWeight(block));
+ result.pushKV("cblocktime", block.vtx.at(0)->nLockTime);
UniValue txs(UniValue::VARR);
switch (verbosity) {
@@ -724,6 +725,7 @@ static RPCHelpMan getblock()
{RPCResult::Type::NUM, "version", "The block version"},
{RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
{RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
+ {RPCResult::Type::NUM, "locktime", "The coinbase transaction's locktime value"},
{RPCResult::Type::ARR, "tx", "The transaction ids",
{{RPCResult::Type::STR_HEX, "", "The transaction id"}}},
{RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},I've tested this on mainnet with your snippet from earlier and it's reasonably performant. |
|
On the coinbase transaction and requesting the nLocktime to be set to the current block height minus one and the nSequence field must not be equal to 0xff_ff_ff_ff, I'm still thinking the second check is unecessary. The first check is ensuring that the transaction is always final (2nd check of https://groups.google.com/g/bitcoindev/c/6TTlDwP2OQg Per se, unless I'm missing something, I don't think the value of the nSequence field needs to be set to a precise value and the whole field could be even “free" up field either for (a) for an extra nonce or (b) as a space where to fit a new commitment structure in the future (and that's particularly interesting as it would work even if there are no segwit txn in the block cf. BIP141). cc @darosior |
|
|
||
| // Make sure the coinbase transaction is timelocked to the block's height. | ||
| if (nHeight > 0 && DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_CONSENSUSCLEANUP)) { | ||
| Assert(!block.vtx.empty() && block.vtx[0] && !block.vtx[0]->vin.empty()); |
There was a problem hiding this comment.
This is not consensus, but more relay. At CMPCTBLOCK reception, there must be at least one transaction.
if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty()))
return READ_STATUS_INVALID;
Sanitizing that the received coinbase is respecting the new rules of consensus avoids to store cmpct block state for a block that will be overall consenus invalid (note the validation of the coinbase is already lax afaict).
There was a problem hiding this comment.
That would be a tiny optimisation in the event that someone decided to waste an enormous amount of energy creating an invalid block. I don't think it's worth it.
There was a problem hiding this comment.
The number of compact block relay peers != block relay peers iirc, so I think there still some margin of shitting the coinbase tx and playing with it, but effectively I don’t think it’s something per se new coming with BIP54, and it’s rather ante date it.
Yeah, seems like -- it takes about 3 times as long as the dedicated RPC for me in a quick test, but that's good enough for how much simpler it is. I found "cblocktime" confusing ( I messed around with the dedicated approach some more; it looks like if you add a dedicated
|
Indeed, it reads like "c block time",
Ideally we keep it generic enough so it can be PR'd to Bitcoin Core indepedent of BIP54. Maybe add a Update: bitcoin#34512 |
|
ACK 55cd9a5 -- this looks good to me i think, and should be ready to merge |
@ariard i have replied to you on the mailing list. TL;DR: it seems my explanation was lacking and you missed the purpose for making sure nSequence is never final.
Note also we don't constrain it to be a specific value. On the contrary it can be anything as long it's not the specific final value. |
@darosior I think I got your idea with the assumption that BIP54 can be buried in the future, and the check to be applied on all historical coinbase transactions, therefore ensuring any old coinbase cannot be used to generate a post-BIP54 action height identifier collision. Replied, I’m still thinking you can make a simpler assumption by just relying on the PoW ordering mechanism itself. There can be only and only one block valid at a given chain height. |
Yes, I see it doesn’t have to be set to a specific value. See the point on protecting the coinbase nSequence field. It’s of independent interest, that the check is kept or not. |
Historical coinbase transactions on mainnet, testnet3, testnet4 and signet all fail to comply with bip54 rules, so that approach isn't possible. |
Okay so up to Darosior what he meant here on the ml: "For instance, if BIP 54 activation height ever gets buried in Bitcoin Core, the BIP 30 check could just be disabled past So strictly a buried activation height, nothing like a retroactive validation from genesis like it was done for BIP30. Point is the additional check in nsequence field is superflous imho. |
|
The issue is what happens if there is a reorg back to a point where neither BIP 34 or BIP 54 are active. At that point, BIP 30 checks must be enabled. With the nsequence check, those checks no longer need to be performed after BIP 54 is enabled because no coinbase that complies with its rules can have ever previously appeared in the blockchain. Also, this discussion should be on the mailing list, not here. |
e0463b4 rpc: add coinbase_tx field to getblock (Sjors Provoost) Pull request description: This adds a `coinbase_tx` field to the `getblock` RPC result, starting at verbosity level 1. It contains only fields guaranteed to be small, i.e. not the outputs. Initial motivation for this was to more efficiently scan for BIP54 compliance. Without this change, it requires verbosity level 2 to get the coinbase, which makes such scan very slow. See bitcoin-inquisition#99 (comment). Adding these fields should be useful in general though and hardly makes the verbosity 1 result longer. ``` bitcoin rpc help getblock getblock "blockhash" ( verbosity ) If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbosity is 1, returns an Object with information about block <hash>. If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. ... Result (for verbosity = 1): { (json object) "hash" : "hex", (string) the block hash (same as provided) "confirmations" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain "size" : n, (numeric) The block size "strippedsize" : n, (numeric) The block size excluding witness data "weight" : n, (numeric) The block weight as defined in BIP 141 "coinbase_tx" : { (json object) Coinbase transaction metadata "version" : n, (numeric) The coinbase transaction version "locktime" : n, (numeric) The coinbase transaction's locktime (nLockTime) "sequence" : n, (numeric) The coinbase input's sequence number (nSequence) "coinbase" : "hex", (string) The coinbase input's script "witness" : "hex" (string, optional) The coinbase input's first (and only) witness stack element, if present }, "height" : n, (numeric) The block height or index "version" : n, (numeric) The block version ... ``` ``` bitcoin rpc getblock 000000000000000000013c986f9aebe800a78454c835ccd07ecae2650bfad3f6 1 ``` ```json { "hash": "000000000000000000013c986f9aebe800a78454c835ccd07ecae2650bfad3f6", "confirmations": 2, "height": 935113, "version": 561807360, "...": "...", "weight": 3993624, "coinbase_tx": { "version": 2, "locktime": 0, "sequence": 4294967295, "coinbase": "03c9440e04307c84692f466f756e6472792055534120506f6f6c202364726f70676f6c642ffabe6d6d9a8624235259d3680c972b0dd42fa3fe1c45c5e5ae5a96fe10c182bda17080e70100000000000000184b17d3f138020000000000", "witness": "0000000000000000000000000000000000000000000000000000000000000000" }, "tx": [ "70eb053340c7978c5aa1b34d75e1ba9f9d1879c09896317f306f30c243536b62", "5bcf8ed2900cb70721e808b8977898e47f2c9001fcee83c3ccd29e51c7775dcd", "3f1991771aef846d7bb379d2931cccc04e8421a630ec9f52d22449d028d2e7f4", "..." ] } ``` ACKs for top commit: sedited: Re-ACK e0463b4 darosior: re-utACK e0463b4 Tree-SHA512: 1b3e7111e6a0edffde8619c49b3e9bca833c8e90e416defc66811bd56dd00d45b69a84c8fd9715508f4e6515f77ac4fb5c59868ab997ae111017c78c05b74ba3
83b9757 rpc: add coinbase_tx field to getblock (Sjors Provoost) Pull request description: This adds a `coinbase_tx` field to the `getblock` RPC result, starting at verbosity level 1. It contains only fields guaranteed to be small, i.e. not the outputs. Initial motivation for this was to more efficiently scan for BIP54 compliance. Without this change, it requires verbosity level 2 to get the coinbase, which makes such scan very slow. See bitcoin-inquisition/bitcoin#99 (comment). Adding these fields should be useful in general though and hardly makes the verbosity 1 result longer. ``` bitcoin rpc help getblock getblock "blockhash" ( verbosity ) If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbosity is 1, returns an Object with information about block <hash>. If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. ... Result (for verbosity = 1): { (json object) "hash" : "hex", (string) the block hash (same as provided) "confirmations" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain "size" : n, (numeric) The block size "strippedsize" : n, (numeric) The block size excluding witness data "weight" : n, (numeric) The block weight as defined in BIP 141 "coinbase_tx" : { (json object) Coinbase transaction metadata "version" : n, (numeric) The coinbase transaction version "locktime" : n, (numeric) The coinbase transaction's locktime (nLockTime) "sequence" : n, (numeric) The coinbase input's sequence number (nSequence) "coinbase" : "hex", (string) The coinbase input's script "witness" : "hex" (string, optional) The coinbase input's first (and only) witness stack element, if present }, "height" : n, (numeric) The block height or index "version" : n, (numeric) The block version ... ``` ``` bitcoin rpc getblock 000000000000000000013c986f9aebe800a78454c835ccd07ecae2650bfad3f6 1 ``` ```json { "hash": "000000000000000000013c986f9aebe800a78454c835ccd07ecae2650bfad3f6", "confirmations": 2, "height": 935113, "version": 561807360, "...": "...", "weight": 3993624, "coinbase_tx": { "version": 2, "locktime": 0, "sequence": 4294967295, "coinbase": "03c9440e04307c84692f466f756e6472792055534120506f6f6c202364726f70676f6c642ffabe6d6d9a8624235259d3680c972b0dd42fa3fe1c45c5e5ae5a96fe10c182bda17080e70100000000000000184b17d3f138020000000000", "witness": "0000000000000000000000000000000000000000000000000000000000000000" }, "tx": [ "70eb053340c7978c5aa1b34d75e1ba9f9d1879c09896317f306f30c243536b62", "5bcf8ed2900cb70721e808b8977898e47f2c9001fcee83c3ccd29e51c7775dcd", "3f1991771aef846d7bb379d2931cccc04e8421a630ec9f52d22449d028d2e7f4", "..." ] } ``` ACKs for top commit: sedited: Re-ACK 83b9757 darosior: re-utACK 83b9757 Tree-SHA512: 1b3e7111e6a0edffde8619c49b3e9bca833c8e90e416defc66811bd56dd00d45b69a84c8fd9715508f4e6515f77ac4fb5c59868ab997ae111017c78c05b74ba3
33a69df rpc: add coinbase_tx field to getblock (Sjors Provoost) Pull request description: This adds a `coinbase_tx` field to the `getblock` RPC result, starting at verbosity level 1. It contains only fields guaranteed to be small, i.e. not the outputs. Initial motivation for this was to more efficiently scan for BIP54 compliance. Without this change, it requires verbosity level 2 to get the coinbase, which makes such scan very slow. See bitcoin-inquisition/bitcoin#99 (comment). Adding these fields should be useful in general though and hardly makes the verbosity 1 result longer. ``` bitcoin rpc help getblock getblock "blockhash" ( verbosity ) If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbosity is 1, returns an Object with information about block <hash>. If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. ... Result (for verbosity = 1): { (json object) "hash" : "hex", (string) the block hash (same as provided) "confirmations" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain "size" : n, (numeric) The block size "strippedsize" : n, (numeric) The block size excluding witness data "weight" : n, (numeric) The block weight as defined in BIP 141 "coinbase_tx" : { (json object) Coinbase transaction metadata "version" : n, (numeric) The coinbase transaction version "locktime" : n, (numeric) The coinbase transaction's locktime (nLockTime) "sequence" : n, (numeric) The coinbase input's sequence number (nSequence) "coinbase" : "hex", (string) The coinbase input's script "witness" : "hex" (string, optional) The coinbase input's first (and only) witness stack element, if present }, "height" : n, (numeric) The block height or index "version" : n, (numeric) The block version ... ``` ``` bitcoin rpc getblock 000000000000000000013c986f9aebe800a78454c835ccd07ecae2650bfad3f6 1 ``` ```json { "hash": "000000000000000000013c986f9aebe800a78454c835ccd07ecae2650bfad3f6", "confirmations": 2, "height": 935113, "version": 561807360, "...": "...", "weight": 3993624, "coinbase_tx": { "version": 2, "locktime": 0, "sequence": 4294967295, "coinbase": "03c9440e04307c84692f466f756e6472792055534120506f6f6c202364726f70676f6c642ffabe6d6d9a8624235259d3680c972b0dd42fa3fe1c45c5e5ae5a96fe10c182bda17080e70100000000000000184b17d3f138020000000000", "witness": "0000000000000000000000000000000000000000000000000000000000000000" }, "tx": [ "70eb053340c7978c5aa1b34d75e1ba9f9d1879c09896317f306f30c243536b62", "5bcf8ed2900cb70721e808b8977898e47f2c9001fcee83c3ccd29e51c7775dcd", "3f1991771aef846d7bb379d2931cccc04e8421a630ec9f52d22449d028d2e7f4", "..." ] } ``` ACKs for top commit: sedited: Re-ACK 33a69df darosior: re-utACK 33a69df Tree-SHA512: 1b3e7111e6a0edffde8619c49b3e9bca833c8e90e416defc66811bd56dd00d45b69a84c8fd9715508f4e6515f77ac4fb5c59868ab997ae111017c78c05b74ba3
e0463b4e8c25f8a5fe10999f2821e7b221d2e40a rpc: add coinbase_tx field to getblock (Sjors Provoost) Pull request description: This adds a `coinbase_tx` field to the `getblock` RPC result, starting at verbosity level 1. It contains only fields guaranteed to be small, i.e. not the outputs. Initial motivation for this was to more efficiently scan for BIP54 compliance. Without this change, it requires verbosity level 2 to get the coinbase, which makes such scan very slow. See bitcoin-inquisition/bitcoin#99 (comment). Adding these fields should be useful in general though and hardly makes the verbosity 1 result longer. ``` bitcoin rpc help getblock getblock "blockhash" ( verbosity ) If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbosity is 1, returns an Object with information about block <hash>. If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. ... Result (for verbosity = 1): { (json object) "hash" : "hex", (string) the block hash (same as provided) "confirmations" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain "size" : n, (numeric) The block size "strippedsize" : n, (numeric) The block size excluding witness data "weight" : n, (numeric) The block weight as defined in BIP 141 "coinbase_tx" : { (json object) Coinbase transaction metadata "version" : n, (numeric) The coinbase transaction version "locktime" : n, (numeric) The coinbase transaction's locktime (nLockTime) "sequence" : n, (numeric) The coinbase input's sequence number (nSequence) "coinbase" : "hex", (string) The coinbase input's script "witness" : "hex" (string, optional) The coinbase input's first (and only) witness stack element, if present }, "height" : n, (numeric) The block height or index "version" : n, (numeric) The block version ... ``` ``` bitcoin rpc getblock 000000000000000000013c986f9aebe800a78454c835ccd07ecae2650bfad3f6 1 ``` ```json { "hash": "000000000000000000013c986f9aebe800a78454c835ccd07ecae2650bfad3f6", "confirmations": 2, "height": 935113, "version": 561807360, "...": "...", "weight": 3993624, "coinbase_tx": { "version": 2, "locktime": 0, "sequence": 4294967295, "coinbase": "03c9440e04307c84692f466f756e6472792055534120506f6f6c202364726f70676f6c642ffabe6d6d9a8624235259d3680c972b0dd42fa3fe1c45c5e5ae5a96fe10c182bda17080e70100000000000000184b17d3f138020000000000", "witness": "0000000000000000000000000000000000000000000000000000000000000000" }, "tx": [ "70eb053340c7978c5aa1b34d75e1ba9f9d1879c09896317f306f30c243536b62", "5bcf8ed2900cb70721e808b8977898e47f2c9001fcee83c3ccd29e51c7775dcd", "3f1991771aef846d7bb379d2931cccc04e8421a630ec9f52d22449d028d2e7f4", "..." ] } ``` ACKs for top commit: sedited: Re-ACK e0463b4e8c25f8a5fe10999f2821e7b221d2e40a darosior: re-utACK e0463b4e8c25f8a5fe10999f2821e7b221d2e40a Tree-SHA512: 1b3e7111e6a0edffde8619c49b3e9bca833c8e90e416defc66811bd56dd00d45b69a84c8fd9715508f4e6515f77ac4fb5c59868ab997ae111017c78c05b74ba3
This implements the BIP54 consensus rules.
Each of the 4 mitigations comes with its own unit test in a new
src/test/bip54_tests.cppmodule. The unit tests can be used to generate JSON test vectors for the BIP (opt-in). For those unit tests that require mainnet blocks, the JSON test vectors were generated separately and included in this PR as data files.Besides the unit test for each mitigation, this contains a fuzz harness for the sigop accounting logic as well as a functional test which goes over all mitigations in pseudo realistic situations (for instance, mimic a Timewarp attack and a Murch-Zawy attack to demonstrate the new timestamp restrictions would prevent that). The fuzz target for the sigop accounting logic was designed to make it possible to seed it from the BIP test vectors. This branch implements that.
The chains of headers for the timestamp restrictions test vectors were generated using this unit test (note the premined headers to allow modifications without re-generating all headers from scratch). The small chains of blocks for the coinbase restrictions test vectors were generated using this unit test.