mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 09:46:52 -05:00
Merge #20165: Only relay Taproot spends if next block has it active
3d0556d410
Increase feature_taproot inactive test coverage (Pieter Wuille)525cbd425e
Only relay Taproot spends if next block has it active (Pieter Wuille) Pull request description: There should be no change to mempool transaction behavior for witness v1 transactions as long as no activation is defined. Until that point, we should treat the consensus rules as under debate, and for soft-fork safety, that means spends should be treated as non-standard. It's possible to go further: don't relay them unless the consensus rules are actually active for the next block. This extends non-relay to the period where a deployment is defined, started, locked in, or failed. I see no downsides to this, and the code change is very simple. ACKs for top commit: Sjors: utACK3d0556d410
MarcoFalke: review ACK3d0556d410
🏓 jnewbery: utACK3d0556d410
Tree-SHA512: ca625a2981716b4b44e8f3722718fd25fd04e25bf3ca1684924b8974fca49f7c1d438fdd9dcdfbc091a442002e20d441d42c41a0e2096e74a61068da6c60267a
This commit is contained in:
commit
c5ec0367d7
9 changed files with 28 additions and 16 deletions
|
@ -45,7 +45,7 @@ static void CCoinsCaching(benchmark::Bench& bench)
|
|||
// Benchmark.
|
||||
const CTransaction tx_1(t1);
|
||||
bench.run([&] {
|
||||
bool success = AreInputsStandard(tx_1, coins);
|
||||
bool success = AreInputsStandard(tx_1, coins, false);
|
||||
assert(success);
|
||||
});
|
||||
ECC_Stop();
|
||||
|
|
|
@ -155,7 +155,7 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR
|
|||
*
|
||||
* Note that only the non-witness portion of the transaction is checked here.
|
||||
*/
|
||||
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
||||
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, bool taproot_active)
|
||||
{
|
||||
if (tx.IsCoinBase())
|
||||
return true; // Coinbases don't use vin normally
|
||||
|
@ -183,6 +183,9 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
|||
if (subscript.GetSigOpCount(true) > MAX_P2SH_SIGOPS) {
|
||||
return false;
|
||||
}
|
||||
} else if (whichType == TxoutType::WITNESS_V1_TAPROOT) {
|
||||
// Don't allow Taproot spends unless Taproot is active.
|
||||
if (!taproot_active) return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,10 +95,11 @@ bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType);
|
|||
bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason);
|
||||
/**
|
||||
* Check for standard transaction types
|
||||
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
|
||||
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
|
||||
* @param[in] taproot_active Whether or taproot consensus rules are active (used to decide whether spends of them are permitted)
|
||||
* @return True if all inputs (scriptSigs) use only standard transaction forms
|
||||
*/
|
||||
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);
|
||||
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, bool taproot_active);
|
||||
/**
|
||||
* Check if the transaction is over standard P2WSH resources limit:
|
||||
* 3600bytes witnessScript size, 80bytes per witness stack element, 100 witness stack elements
|
||||
|
|
|
@ -229,7 +229,8 @@ void test_one_input(const std::vector<uint8_t>& buffer)
|
|||
break;
|
||||
}
|
||||
case 1: {
|
||||
(void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
|
||||
(void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache, false);
|
||||
(void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache, true);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
|
|
|
@ -95,7 +95,8 @@ void test_one_input(const std::vector<uint8_t>& buffer)
|
|||
|
||||
CCoinsView coins_view;
|
||||
const CCoinsViewCache coins_view_cache(&coins_view);
|
||||
(void)AreInputsStandard(tx, coins_view_cache);
|
||||
(void)AreInputsStandard(tx, coins_view_cache, false);
|
||||
(void)AreInputsStandard(tx, coins_view_cache, true);
|
||||
(void)IsWitnessStandard(tx, coins_view_cache);
|
||||
|
||||
UniValue u(UniValue::VOBJ);
|
||||
|
|
|
@ -343,7 +343,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
|
|||
txTo.vin[3].scriptSig << OP_11 << OP_11 << std::vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end());
|
||||
txTo.vin[4].scriptSig << std::vector<unsigned char>(fifteenSigops.begin(), fifteenSigops.end());
|
||||
|
||||
BOOST_CHECK(::AreInputsStandard(CTransaction(txTo), coins));
|
||||
BOOST_CHECK(::AreInputsStandard(CTransaction(txTo), coins, false));
|
||||
// 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4]
|
||||
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txTo), coins), 22U);
|
||||
|
||||
|
@ -356,7 +356,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
|
|||
txToNonStd1.vin[0].prevout.hash = txFrom.GetHash();
|
||||
txToNonStd1.vin[0].scriptSig << std::vector<unsigned char>(sixteenSigops.begin(), sixteenSigops.end());
|
||||
|
||||
BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd1), coins));
|
||||
BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd1), coins, false));
|
||||
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd1), coins), 16U);
|
||||
|
||||
CMutableTransaction txToNonStd2;
|
||||
|
@ -368,7 +368,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
|
|||
txToNonStd2.vin[0].prevout.hash = txFrom.GetHash();
|
||||
txToNonStd2.vin[0].scriptSig << std::vector<unsigned char>(twentySigops.begin(), twentySigops.end());
|
||||
|
||||
BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd2), coins));
|
||||
BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd2), coins, false));
|
||||
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd2), coins), 20U);
|
||||
}
|
||||
|
||||
|
|
|
@ -305,7 +305,7 @@ BOOST_AUTO_TEST_CASE(test_Get)
|
|||
t1.vout[0].nValue = 90*CENT;
|
||||
t1.vout[0].scriptPubKey << OP_1;
|
||||
|
||||
BOOST_CHECK(AreInputsStandard(CTransaction(t1), coins));
|
||||
BOOST_CHECK(AreInputsStandard(CTransaction(t1), coins, false));
|
||||
}
|
||||
|
||||
static void CreateCreditAndSpend(const FillableSigningProvider& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
|
||||
|
|
|
@ -690,7 +690,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|||
}
|
||||
|
||||
// Check for non-standard pay-to-script-hash in inputs
|
||||
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
|
||||
const auto& params = args.m_chainparams.GetConsensus();
|
||||
auto taproot_state = VersionBitsState(::ChainActive().Tip(), params, Consensus::DEPLOYMENT_TAPROOT, versionbitscache);
|
||||
if (fRequireStandard && !AreInputsStandard(tx, m_view, taproot_state == ThresholdState::ACTIVE)) {
|
||||
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
|
||||
}
|
||||
|
||||
|
|
|
@ -1129,13 +1129,13 @@ def spenders_taproot_inactive():
|
|||
]
|
||||
tap = taproot_construct(pub, scripts)
|
||||
|
||||
# Test that keypath spending is valid & standard if compliant, but valid and nonstandard otherwise.
|
||||
add_spender(spenders, "inactive/keypath_valid", key=sec, tap=tap)
|
||||
# Test that keypath spending is valid & non-standard, regardless of validity.
|
||||
add_spender(spenders, "inactive/keypath_valid", key=sec, tap=tap, standard=False)
|
||||
add_spender(spenders, "inactive/keypath_invalidsig", key=sec, tap=tap, standard=False, sighash=bitflipper(default_sighash))
|
||||
add_spender(spenders, "inactive/keypath_empty", key=sec, tap=tap, standard=False, witness=[])
|
||||
|
||||
# Same for scriptpath spending (but using future features like annex, leaf versions, or OP_SUCCESS is nonstandard).
|
||||
add_spender(spenders, "inactive/scriptpath_valid", key=sec, tap=tap, leaf="pk", inputs=[getter("sign")])
|
||||
# Same for scriptpath spending (and features like annex, leaf versions, or OP_SUCCESS don't change this)
|
||||
add_spender(spenders, "inactive/scriptpath_valid", key=sec, tap=tap, leaf="pk", standard=False, inputs=[getter("sign")])
|
||||
add_spender(spenders, "inactive/scriptpath_invalidsig", key=sec, tap=tap, leaf="pk", standard=False, inputs=[getter("sign")], sighash=bitflipper(default_sighash))
|
||||
add_spender(spenders, "inactive/scriptpath_invalidcb", key=sec, tap=tap, leaf="pk", standard=False, inputs=[getter("sign")], controlblock=bitflipper(default_controlblock))
|
||||
add_spender(spenders, "inactive/scriptpath_valid_unkleaf", key=sec, tap=tap, leaf="future_leaf", standard=False, inputs=[getter("sign")])
|
||||
|
@ -1451,7 +1451,11 @@ class TaprootTest(BitcoinTestFramework):
|
|||
|
||||
# Pre-taproot activation tests.
|
||||
self.log.info("Pre-activation tests...")
|
||||
self.test_spenders(self.nodes[0], spenders_taproot_inactive(), input_counts=[1, 2, 2, 2, 2, 3])
|
||||
# Run each test twice; once in isolation, and once combined with others. Testing in isolation
|
||||
# means that the standardness is verified in every test (as combined transactions are only standard
|
||||
# when all their inputs are standard).
|
||||
self.test_spenders(self.nodes[0], spenders_taproot_inactive(), input_counts=[1])
|
||||
self.test_spenders(self.nodes[0], spenders_taproot_inactive(), input_counts=[2, 3])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Add table
Reference in a new issue