Skip to content

Commit 1473f69

Browse files
committed
Merge bitcoin/bitcoin#32421: test: refactor: overhaul (w)txid determination for CTransaction objects
4ef6253 test: avoid unneeded (w)txid hex -> integer conversions (Sebastian Falbesoner) 472f377 scripted-diff: test: rename CTransaction `.getwtxid()` -> `wtxid_hex` for consistency (Sebastian Falbesoner) 81af433 test: rename CTransaction `.sha256` -> `.txid_int` for consistency (Sebastian Falbesoner) ce83924 test: rename CTransaction `.rehash()`/`.hash` -> `.txid_hex` for consistency (Sebastian Falbesoner) e9cdaef test: introduce and use CTransaction `.wtxid_int` property (Sebastian Falbesoner) 9b3dce2 test: remove bare CTransaction `.rehash()`/`.calc_sha256()` calls (Sebastian Falbesoner) a2724e3 test: remove txid caching in CTransaction class (Sebastian Falbesoner) Pull request description: In the functional test framework, determining a (w)txid for a `CTransaction` instance is currently rather confusing and footgunny due to inconsistent naming/interfaces (see table below) and statefulness involved. This PR aims to improve that by: * removing the (w)txid caching mechanism, in order to avoid the need to call additional rehashing functions (`.rehash()`/`.calculate_sha256()`, see first two commits and bitcoin/bitcoin#32050 (comment)). This change in theory decreases the performance, as the involved serialization and hashing involved might be called more often than previously, but I couldn't find a functional test where this leads to a measurable run-time increase on my machine. * introduce consistent naming that shows the type of the returned txid, i.e. hex string vs. test-framework-internal representation [currently integers] (see remaining commits) Summary table showing (w)txid determaination before/after this PR: | Task | master | PR | |:-----------------------|:-----------------------|:-------------| | get TXID (hex string) | `.rehash()` / `.hash`[1] | `.txid_hex` | | get TXID (integer) | `.sha256`[1] | `.txid_int` | | get WTXID (hex string) | `.getwtxid()` | `.wtxid_hex` | | get WTXID (integer) | `.calc_sha256(True)` | `.wtxid_int` | Unfortunately, most renames can't be done with a scripted-diff, as the property names (`.hash`, `.sha256`) are also used for blocks and other message types. The PR is rather invasive and touches a lot of files, but I think it's worth to do it, also to make life easier for new contributors. Future tasks like e.g. doing the same overhaul for block (header) objects or getting rid of the integer representation (see bitcoin/bitcoin#32050) become easier should become easier after this one. [1] = returned value might be out-of-date, if rehashing function wasn't called after modification ACKs for top commit: maflcko: re-ACK 4ef6253 🏈 achow101: ACK 4ef6253 marcofleon: code review ACK 4ef6253 Tree-SHA512: 4b472c31d169966b6f6878911a8404d25bf3e503b6e8ef30f36a7415d21ad4bc1265083af2d3ead6edfcd9fac9ccb0a8be57e1b0739ad431b836413070d7d583
2 parents 7c0cfce + 4ef6253 commit 1473f69

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+333
-499
lines changed

contrib/signet/miner

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ def signet_txs(block, challenge):
4040
txs[0].vout[-1].scriptPubKey += CScriptOp.encode_op_pushdata(SIGNET_HEADER)
4141
hashes = []
4242
for tx in txs:
43-
tx.rehash()
44-
hashes.append(ser_uint256(tx.sha256))
43+
hashes.append(ser_uint256(tx.txid_int))
4544
mroot = block.get_merkle_root(hashes)
4645

4746
sd = b""
@@ -55,12 +54,11 @@ def signet_txs(block, challenge):
5554
to_spend.nLockTime = 0
5655
to_spend.vin = [CTxIn(COutPoint(0, 0xFFFFFFFF), b"\x00" + CScriptOp.encode_op_pushdata(sd), 0)]
5756
to_spend.vout = [CTxOut(0, challenge)]
58-
to_spend.rehash()
5957

6058
spend = CTransaction()
6159
spend.version = 0
6260
spend.nLockTime = 0
63-
spend.vin = [CTxIn(COutPoint(to_spend.sha256, 0), b"", 0)]
61+
spend.vin = [CTxIn(COutPoint(to_spend.txid_int, 0), b"", 0)]
6462
spend.vout = [CTxOut(0, b"\x6a")]
6563

6664
return spend, to_spend
@@ -88,7 +86,6 @@ def finish_block(block, signet_solution, grind_cmd):
8886
pass # Don't need to add a signet commitment if there's no signet signature needed
8987
else:
9088
block.vtx[0].vout[-1].scriptPubKey += CScriptOp.encode_op_pushdata(SIGNET_HEADER + signet_solution)
91-
block.vtx[0].rehash()
9289
block.hashMerkleRoot = block.calc_merkle_root()
9390
if grind_cmd is None:
9491
block.solve()
@@ -111,7 +108,6 @@ def new_block(tmpl, reward_spk, *, blocktime=None, poolid=None):
111108
cbtx.vin = [CTxIn(COutPoint(0, 0xffffffff), scriptSig, MAX_SEQUENCE_NONFINAL)]
112109
cbtx.vout = [CTxOut(tmpl["coinbasevalue"], reward_spk)]
113110
cbtx.vin[0].nSequence = 2**32-2
114-
cbtx.rehash()
115111

116112
block = CBlock()
117113
block.nVersion = tmpl["version"]
@@ -130,7 +126,6 @@ def new_block(tmpl, reward_spk, *, blocktime=None, poolid=None):
130126
block.vtx[0].wit.vtxinwit = [cbwit]
131127
block.vtx[0].vout.append(CTxOut(0, bytes(get_witness_script(witroot, witnonce))))
132128

133-
block.vtx[0].rehash()
134129
block.hashMerkleRoot = block.calc_merkle_root()
135130

136131
return block

test/functional/data/invalid_txs.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class BadTxTemplate:
8383
def __init__(self, *, spend_tx=None, spend_block=None):
8484
self.spend_tx = spend_block.vtx[0] if spend_block else spend_tx
8585
self.spend_avail = sum(o.nValue for o in self.spend_tx.vout)
86-
self.valid_txin = CTxIn(COutPoint(self.spend_tx.sha256, 0), b"", SEQUENCE_FINAL)
86+
self.valid_txin = CTxIn(COutPoint(self.spend_tx.txid_int, 0), b"", SEQUENCE_FINAL)
8787

8888
@abc.abstractmethod
8989
def get_tx(self, *args, **kwargs):
@@ -98,7 +98,6 @@ class OutputMissing(BadTxTemplate):
9898
def get_tx(self):
9999
tx = CTransaction()
100100
tx.vin.append(self.valid_txin)
101-
tx.calc_sha256()
102101
return tx
103102

104103

@@ -113,7 +112,6 @@ class InputMissing(BadTxTemplate):
113112
# rather than the input count check.
114113
def get_tx(self):
115114
tx = CTransaction()
116-
tx.calc_sha256()
117115
return tx
118116

119117

@@ -130,7 +128,6 @@ def get_tx(self):
130128
tx.vout.append(CTxOut(0, CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 2)))))
131129
assert len(tx.serialize_without_witness()) == 64
132130
assert MIN_STANDARD_TX_NONWITNESS_SIZE - 1 == 64
133-
tx.calc_sha256()
134131
return tx
135132

136133

@@ -147,9 +144,8 @@ def get_tx(self):
147144
bad_idx = num_indices + 100
148145

149146
tx = CTransaction()
150-
tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256, bad_idx), b"", SEQUENCE_FINAL))
147+
tx.vin.append(CTxIn(COutPoint(self.spend_tx.txid_int, bad_idx), b"", SEQUENCE_FINAL))
151148
tx.vout.append(CTxOut(0, basic_p2sh))
152-
tx.calc_sha256()
153149
return tx
154150

155151

@@ -162,7 +158,6 @@ def get_tx(self):
162158
tx.vin.append(self.valid_txin)
163159
tx.vin.append(self.valid_txin)
164160
tx.vout.append(CTxOut(1, basic_p2sh))
165-
tx.calc_sha256()
166161
return tx
167162

168163

@@ -175,7 +170,6 @@ def get_tx(self):
175170
tx.vin.append(self.valid_txin)
176171
tx.vin.append(CTxIn(COutPoint(hash=0, n=0xffffffff)))
177172
tx.vout.append(CTxOut(1, basic_p2sh))
178-
tx.calc_sha256()
179173
return tx
180174

181175

@@ -187,10 +181,9 @@ class NonexistentInput(BadTxTemplate):
187181

188182
def get_tx(self):
189183
tx = CTransaction()
190-
tx.vin.append(CTxIn(COutPoint(self.spend_tx.sha256 + 1, 0), b"", SEQUENCE_FINAL))
184+
tx.vin.append(CTxIn(COutPoint(self.spend_tx.txid_int + 1, 0), b"", SEQUENCE_FINAL))
191185
tx.vin.append(self.valid_txin)
192186
tx.vout.append(CTxOut(1, basic_p2sh))
193-
tx.calc_sha256()
194187
return tx
195188

196189

@@ -226,7 +219,6 @@ class CreateSumTooLarge(BadTxTemplate):
226219
def get_tx(self):
227220
tx = create_tx_with_script(self.spend_tx, 0, amount=MAX_MONEY)
228221
tx.vout = [tx.vout[0]] * 2
229-
tx.calc_sha256()
230222
return tx
231223

232224

@@ -274,7 +266,6 @@ def get_tx(self):
274266
vin.scriptSig = CScript([opcode])
275267
tx.vin.append(vin)
276268
tx.vout.append(CTxOut(1, basic_p2sh))
277-
tx.calc_sha256()
278269
return tx
279270

280271
return type('DisabledOpcode_' + str(opcode), (BadTxTemplate,), {

test/functional/feature_assumeutxo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ def check_tx_counts(final: bool) -> None:
605605
privkey = n0.get_deterministic_priv_key().key
606606
raw_tx = n1.createrawtransaction([prevout], {getnewdestination()[2]: 24.99})
607607
signed_tx = n1.signrawtransactionwithkey(raw_tx, [privkey], [prevout])['hex']
608-
signed_txid = tx_from_hex(signed_tx).rehash()
608+
signed_txid = tx_from_hex(signed_tx).txid_hex
609609

610610
assert n1.gettxout(prev_tx['txid'], 0) is not None
611611
n1.sendrawtransaction(signed_tx)

test/functional/feature_assumevalid.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,8 @@ def run_test(self):
117117

118118
# Create a transaction spending the coinbase output with an invalid (null) signature
119119
tx = CTransaction()
120-
tx.vin.append(CTxIn(COutPoint(self.block1.vtx[0].sha256, 0), scriptSig=b""))
120+
tx.vin.append(CTxIn(COutPoint(self.block1.vtx[0].txid_int, 0), scriptSig=b""))
121121
tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE])))
122-
tx.calc_sha256()
123122

124123
block102 = create_block(self.tip, create_coinbase(height), self.block_time, txlist=[tx])
125124
self.block_time += 1

test/functional/feature_bip68_sequence.py

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ def test_disable_flag(self):
110110
tx2.wit.vtxinwit = [CTxInWitness()]
111111
tx2.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
112112
tx2.vout = [CTxOut(int(value - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
113-
tx2.rehash()
114113

115114
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx2.serialize().hex())
116115

@@ -220,17 +219,15 @@ def test_sequence_lock_unconfirmed_inputs(self):
220219
# Create a mempool tx.
221220
self.wallet.rescan_utxos()
222221
tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0])["tx"]
223-
tx1.rehash()
224222

225223
# Anyone-can-spend mempool tx.
226224
# Sequence lock of 0 should pass.
227225
tx2 = CTransaction()
228226
tx2.version = 2
229-
tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
227+
tx2.vin = [CTxIn(COutPoint(tx1.txid_int, 0), nSequence=0)]
230228
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
231229
self.wallet.sign_tx(tx=tx2)
232230
tx2_raw = tx2.serialize().hex()
233-
tx2.rehash()
234231

235232
self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2_raw)
236233

@@ -244,13 +241,12 @@ def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock):
244241

245242
tx = CTransaction()
246243
tx.version = 2
247-
tx.vin = [CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value)]
244+
tx.vin = [CTxIn(COutPoint(orig_tx.txid_int, 0), nSequence=sequence_value)]
248245
tx.wit.vtxinwit = [CTxInWitness()]
249246
tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
250247
tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
251-
tx.rehash()
252248

253-
if (orig_tx.hash in node.getrawmempool()):
249+
if (orig_tx.txid_hex in node.getrawmempool()):
254250
# sendrawtransaction should fail if the tx is in the mempool
255251
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=node, tx_hex=tx.serialize().hex())
256252
else:
@@ -264,43 +260,43 @@ def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock):
264260

265261
# Now mine some blocks, but make sure tx2 doesn't get mined.
266262
# Use prioritisetransaction to lower the effective feerate to 0
267-
self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=int(-self.relayfee*COIN))
263+
self.nodes[0].prioritisetransaction(txid=tx2.txid_hex, fee_delta=int(-self.relayfee*COIN))
268264
cur_time = int(time.time())
269265
for _ in range(10):
270266
self.nodes[0].setmocktime(cur_time + 600)
271267
self.generate(self.wallet, 1, sync_fun=self.no_op)
272268
cur_time += 600
273269

274-
assert tx2.hash in self.nodes[0].getrawmempool()
270+
assert tx2.txid_hex in self.nodes[0].getrawmempool()
275271

276272
test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=True)
277273
test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
278274

279275
# Mine tx2, and then try again
280-
self.nodes[0].prioritisetransaction(txid=tx2.hash, fee_delta=int(self.relayfee*COIN))
276+
self.nodes[0].prioritisetransaction(txid=tx2.txid_hex, fee_delta=int(self.relayfee*COIN))
281277

282278
# Advance the time on the node so that we can test timelocks
283279
self.nodes[0].setmocktime(cur_time+600)
284280
# Save block template now to use for the reorg later
285281
tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
286282
self.generate(self.nodes[0], 1)
287-
assert tx2.hash not in self.nodes[0].getrawmempool()
283+
assert tx2.txid_hex not in self.nodes[0].getrawmempool()
288284

289285
# Now that tx2 is not in the mempool, a sequence locked spend should
290286
# succeed
291287
tx3 = test_nonzero_locks(tx2, self.nodes[0], self.relayfee, use_height_lock=False)
292-
assert tx3.hash in self.nodes[0].getrawmempool()
288+
assert tx3.txid_hex in self.nodes[0].getrawmempool()
293289

294290
self.generate(self.nodes[0], 1)
295-
assert tx3.hash not in self.nodes[0].getrawmempool()
291+
assert tx3.txid_hex not in self.nodes[0].getrawmempool()
296292

297293
# One more test, this time using height locks
298294
tx4 = test_nonzero_locks(tx3, self.nodes[0], self.relayfee, use_height_lock=True)
299-
assert tx4.hash in self.nodes[0].getrawmempool()
295+
assert tx4.txid_hex in self.nodes[0].getrawmempool()
300296

301297
# Now try combining confirmed and unconfirmed inputs
302298
tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True)
303-
assert tx5.hash not in self.nodes[0].getrawmempool()
299+
assert tx5.txid_hex not in self.nodes[0].getrawmempool()
304300

305301
utxo = self.wallet.get_utxo()
306302
tx5.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=1))
@@ -319,8 +315,8 @@ def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock):
319315
# If we invalidate the tip, tx3 should get added to the mempool, causing
320316
# tx4 to be removed (fails sequence-lock).
321317
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
322-
assert tx4.hash not in self.nodes[0].getrawmempool()
323-
assert tx3.hash in self.nodes[0].getrawmempool()
318+
assert tx4.txid_hex not in self.nodes[0].getrawmempool()
319+
assert tx3.txid_hex in self.nodes[0].getrawmempool()
324320

325321
# Now mine 2 empty blocks to reorg out the current tip (labeled tip-1 in
326322
# diagram above).
@@ -337,8 +333,8 @@ def test_nonzero_locks(orig_tx, node, relayfee, use_height_lock):
337333
cur_time += 1
338334

339335
mempool = self.nodes[0].getrawmempool()
340-
assert tx3.hash not in mempool
341-
assert tx2.hash in mempool
336+
assert tx3.txid_hex not in mempool
337+
assert tx2.txid_hex in mempool
342338

343339
# Reset the chain and get rid of the mocktimed-blocks
344340
self.nodes[0].setmocktime(0)
@@ -353,19 +349,17 @@ def test_bip68_not_consensus(self):
353349
assert not softfork_active(self.nodes[0], 'csv')
354350

355351
tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0])["tx"]
356-
tx1.rehash()
357352

358353
# Make an anyone-can-spend transaction
359354
tx2 = CTransaction()
360355
tx2.version = 1
361-
tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
356+
tx2.vin = [CTxIn(COutPoint(tx1.txid_int, 0), nSequence=0)]
362357
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
363358

364359
# sign tx2
365360
self.wallet.sign_tx(tx=tx2)
366361
tx2_raw = tx2.serialize().hex()
367362
tx2 = tx_from_hex(tx2_raw)
368-
tx2.rehash()
369363

370364
self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2_raw)
371365

@@ -374,11 +368,10 @@ def test_bip68_not_consensus(self):
374368

375369
tx3 = CTransaction()
376370
tx3.version = 2
377-
tx3.vin = [CTxIn(COutPoint(tx2.sha256, 0), nSequence=sequence_value)]
371+
tx3.vin = [CTxIn(COutPoint(tx2.txid_int, 0), nSequence=sequence_value)]
378372
tx3.wit.vtxinwit = [CTxInWitness()]
379373
tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
380374
tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
381-
tx3.rehash()
382375

383376
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx3.serialize().hex())
384377

0 commit comments

Comments
 (0)