Skip to content

Commit 4a020ca

Browse files
committed
Merge bitcoin/bitcoin#29401: test: Remove struct.pack from almost all places
fa52e13 test: Remove struct.pack from almost all places (MarcoFalke) fa826db scripted-diff: test: Use int.to_bytes over struct packing (MarcoFalke) faf2a97 test: Use int.to_bytes over struct packing (MarcoFalke) faf3cd6 test: Normalize struct.pack format (MarcoFalke) Pull request description: `struct.pack` has many issues: * The format string consists of characters that may be confusing and may need to be looked up in the documentation, as opposed to using easy to understand self-documenting code. This lead to many test bugs, which weren't hit, which is fine, but still confusing. Ref: bitcoin/bitcoin#29400, bitcoin/bitcoin#29399, bitcoin/bitcoin#29363, fa3886b, ... Fix all issues by using the built-in `int` helpers `to_bytes` via a scripted diff. Review notes: * For `struct.pack` and `int.to_bytes` the error behavior is the same, although the error messages are not identical. * Two `struct.pack` remain. One for float serialization in a C++ code comment, and one for native serialization. ACKs for top commit: achow101: ACK fa52e13 rkrux: tACK [fa52e13](bitcoin/bitcoin@fa52e13) theStack: Code-review ACK fa52e13 Tree-SHA512: ee80d935b68ae43d1654b047e84ceb80abbd20306df35cca848b3f7874634b518560ddcbc7e714e2e7a19241e153dee64556dc4701287ae811e26e4f8c57fe3e
2 parents 1040a1f + fa52e13 commit 4a020ca

File tree

9 files changed

+41
-48
lines changed

9 files changed

+41
-48
lines changed

contrib/seeds/generate-seeds.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929

3030
from base64 import b32decode
3131
from enum import Enum
32-
import struct
3332
import sys
3433
import os
3534
import re
@@ -115,24 +114,24 @@ def parse_spec(s):
115114
def ser_compact_size(l):
116115
r = b""
117116
if l < 253:
118-
r = struct.pack("B", l)
117+
r = l.to_bytes(1, "little")
119118
elif l < 0x10000:
120-
r = struct.pack("<BH", 253, l)
119+
r = (253).to_bytes(1, "little") + l.to_bytes(2, "little")
121120
elif l < 0x100000000:
122-
r = struct.pack("<BI", 254, l)
121+
r = (254).to_bytes(1, "little") + l.to_bytes(4, "little")
123122
else:
124-
r = struct.pack("<BQ", 255, l)
123+
r = (255).to_bytes(1, "little") + l.to_bytes(8, "little")
125124
return r
126125

127126
def bip155_serialize(spec):
128127
'''
129128
Serialize (networkID, addr, port) tuple to BIP155 binary format.
130129
'''
131130
r = b""
132-
r += struct.pack('B', spec[0].value)
131+
r += spec[0].value.to_bytes(1, "little")
133132
r += ser_compact_size(len(spec[1]))
134133
r += spec[1]
135-
r += struct.pack('>H', spec[2])
134+
r += spec[2].to_bytes(2, "big")
136135
return r
137136

138137
def process_nodes(g, f, structname):

contrib/signet/miner

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ def signet_txs(block, challenge):
5252
mroot = block.get_merkle_root(hashes)
5353

5454
sd = b""
55-
sd += struct.pack("<i", block.nVersion)
55+
sd += block.nVersion.to_bytes(4, "little", signed=True)
5656
sd += ser_uint256(block.hashPrevBlock)
5757
sd += ser_uint256(mroot)
58-
sd += struct.pack("<I", block.nTime)
58+
sd += block.nTime.to_bytes(4, "little")
5959

6060
to_spend = CTransaction()
6161
to_spend.nVersion = 0

test/functional/feature_addrman.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import os
88
import re
9-
import struct
109

1110
from test_framework.messages import ser_uint256, hash256, MAGIC_BYTES
1211
from test_framework.netutil import ADDRMAN_NEW_BUCKET_COUNT, ADDRMAN_TRIED_BUCKET_COUNT, ADDRMAN_BUCKET_SIZE
@@ -28,15 +27,15 @@ def serialize_addrman(
2827
tried = []
2928
INCOMPATIBILITY_BASE = 32
3029
r = MAGIC_BYTES[net_magic]
31-
r += struct.pack("B", format)
32-
r += struct.pack("B", INCOMPATIBILITY_BASE + lowest_compatible)
30+
r += format.to_bytes(1, "little")
31+
r += (INCOMPATIBILITY_BASE + lowest_compatible).to_bytes(1, "little")
3332
r += ser_uint256(bucket_key)
34-
r += struct.pack("<i", len_new or len(new))
35-
r += struct.pack("<i", len_tried or len(tried))
33+
r += (len_new or len(new)).to_bytes(4, "little", signed=True)
34+
r += (len_tried or len(tried)).to_bytes(4, "little", signed=True)
3635
ADDRMAN_NEW_BUCKET_COUNT = 1 << 10
37-
r += struct.pack("<i", ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30))
36+
r += (ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30)).to_bytes(4, "little", signed=True)
3837
for _ in range(ADDRMAN_NEW_BUCKET_COUNT):
39-
r += struct.pack("<i", 0)
38+
r += (0).to_bytes(4, "little", signed=True)
4039
checksum = hash256(r)
4140
r += mock_checksum or checksum
4241
return r

test/functional/feature_block.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test block processing."""
66
import copy
7-
import struct
87
import time
98

109
from test_framework.blocktools import (
@@ -67,7 +66,7 @@ def initialize(self, base_block):
6766
def serialize(self, with_witness=False):
6867
r = b""
6968
r += super(CBlock, self).serialize()
70-
r += struct.pack("<BQ", 255, len(self.vtx))
69+
r += (255).to_bytes(1, "little") + len(self.vtx).to_bytes(8, "little")
7170
for tx in self.vtx:
7271
if with_witness:
7372
r += tx.serialize_with_witness()

test/functional/p2p_invalid_messages.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"""Test node responses to invalid network messages."""
66

77
import random
8-
import struct
98
import time
109

1110
from test_framework.messages import (
@@ -233,7 +232,7 @@ def test_addrv2_too_long_address(self):
233232
'208d')) # port
234233

235234
def test_addrv2_unrecognized_network(self):
236-
now_hex = struct.pack('<I', int(time.time())).hex()
235+
now_hex = int(time.time()).to_bytes(4, "little").hex()
237236
self.test_addrv2('unrecognized network',
238237
[
239238
'received: addrv2 (25 bytes)',

test/functional/p2p_segwit.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"""Test segwit transactions and blocks on P2P network."""
66
from decimal import Decimal
77
import random
8-
import struct
98
import time
109

1110
from test_framework.blocktools import (
@@ -1165,16 +1164,16 @@ def serialize_with_witness(self):
11651164
if not self.wit.is_null():
11661165
flags |= 1
11671166
r = b""
1168-
r += struct.pack("<i", self.nVersion)
1167+
r += self.nVersion.to_bytes(4, "little", signed=True)
11691168
if flags:
11701169
dummy = []
11711170
r += ser_vector(dummy)
1172-
r += struct.pack("<B", flags)
1171+
r += flags.to_bytes(1, "little")
11731172
r += ser_vector(self.vin)
11741173
r += ser_vector(self.vout)
11751174
if flags & 1:
11761175
r += self.wit.serialize()
1177-
r += struct.pack("<I", self.nLockTime)
1176+
r += self.nLockTime.to_bytes(4, "little")
11781177
return r
11791178

11801179
tx2 = BrokenCTransaction()
@@ -1976,11 +1975,11 @@ def test_superfluous_witness(self):
19761975
def serialize_with_bogus_witness(tx):
19771976
flags = 3
19781977
r = b""
1979-
r += struct.pack("<i", tx.nVersion)
1978+
r += tx.nVersion.to_bytes(4, "little", signed=True)
19801979
if flags:
19811980
dummy = []
19821981
r += ser_vector(dummy)
1983-
r += struct.pack("<B", flags)
1982+
r += flags.to_bytes(1, "little")
19841983
r += ser_vector(tx.vin)
19851984
r += ser_vector(tx.vout)
19861985
if flags & 1:
@@ -1990,7 +1989,7 @@ def serialize_with_bogus_witness(tx):
19901989
for _ in range(len(tx.wit.vtxinwit), len(tx.vin)):
19911990
tx.wit.vtxinwit.append(CTxInWitness())
19921991
r += tx.wit.serialize()
1993-
r += struct.pack("<I", tx.nLockTime)
1992+
r += tx.nLockTime.to_bytes(4, "little")
19941993
return r
19951994

19961995
class msg_bogus_tx(msg_tx):

test/functional/test_framework/p2p.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ def build_message(self, message, is_decoy=False):
411411
tmsg = self.magic_bytes
412412
tmsg += msgtype
413413
tmsg += b"\x00" * (12 - len(msgtype))
414-
tmsg += struct.pack("<I", len(data))
414+
tmsg += len(data).to_bytes(4, "little")
415415
th = sha256(data)
416416
h = sha256(th)
417417
tmsg += h[:4]

test/functional/test_framework/script.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
"""
99

1010
from collections import namedtuple
11-
import struct
1211
import unittest
1312

1413
from .key import TaggedHash, tweak_add_pubkey, compute_xonly_pubkey
@@ -58,9 +57,9 @@ def encode_op_pushdata(d):
5857
elif len(d) <= 0xff:
5958
return b'\x4c' + bytes([len(d)]) + d # OP_PUSHDATA1
6059
elif len(d) <= 0xffff:
61-
return b'\x4d' + struct.pack(b'<H', len(d)) + d # OP_PUSHDATA2
60+
return b'\x4d' + len(d).to_bytes(2, "little") + d # OP_PUSHDATA2
6261
elif len(d) <= 0xffffffff:
63-
return b'\x4e' + struct.pack(b'<I', len(d)) + d # OP_PUSHDATA4
62+
return b'\x4e' + len(d).to_bytes(4, "little") + d # OP_PUSHDATA4
6463
else:
6564
raise ValueError("Data too long to encode in a PUSHDATA op")
6665

@@ -670,7 +669,7 @@ def LegacySignatureMsg(script, txTo, inIdx, hashtype):
670669
txtmp.vin.append(tmp)
671670

672671
s = txtmp.serialize_without_witness()
673-
s += struct.pack(b"<I", hashtype)
672+
s += hashtype.to_bytes(4, "little")
674673

675674
return (s, None)
676675

@@ -726,7 +725,7 @@ def SegwitV0SignatureMsg(script, txTo, inIdx, hashtype, amount):
726725
if (not (hashtype & SIGHASH_ANYONECANPAY) and (hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE):
727726
serialize_sequence = bytes()
728727
for i in txTo.vin:
729-
serialize_sequence += struct.pack("<I", i.nSequence)
728+
serialize_sequence += i.nSequence.to_bytes(4, "little")
730729
hashSequence = uint256_from_str(hash256(serialize_sequence))
731730

732731
if ((hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE):
@@ -739,16 +738,16 @@ def SegwitV0SignatureMsg(script, txTo, inIdx, hashtype, amount):
739738
hashOutputs = uint256_from_str(hash256(serialize_outputs))
740739

741740
ss = bytes()
742-
ss += struct.pack("<i", txTo.nVersion)
741+
ss += txTo.nVersion.to_bytes(4, "little", signed=True)
743742
ss += ser_uint256(hashPrevouts)
744743
ss += ser_uint256(hashSequence)
745744
ss += txTo.vin[inIdx].prevout.serialize()
746745
ss += ser_string(script)
747-
ss += struct.pack("<q", amount)
748-
ss += struct.pack("<I", txTo.vin[inIdx].nSequence)
746+
ss += amount.to_bytes(8, "little", signed=True)
747+
ss += txTo.vin[inIdx].nSequence.to_bytes(4, "little")
749748
ss += ser_uint256(hashOutputs)
750749
ss += txTo.nLockTime.to_bytes(4, "little")
751-
ss += struct.pack("<I", hashtype)
750+
ss += hashtype.to_bytes(4, "little")
752751
return ss
753752

754753
def SegwitV0SignatureHash(*args, **kwargs):
@@ -800,13 +799,13 @@ def BIP341_sha_prevouts(txTo):
800799
return sha256(b"".join(i.prevout.serialize() for i in txTo.vin))
801800

802801
def BIP341_sha_amounts(spent_utxos):
803-
return sha256(b"".join(struct.pack("<q", u.nValue) for u in spent_utxos))
802+
return sha256(b"".join(u.nValue.to_bytes(8, "little", signed=True) for u in spent_utxos))
804803

805804
def BIP341_sha_scriptpubkeys(spent_utxos):
806805
return sha256(b"".join(ser_string(u.scriptPubKey) for u in spent_utxos))
807806

808807
def BIP341_sha_sequences(txTo):
809-
return sha256(b"".join(struct.pack("<I", i.nSequence) for i in txTo.vin))
808+
return sha256(b"".join(i.nSequence.to_bytes(4, "little") for i in txTo.vin))
810809

811810
def BIP341_sha_outputs(txTo):
812811
return sha256(b"".join(o.serialize() for o in txTo.vout))
@@ -818,8 +817,8 @@ def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index = 0, scriptpat
818817
in_type = hash_type & SIGHASH_ANYONECANPAY
819818
spk = spent_utxos[input_index].scriptPubKey
820819
ss = bytes([0, hash_type]) # epoch, hash_type
821-
ss += struct.pack("<i", txTo.nVersion)
822-
ss += struct.pack("<I", txTo.nLockTime)
820+
ss += txTo.nVersion.to_bytes(4, "little", signed=True)
821+
ss += txTo.nLockTime.to_bytes(4, "little")
823822
if in_type != SIGHASH_ANYONECANPAY:
824823
ss += BIP341_sha_prevouts(txTo)
825824
ss += BIP341_sha_amounts(spent_utxos)
@@ -835,11 +834,11 @@ def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index = 0, scriptpat
835834
ss += bytes([spend_type])
836835
if in_type == SIGHASH_ANYONECANPAY:
837836
ss += txTo.vin[input_index].prevout.serialize()
838-
ss += struct.pack("<q", spent_utxos[input_index].nValue)
837+
ss += spent_utxos[input_index].nValue.to_bytes(8, "little", signed=True)
839838
ss += ser_string(spk)
840-
ss += struct.pack("<I", txTo.vin[input_index].nSequence)
839+
ss += txTo.vin[input_index].nSequence.to_bytes(4, "little")
841840
else:
842-
ss += struct.pack("<I", input_index)
841+
ss += input_index.to_bytes(4, "little")
843842
if (spend_type & 1):
844843
ss += sha256(ser_string(annex))
845844
if out_type == SIGHASH_SINGLE:
@@ -850,7 +849,7 @@ def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index = 0, scriptpat
850849
if (scriptpath):
851850
ss += TaggedHash("TapLeaf", bytes([leaf_ver]) + ser_string(script))
852851
ss += bytes([0])
853-
ss += struct.pack("<i", codeseparator_pos)
852+
ss += codeseparator_pos.to_bytes(4, "little", signed=True)
854853
assert len(ss) == 175 - (in_type == SIGHASH_ANYONECANPAY) * 49 - (out_type != SIGHASH_ALL and out_type != SIGHASH_SINGLE) * 32 + (annex is not None) * 32 + scriptpath * 37
855854
return ss
856855

test/functional/wallet_balance.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test the wallet balance RPC methods."""
66
from decimal import Decimal
7-
import struct
87

98
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE as ADDRESS_WATCHONLY
109
from test_framework.blocktools import COINBASE_MATURITY
@@ -266,8 +265,8 @@ def test_balances(*, fee_node_1=0):
266265
tx_orig = self.nodes[0].gettransaction(txid)['hex']
267266
# Increase fee by 1 coin
268267
tx_replace = tx_orig.replace(
269-
struct.pack("<q", 99 * 10**8).hex(),
270-
struct.pack("<q", 98 * 10**8).hex(),
268+
(99 * 10**8).to_bytes(8, "little", signed=True).hex(),
269+
(98 * 10**8).to_bytes(8, "little", signed=True).hex(),
271270
)
272271
tx_replace = self.nodes[0].signrawtransactionwithwallet(tx_replace)['hex']
273272
# Total balance is given by the sum of outputs of the tx

0 commit comments

Comments
 (0)