mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-21 04:09:09 +02:00
Merge bitcoin/bitcoin#23512: policy: Treat taproot as always active
fa3e0da06bpolicy: Treat taproot as always active (MarcoFalke) Pull request description: Now that taproot is active, it can be treated as if it was always active for policy for the next major release. This simplifies the code and changes two things: * Importing `tr` descriptors can be done before the chain is fully synced. This is fine, because the wallet will already generate `tr` descriptors by default (regardless of the taproot status) after commit47fe7445e7. * Valid taproot spends won't be rejected from the mempool before taproot is active. This is strictly speaking a bugfix after commit47fe7445e7, since the wallet may generate taproot spends before the chain is fully synced. For example, a slow node or a purposefully offline node. Currently, the wallet needs the mempool to account for change. See https://github.com/bitcoin/bitcoin/issues/11887. A similar change was done for segwit v0 in https://github.com/bitcoin/bitcoin/pull/13120 . This effectively reverts commitc5ec0367d7. ACKs for top commit: mjdietzx: Code Review ACKfa3e0da06bachow101: ACKfa3e0da06bsipa: utACKfa3e0da06bgruve-p: ACKfa3e0da06bgunar: Code Review + tACKfa3e0da06rajarshimaitra: code review + tACKfa3e0da06bTree-SHA512: c6dc7a4e6c345bdec33f256847dc63906ab1696aa683ab9b32a79e715613950884ac3a1a7a44e95f31bb28e58dd64679a616175f7e152b21f5550f3337c8e622
This commit is contained in:
@@ -98,6 +98,7 @@ from test_framework.address import (
|
||||
program_to_witness
|
||||
)
|
||||
from collections import OrderedDict, namedtuple
|
||||
from enum import Enum
|
||||
from io import BytesIO
|
||||
import json
|
||||
import hashlib
|
||||
@@ -456,7 +457,7 @@ def spend(tx, idx, utxos, **kwargs):
|
||||
# Each spender is a tuple of:
|
||||
# - A scriptPubKey which is to be spent from (CScript)
|
||||
# - A comment describing the test (string)
|
||||
# - Whether the spending (on itself) is expected to be standard (bool)
|
||||
# - Whether the spending (on itself) is expected to be standard (Enum.Standard)
|
||||
# - A tx-signing lambda returning (scriptsig, witness_stack), taking as inputs:
|
||||
# - A transaction to sign (CTransaction)
|
||||
# - An input position (int)
|
||||
@@ -468,8 +469,14 @@ def spend(tx, idx, utxos, **kwargs):
|
||||
# - Whether this test demands being placed in a txin with no corresponding txout (for testing SIGHASH_SINGLE behavior)
|
||||
|
||||
Spender = namedtuple("Spender", "script,comment,is_standard,sat_function,err_msg,sigops_weight,no_fail,need_vin_vout_mismatch")
|
||||
# The full node versions that treat the tx standard.
|
||||
# ALL means any version
|
||||
# V23 means the major version 23.0 and any later version
|
||||
# NONE means no version
|
||||
Standard = Enum('Standard', 'ALL V23 NONE')
|
||||
|
||||
def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=False, spk_mutate_pre_p2sh=None, failure=None, standard=True, err_msg=None, sigops_weight=0, need_vin_vout_mismatch=False, **kwargs):
|
||||
|
||||
def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=False, spk_mutate_pre_p2sh=None, failure=None, standard=Standard.ALL, err_msg=None, sigops_weight=0, need_vin_vout_mismatch=False, **kwargs):
|
||||
"""Helper for constructing Spender objects using the context signing framework.
|
||||
|
||||
* tap: a TaprootInfo object (see taproot_construct), for Taproot spends (cannot be combined with pkh, witv0, or script)
|
||||
@@ -479,13 +486,18 @@ def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=
|
||||
* p2sh: whether the output is P2SH wrapper (this is supported even for Taproot, where it makes the output unencumbered)
|
||||
* spk_mutate_pre_psh: a callable to be applied to the script (before potentially P2SH-wrapping it)
|
||||
* failure: a dict of entries to override in the context when intentionally failing to spend (if None, no_fail will be set)
|
||||
* standard: whether the (valid version of) spending is expected to be standard
|
||||
* standard: whether the (valid version of) spending is expected to be standard (True is mapped to Standard.ALL, False is mapped to Standard.NONE)
|
||||
* err_msg: a string with an expected error message for failure (or None, if not cared about)
|
||||
* sigops_weight: the pre-taproot sigops weight consumed by a successful spend
|
||||
* need_vin_vout_mismatch: whether this test requires being tested in a transaction input that has no corresponding
|
||||
transaction output.
|
||||
"""
|
||||
|
||||
if standard == True:
|
||||
standard = Standard.ALL
|
||||
elif standard == False:
|
||||
standard = Standard.NONE
|
||||
|
||||
conf = dict()
|
||||
|
||||
# Compute scriptPubKey and set useful defaults based on the inputs.
|
||||
@@ -1170,12 +1182,12 @@ def spenders_taproot_inactive():
|
||||
tap = taproot_construct(pub, scripts)
|
||||
|
||||
# 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_valid", key=sec, tap=tap, standard=Standard.V23)
|
||||
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 (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_valid", key=sec, tap=tap, leaf="pk", standard=Standard.V23, 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")])
|
||||
@@ -1205,7 +1217,7 @@ def dump_json_test(tx, input_utxos, idx, success, failure):
|
||||
|
||||
# The "final" field indicates that a spend should be always valid, even with more validation flags enabled
|
||||
# than the listed ones. Use standardness as a proxy for this (which gives a conservative underestimate).
|
||||
if spender.is_standard:
|
||||
if spender.is_standard == Standard.ALL:
|
||||
fields.append(("final", True))
|
||||
|
||||
def dump_witness(wit):
|
||||
@@ -1470,8 +1482,13 @@ class TaprootTest(BitcoinTestFramework):
|
||||
for i in range(len(input_utxos)):
|
||||
tx.vin[i].scriptSig = input_data[i][i != fail_input][0]
|
||||
tx.wit.vtxinwit[i].scriptWitness.stack = input_data[i][i != fail_input][1]
|
||||
taproot_spend_policy = Standard.V23 if node.version is None else Standard.ALL
|
||||
# Submit to mempool to check standardness
|
||||
is_standard_tx = fail_input is None and all(utxo.spender.is_standard for utxo in input_utxos) and tx.nVersion >= 1 and tx.nVersion <= 2
|
||||
is_standard_tx = (
|
||||
fail_input is None # Must be valid to be standard
|
||||
and (all(utxo.spender.is_standard == Standard.ALL or utxo.spender.is_standard == taproot_spend_policy for utxo in input_utxos)) # All inputs must be standard
|
||||
and tx.nVersion >= 1 # The tx version must be standard
|
||||
and tx.nVersion <= 2)
|
||||
tx.rehash()
|
||||
msg = ','.join(utxo.spender.comment + ("*" if n == fail_input else "") for n, utxo in enumerate(input_utxos))
|
||||
if is_standard_tx:
|
||||
|
||||
@@ -242,20 +242,15 @@ class WalletTaprootTest(BitcoinTestFramework):
|
||||
assert_equal(len(rederive), 1)
|
||||
assert_equal(rederive[0], addr_g)
|
||||
|
||||
# tr descriptors cannot be imported when Taproot is not active
|
||||
# tr descriptors can be imported regardless of Taproot status
|
||||
result = self.privs_tr_enabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
|
||||
assert(result[0]["success"])
|
||||
result = self.pubs_tr_enabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
|
||||
assert(result[0]["success"])
|
||||
if desc.startswith("tr"):
|
||||
result = self.privs_tr_disabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
|
||||
assert(not result[0]["success"])
|
||||
assert_equal(result[0]["error"]["code"], -4)
|
||||
assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
|
||||
result = self.pubs_tr_disabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
|
||||
assert(not result[0]["success"])
|
||||
assert_equal(result[0]["error"]["code"], -4)
|
||||
assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
|
||||
result = self.privs_tr_disabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
|
||||
assert result[0]["success"]
|
||||
result = self.pubs_tr_disabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
|
||||
assert result[0]["success"]
|
||||
|
||||
def do_test_sendtoaddress(self, comment, pattern, privmap, treefn, keys_pay, keys_change):
|
||||
self.log.info("Testing %s through sendtoaddress" % comment)
|
||||
|
||||
Reference in New Issue
Block a user