mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-05-29 23:33:33 +02:00
Merge bitcoin/bitcoin#29325: consensus: Store transaction nVersion as uint32_t
429ec1aaaarefactor: Rename CTransaction::nVersion to version (Ava Chow)27e70f1f5bconsensus: Store transaction nVersion as uint32_t (Ava Chow) Pull request description: Given that the use of a transaction's nVersion is always as an unsigned int, it doesn't make sense to store it as signed and then cast it to unsigned everywhere it is used and displayed. Since a few alternative implementations have recently been revealed to have made an error with this signedness that would have resulted in consensus failure, I think it makes sense for us to just make this always unsigned to make it clear that the version is treated as unsigned. This would also help us avoid future potential issues with signedness of this value. I believe that this is safe and does not actually change what transactions would or would not be considered both standard and consensus valid. Within consensus, the only use of the version in consensus is in BIP68 validation which was already casting it to uint32_t. Within policy, although it is used as a signed int for the transaction version number check, I do not think that this change would change standardness. Standard transactions are limited to the range [1, 2]. Negative numbers would have fallen under the < 1 condition, but by making it unsigned, they are still non-standard under the > 2 condition. Unsigned and signed ints are serialized and unserialized the same way so there is no change in serialization. ACKs for top commit: maflcko: ACK429ec1aaaa🐿 glozow: ACK429ec1aaaashaavan: ACK429ec1aaaa💯 Tree-SHA512: 0bcd92a245d7d16c3665d2d4e815a4ef28207ad4a1fb46c6f0203cdafeab1b82c4e95e4bdce7805d80a4f4a46074f6542abad708e970550d38a00d759e3dcef1
This commit is contained in:
@@ -78,8 +78,8 @@ class BIP68Test(BitcoinTestFramework):
|
||||
self.log.info("Activating BIP68 (and 112/113)")
|
||||
self.activateCSV()
|
||||
|
||||
self.log.info("Verifying nVersion=2 transactions are standard.")
|
||||
self.log.info("Note that nVersion=2 transactions are always standard (independent of BIP68 activation status).")
|
||||
self.log.info("Verifying version=2 transactions are standard.")
|
||||
self.log.info("Note that version=2 transactions are always standard (independent of BIP68 activation status).")
|
||||
self.test_version2_relay()
|
||||
|
||||
self.log.info("Passed")
|
||||
@@ -107,7 +107,7 @@ class BIP68Test(BitcoinTestFramework):
|
||||
# This transaction will enable sequence-locks, so this transaction should
|
||||
# fail
|
||||
tx2 = CTransaction()
|
||||
tx2.nVersion = 2
|
||||
tx2.version = 2
|
||||
sequence_value = sequence_value & 0x7fffffff
|
||||
tx2.vin = [CTxIn(COutPoint(tx1_id, 0), nSequence=sequence_value)]
|
||||
tx2.wit.vtxinwit = [CTxInWitness()]
|
||||
@@ -119,7 +119,7 @@ class BIP68Test(BitcoinTestFramework):
|
||||
|
||||
# Setting the version back down to 1 should disable the sequence lock,
|
||||
# so this should be accepted.
|
||||
tx2.nVersion = 1
|
||||
tx2.version = 1
|
||||
|
||||
self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2.serialize().hex())
|
||||
|
||||
@@ -159,7 +159,7 @@ class BIP68Test(BitcoinTestFramework):
|
||||
using_sequence_locks = False
|
||||
|
||||
tx = CTransaction()
|
||||
tx.nVersion = 2
|
||||
tx.version = 2
|
||||
value = 0
|
||||
for j in range(num_inputs):
|
||||
sequence_value = 0xfffffffe # this disables sequence locks
|
||||
@@ -228,7 +228,7 @@ class BIP68Test(BitcoinTestFramework):
|
||||
# Anyone-can-spend mempool tx.
|
||||
# Sequence lock of 0 should pass.
|
||||
tx2 = CTransaction()
|
||||
tx2.nVersion = 2
|
||||
tx2.version = 2
|
||||
tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
|
||||
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
|
||||
self.wallet.sign_tx(tx=tx2)
|
||||
@@ -246,7 +246,7 @@ class BIP68Test(BitcoinTestFramework):
|
||||
sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG
|
||||
|
||||
tx = CTransaction()
|
||||
tx.nVersion = 2
|
||||
tx.version = 2
|
||||
tx.vin = [CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value)]
|
||||
tx.wit.vtxinwit = [CTxInWitness()]
|
||||
tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
|
||||
@@ -360,7 +360,7 @@ class BIP68Test(BitcoinTestFramework):
|
||||
|
||||
# Make an anyone-can-spend transaction
|
||||
tx2 = CTransaction()
|
||||
tx2.nVersion = 1
|
||||
tx2.version = 1
|
||||
tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
|
||||
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
|
||||
|
||||
@@ -376,7 +376,7 @@ class BIP68Test(BitcoinTestFramework):
|
||||
sequence_value = 100 # 100 block relative locktime
|
||||
|
||||
tx3 = CTransaction()
|
||||
tx3.nVersion = 2
|
||||
tx3.version = 2
|
||||
tx3.vin = [CTxIn(COutPoint(tx2.sha256, 0), nSequence=sequence_value)]
|
||||
tx3.wit.vtxinwit = [CTxInWitness()]
|
||||
tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
|
||||
|
||||
@@ -110,7 +110,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
|
||||
def create_bip112special(self, input, txversion):
|
||||
tx = self.create_self_transfer_from_utxo(input)
|
||||
tx.nVersion = txversion
|
||||
tx.version = txversion
|
||||
self.miniwallet.sign_tx(tx)
|
||||
tx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig)))
|
||||
tx.rehash()
|
||||
@@ -118,7 +118,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
|
||||
def create_bip112emptystack(self, input, txversion):
|
||||
tx = self.create_self_transfer_from_utxo(input)
|
||||
tx.nVersion = txversion
|
||||
tx.version = txversion
|
||||
self.miniwallet.sign_tx(tx)
|
||||
tx.vin[0].scriptSig = CScript([OP_CHECKSEQUENCEVERIFY] + list(CScript(tx.vin[0].scriptSig)))
|
||||
tx.rehash()
|
||||
@@ -136,7 +136,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
|
||||
locktime = relative_locktime(sdf, srhb, stf, srlb)
|
||||
tx = self.create_self_transfer_from_utxo(bip68inputs[i])
|
||||
tx.nVersion = txversion
|
||||
tx.version = txversion
|
||||
tx.vin[0].nSequence = locktime + locktime_delta
|
||||
self.miniwallet.sign_tx(tx)
|
||||
txs.append({'tx': tx, 'sdf': sdf, 'stf': stf})
|
||||
@@ -154,7 +154,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME + locktime_delta
|
||||
else: # vary nSequence instead, OP_CSV is fixed
|
||||
tx.vin[0].nSequence = locktime + locktime_delta
|
||||
tx.nVersion = txversion
|
||||
tx.version = txversion
|
||||
self.miniwallet.sign_tx(tx)
|
||||
if varyOP_CSV:
|
||||
tx.vin[0].scriptSig = CScript([locktime, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig)))
|
||||
@@ -257,10 +257,10 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
# BIP113 test transaction will be modified before each use to put in appropriate block time
|
||||
bip113tx_v1 = self.create_self_transfer_from_utxo(bip113input)
|
||||
bip113tx_v1.vin[0].nSequence = 0xFFFFFFFE
|
||||
bip113tx_v1.nVersion = 1
|
||||
bip113tx_v1.version = 1
|
||||
bip113tx_v2 = self.create_self_transfer_from_utxo(bip113input)
|
||||
bip113tx_v2.vin[0].nSequence = 0xFFFFFFFE
|
||||
bip113tx_v2.nVersion = 2
|
||||
bip113tx_v2.version = 2
|
||||
|
||||
# For BIP68 test all 16 relative sequence locktimes
|
||||
bip68txs_v1 = self.create_bip68txs(bip68inputs, 1)
|
||||
|
||||
@@ -1408,10 +1408,10 @@ class TaprootTest(BitcoinTestFramework):
|
||||
|
||||
left = done
|
||||
while left:
|
||||
# Construct CTransaction with random nVersion, nLocktime
|
||||
# Construct CTransaction with random version, nLocktime
|
||||
tx = CTransaction()
|
||||
tx.nVersion = random.choice([1, 2, random.randint(-0x80000000, 0x7fffffff)])
|
||||
min_sequence = (tx.nVersion != 1 and tx.nVersion != 0) * 0x80000000 # The minimum sequence number to disable relative locktime
|
||||
tx.version = random.choice([1, 2, random.getrandbits(32)])
|
||||
min_sequence = (tx.version != 1 and tx.version != 0) * 0x80000000 # The minimum sequence number to disable relative locktime
|
||||
if random.choice([True, False]):
|
||||
tx.nLockTime = random.randrange(LOCKTIME_THRESHOLD, self.lastblocktime - 7200) # all absolute locktimes in the past
|
||||
else:
|
||||
@@ -1502,8 +1502,8 @@ class TaprootTest(BitcoinTestFramework):
|
||||
is_standard_tx = (
|
||||
fail_input is None # Must be valid to be standard
|
||||
and (all(utxo.spender.is_standard for utxo in input_utxos)) # All inputs must be standard
|
||||
and tx.nVersion >= 1 # The tx version must be standard
|
||||
and tx.nVersion <= 2)
|
||||
and tx.version >= 1 # The tx version must be standard
|
||||
and tx.version <= 2)
|
||||
tx.rehash()
|
||||
msg = ','.join(utxo.spender.comment + ("*" if n == fail_input else "") for n, utxo in enumerate(input_utxos))
|
||||
if is_standard_tx:
|
||||
@@ -1530,7 +1530,7 @@ class TaprootTest(BitcoinTestFramework):
|
||||
# Deterministically mine coins to OP_TRUE in block 1
|
||||
assert_equal(self.nodes[0].getblockcount(), 0)
|
||||
coinbase = CTransaction()
|
||||
coinbase.nVersion = 1
|
||||
coinbase.version = 1
|
||||
coinbase.vin = [CTxIn(COutPoint(0, 0xffffffff), CScript([OP_1, OP_1]), SEQUENCE_FINAL)]
|
||||
coinbase.vout = [CTxOut(5000000000, CScript([OP_1]))]
|
||||
coinbase.nLockTime = 0
|
||||
@@ -1622,7 +1622,7 @@ class TaprootTest(BitcoinTestFramework):
|
||||
for i, spk in enumerate(old_spks + tap_spks):
|
||||
val = 42000000 * (i + 7)
|
||||
tx = CTransaction()
|
||||
tx.nVersion = 1
|
||||
tx.version = 1
|
||||
tx.vin = [CTxIn(COutPoint(lasttxid, i & 1), CScript([]), SEQUENCE_FINAL)]
|
||||
tx.vout = [CTxOut(val, spk), CTxOut(amount - val, CScript([OP_1]))]
|
||||
if i & 1:
|
||||
@@ -1679,7 +1679,7 @@ class TaprootTest(BitcoinTestFramework):
|
||||
|
||||
# Construct a deterministic transaction spending all outputs created above.
|
||||
tx = CTransaction()
|
||||
tx.nVersion = 2
|
||||
tx.version = 2
|
||||
tx.vin = []
|
||||
inputs = []
|
||||
input_spks = [tap_spks[0], tap_spks[1], old_spks[0], tap_spks[2], tap_spks[5], old_spks[2], tap_spks[6], tap_spks[3], tap_spks[4]]
|
||||
|
||||
@@ -287,7 +287,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
||||
|
||||
self.log.info('Some nonstandard transactions')
|
||||
tx = tx_from_hex(raw_tx_reference)
|
||||
tx.nVersion = 4 # A version currently non-standard
|
||||
tx.version = 4 # A version currently non-standard
|
||||
self.check_mempool_result(
|
||||
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'version'}],
|
||||
rawtxs=[tx.serialize().hex()],
|
||||
|
||||
@@ -55,7 +55,7 @@ class MutatedBlocksTest(BitcoinTestFramework):
|
||||
# Create mutated version of the block by changing the transaction
|
||||
# version on the self-transfer.
|
||||
mutated_block = copy.deepcopy(block)
|
||||
mutated_block.vtx[1].nVersion = 4
|
||||
mutated_block.vtx[1].version = 4
|
||||
|
||||
# Announce the new block via a compact block through the honest relayer
|
||||
cmpctblock = HeaderAndShortIDs()
|
||||
|
||||
@@ -1164,7 +1164,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
if not self.wit.is_null():
|
||||
flags |= 1
|
||||
r = b""
|
||||
r += self.nVersion.to_bytes(4, "little", signed=True)
|
||||
r += self.version.to_bytes(4, "little")
|
||||
if flags:
|
||||
dummy = []
|
||||
r += ser_vector(dummy)
|
||||
@@ -1975,7 +1975,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
def serialize_with_bogus_witness(tx):
|
||||
flags = 3
|
||||
r = b""
|
||||
r += tx.nVersion.to_bytes(4, "little", signed=True)
|
||||
r += tx.version.to_bytes(4, "little")
|
||||
if flags:
|
||||
dummy = []
|
||||
r += ser_vector(dummy)
|
||||
|
||||
@@ -394,7 +394,7 @@ class RPCPackagesTest(BitcoinTestFramework):
|
||||
peer = node.add_p2p_connection(P2PTxInvStore())
|
||||
txs = self.wallet.create_self_transfer_chain(chain_length=2)
|
||||
bad_child = tx_from_hex(txs[1]["hex"])
|
||||
bad_child.nVersion = -1
|
||||
bad_child.version = 0xffffffff
|
||||
hex_partial_acceptance = [txs[0]["hex"], bad_child.serialize().hex()]
|
||||
res = node.submitpackage(hex_partial_acceptance)
|
||||
assert_equal(res["package_msg"], "transaction failed")
|
||||
|
||||
@@ -463,20 +463,34 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
self.log.info("Test transaction version numbers")
|
||||
|
||||
# Test the minimum transaction version number that fits in a signed 32-bit integer.
|
||||
# As transaction version is unsigned, this should convert to its unsigned equivalent.
|
||||
# As transaction version is serialized unsigned, this should convert to its unsigned equivalent.
|
||||
tx = CTransaction()
|
||||
tx.nVersion = -0x80000000
|
||||
tx.version = 0x80000000
|
||||
rawtx = tx.serialize().hex()
|
||||
decrawtx = self.nodes[0].decoderawtransaction(rawtx)
|
||||
assert_equal(decrawtx['version'], 0x80000000)
|
||||
|
||||
# Test the maximum transaction version number that fits in a signed 32-bit integer.
|
||||
tx = CTransaction()
|
||||
tx.nVersion = 0x7fffffff
|
||||
tx.version = 0x7fffffff
|
||||
rawtx = tx.serialize().hex()
|
||||
decrawtx = self.nodes[0].decoderawtransaction(rawtx)
|
||||
assert_equal(decrawtx['version'], 0x7fffffff)
|
||||
|
||||
# Test the minimum transaction version number that fits in an unsigned 32-bit integer.
|
||||
tx = CTransaction()
|
||||
tx.version = 0
|
||||
rawtx = tx.serialize().hex()
|
||||
decrawtx = self.nodes[0].decoderawtransaction(rawtx)
|
||||
assert_equal(decrawtx['version'], 0)
|
||||
|
||||
# Test the maximum transaction version number that fits in an unsigned 32-bit integer.
|
||||
tx = CTransaction()
|
||||
tx.version = 0xffffffff
|
||||
rawtx = tx.serialize().hex()
|
||||
decrawtx = self.nodes[0].decoderawtransaction(rawtx)
|
||||
assert_equal(decrawtx['version'], 0xffffffff)
|
||||
|
||||
def raw_multisig_transaction_legacy_tests(self):
|
||||
self.log.info("Test raw multisig transactions (legacy)")
|
||||
# The traditional multisig workflow does not work with descriptor wallets so these are legacy only.
|
||||
|
||||
@@ -560,12 +560,12 @@ class CTxWitness:
|
||||
|
||||
|
||||
class CTransaction:
|
||||
__slots__ = ("hash", "nLockTime", "nVersion", "sha256", "vin", "vout",
|
||||
__slots__ = ("hash", "nLockTime", "version", "sha256", "vin", "vout",
|
||||
"wit")
|
||||
|
||||
def __init__(self, tx=None):
|
||||
if tx is None:
|
||||
self.nVersion = 2
|
||||
self.version = 2
|
||||
self.vin = []
|
||||
self.vout = []
|
||||
self.wit = CTxWitness()
|
||||
@@ -573,7 +573,7 @@ class CTransaction:
|
||||
self.sha256 = None
|
||||
self.hash = None
|
||||
else:
|
||||
self.nVersion = tx.nVersion
|
||||
self.version = tx.version
|
||||
self.vin = copy.deepcopy(tx.vin)
|
||||
self.vout = copy.deepcopy(tx.vout)
|
||||
self.nLockTime = tx.nLockTime
|
||||
@@ -582,7 +582,7 @@ class CTransaction:
|
||||
self.wit = copy.deepcopy(tx.wit)
|
||||
|
||||
def deserialize(self, f):
|
||||
self.nVersion = int.from_bytes(f.read(4), "little", signed=True)
|
||||
self.version = int.from_bytes(f.read(4), "little")
|
||||
self.vin = deser_vector(f, CTxIn)
|
||||
flags = 0
|
||||
if len(self.vin) == 0:
|
||||
@@ -605,7 +605,7 @@ class CTransaction:
|
||||
|
||||
def serialize_without_witness(self):
|
||||
r = b""
|
||||
r += self.nVersion.to_bytes(4, "little", signed=True)
|
||||
r += self.version.to_bytes(4, "little")
|
||||
r += ser_vector(self.vin)
|
||||
r += ser_vector(self.vout)
|
||||
r += self.nLockTime.to_bytes(4, "little")
|
||||
@@ -617,7 +617,7 @@ class CTransaction:
|
||||
if not self.wit.is_null():
|
||||
flags |= 1
|
||||
r = b""
|
||||
r += self.nVersion.to_bytes(4, "little", signed=True)
|
||||
r += self.version.to_bytes(4, "little")
|
||||
if flags:
|
||||
dummy = []
|
||||
r += ser_vector(dummy)
|
||||
@@ -677,8 +677,8 @@ class CTransaction:
|
||||
return math.ceil(self.get_weight() / WITNESS_SCALE_FACTOR)
|
||||
|
||||
def __repr__(self):
|
||||
return "CTransaction(nVersion=%i vin=%s vout=%s wit=%s nLockTime=%i)" \
|
||||
% (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime)
|
||||
return "CTransaction(version=%i vin=%s vout=%s wit=%s nLockTime=%i)" \
|
||||
% (self.version, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime)
|
||||
|
||||
|
||||
class CBlockHeader:
|
||||
|
||||
@@ -738,7 +738,7 @@ def SegwitV0SignatureMsg(script, txTo, inIdx, hashtype, amount):
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
|
||||
ss = bytes()
|
||||
ss += txTo.nVersion.to_bytes(4, "little", signed=True)
|
||||
ss += txTo.version.to_bytes(4, "little")
|
||||
ss += ser_uint256(hashPrevouts)
|
||||
ss += ser_uint256(hashSequence)
|
||||
ss += txTo.vin[inIdx].prevout.serialize()
|
||||
@@ -817,7 +817,7 @@ def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index = 0, scriptpat
|
||||
in_type = hash_type & SIGHASH_ANYONECANPAY
|
||||
spk = spent_utxos[input_index].scriptPubKey
|
||||
ss = bytes([0, hash_type]) # epoch, hash_type
|
||||
ss += txTo.nVersion.to_bytes(4, "little", signed=True)
|
||||
ss += txTo.version.to_bytes(4, "little")
|
||||
ss += txTo.nLockTime.to_bytes(4, "little")
|
||||
if in_type != SIGHASH_ANYONECANPAY:
|
||||
ss += BIP341_sha_prevouts(txTo)
|
||||
|
||||
@@ -329,7 +329,7 @@ class MiniWallet:
|
||||
tx = CTransaction()
|
||||
tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']), nSequence=seq) for utxo_to_spend, seq in zip(utxos_to_spend, sequence)]
|
||||
tx.vout = [CTxOut(amount_per_output, bytearray(self._scriptPubKey)) for _ in range(num_outputs)]
|
||||
tx.nVersion = version
|
||||
tx.version = version
|
||||
tx.nLockTime = locktime
|
||||
|
||||
self.sign_tx(tx)
|
||||
|
||||
@@ -111,7 +111,7 @@ class CreateTxWalletTest(BitcoinTestFramework):
|
||||
test_wallet.unloadwallet()
|
||||
|
||||
def test_version3(self):
|
||||
self.log.info('Check wallet does not create transactions with nVersion=3 yet')
|
||||
self.log.info('Check wallet does not create transactions with version=3 yet')
|
||||
wallet_rpc = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
|
||||
|
||||
self.nodes[0].createwallet("v3")
|
||||
@@ -124,7 +124,7 @@ class CreateTxWalletTest(BitcoinTestFramework):
|
||||
# While v3 transactions are standard, the CURRENT_VERSION is 2.
|
||||
# This test can be removed if CURRENT_VERSION is changed, and replaced with tests that the
|
||||
# wallet handles v3 rules properly.
|
||||
assert_equal(tx_current_version.nVersion, 2)
|
||||
assert_equal(tx_current_version.version, 2)
|
||||
wallet_v3.unloadwallet()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user