Skip to content

Commit bbffef5

Browse files
authored
secp2561 precompile (#102)
1 parent 302059e commit bbffef5

File tree

2 files changed

+308
-13
lines changed

2 files changed

+308
-13
lines changed

generators/all.sh

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,45 @@
11
#!/bin/bash
22
TESTS_PATH=./test-vectors/txn/tests/precompile
33
FIXTS_PATH=./test-vectors/txn/fixtures/precompile
4+
LIB_SOLFUZZ_AGAVE=../solfuzz-agave/target/debug/libsolfuzz_agave.so
45

56
rm -r ${TESTS_PATH}/ed25519 ; mkdir -p ${TESTS_PATH}/ed25519
67
python3 generators/ed25519.py
78
rm -r ${FIXTS_PATH}/ed25519 ; mkdir -p ${FIXTS_PATH}/ed25519
8-
HARNESS_TYPE=TxnHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/ed25519 -o ${FIXTS_PATH}/ed25519 -s ./impl/lib/libsolfuzz_agave_v2.0.so
9+
solana-test-suite create-fixtures -i ${TESTS_PATH}/ed25519 -o ${FIXTS_PATH}/ed25519 -s ${LIB_SOLFUZZ_AGAVE} -h TxnHarness
910

1011
rm -r ${TESTS_PATH}/secp256k1 ; mkdir -p ${TESTS_PATH}/secp256k1
1112
python3 generators/secp256k1.py
1213
rm -r ${FIXTS_PATH}/secp256k1 ; mkdir -p ${FIXTS_PATH}/secp256k1
13-
HARNESS_TYPE=TxnHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/secp256k1 -o ${FIXTS_PATH}/secp256k1 -s ./impl/lib/libsolfuzz_agave_v2.0.so
14+
solana-test-suite create-fixtures -i ${TESTS_PATH}/secp256k1 -o ${FIXTS_PATH}/secp256k1 -s ${LIB_SOLFUZZ_AGAVE} -h TxnHarness
15+
16+
rm -r ${TESTS_PATH}/secp256r1 ; mkdir -p ${TESTS_PATH}/secp256r1
17+
python3 generators/precompile_secp256r1.py
18+
rm -r ${FIXTS_PATH}/secp256r1 ; mkdir -p ${FIXTS_PATH}/secp256r1
19+
solana-test-suite create-fixtures -i ${TESTS_PATH}/secp256r1 -o ${FIXTS_PATH}/secp256r1 -s ${LIB_SOLFUZZ_AGAVE} -h TxnHarness
1420

1521
TESTS_PATH=./test-vectors/syscall/tests
1622
FIXTS_PATH=./test-vectors/syscall/fixtures
1723

1824
rm -r ${TESTS_PATH}/curve25519 ; mkdir -p ${TESTS_PATH}/curve25519
1925
python3 generators/syscalls_curve25519.py
2026
rm -r ${FIXTS_PATH}/curve25519 ; mkdir -p ${FIXTS_PATH}/curve25519
21-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/curve25519 -o ${FIXTS_PATH}/curve25519 -s ./impl/lib/libsolfuzz_agave_v2.0.so
27+
solana-test-suite create-fixtures -i ${TESTS_PATH}/curve25519 -o ${FIXTS_PATH}/curve25519 -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness
2228

2329
rm -r ${TESTS_PATH}/secp256k1 ; mkdir -p ${TESTS_PATH}/secp256k1
2430
python3 generators/syscalls_secp256k1.py
2531
rm -r ${FIXTS_PATH}/secp256k1 ; mkdir -p ${FIXTS_PATH}/secp256k1
26-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/secp256k1 -o ${FIXTS_PATH}/secp256k1 -s ./impl/lib/libsolfuzz_agave_v2.0.so
32+
solana-test-suite create-fixtures -i ${TESTS_PATH}/secp256k1 -o ${FIXTS_PATH}/secp256k1 -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness
2733

2834
rm -r ${TESTS_PATH}/poseidon ; mkdir -p ${TESTS_PATH}/poseidon
2935
python3 generators/syscalls_poseidon.py
3036
rm -r ${FIXTS_PATH}/poseidon ; mkdir -p ${FIXTS_PATH}/poseidon
31-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/poseidon -o ${FIXTS_PATH}/poseidon -s ./impl/lib/libsolfuzz_agave_v2.0.so
37+
solana-test-suite create-fixtures -i ${TESTS_PATH}/poseidon -o ${FIXTS_PATH}/poseidon -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness
3238

3339
rm -r ${TESTS_PATH}/alt_bn128 ; mkdir -p ${TESTS_PATH}/alt_bn128
3440
python3 generators/syscalls_alt_bn128.py
3541
rm -r ${FIXTS_PATH}/alt_bn128 ; mkdir -p ${FIXTS_PATH}/alt_bn128
36-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/alt_bn128 -o ${FIXTS_PATH}/alt_bn128 -s ./impl/lib/libsolfuzz_agave_v2.0.so
42+
solana-test-suite create-fixtures -i ${TESTS_PATH}/alt_bn128 -o ${FIXTS_PATH}/alt_bn128 -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness
3743

3844
rm -r ${TESTS_PATH}/sha256 ; mkdir -p ${TESTS_PATH}/sha256
3945
rm -r ${TESTS_PATH}/keccak256 ; mkdir -p ${TESTS_PATH}/keccak256
@@ -42,9 +48,9 @@ python3 generators/syscalls_hash.py
4248
rm -r ${FIXTS_PATH}/sha256 ; mkdir -p ${FIXTS_PATH}/sha256
4349
rm -r ${FIXTS_PATH}/keccak256 ; mkdir -p ${FIXTS_PATH}/keccak256
4450
rm -r ${FIXTS_PATH}/blake3 ; mkdir -p ${FIXTS_PATH}/blake3
45-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/sha256 -o ${FIXTS_PATH}/sha256 -s ./impl/lib/libsolfuzz_agave_v2.0.so
46-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/keccak256 -o ${FIXTS_PATH}/keccak256 -s ./impl/lib/libsolfuzz_agave_v2.0.so
47-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/blake3 -o ${FIXTS_PATH}/blake3 -s ./impl/lib/libsolfuzz_agave_v2.0.so
51+
solana-test-suite create-fixtures -i ${TESTS_PATH}/sha256 -o ${FIXTS_PATH}/sha256 -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness
52+
solana-test-suite create-fixtures -i ${TESTS_PATH}/keccak256 -o ${FIXTS_PATH}/keccak256 -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness
53+
solana-test-suite create-fixtures -i ${TESTS_PATH}/blake3 -o ${FIXTS_PATH}/blake3 -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness
4854

4955
rm -r ${TESTS_PATH}/panic ; mkdir -p ${TESTS_PATH}/panic
5056
rm -r ${TESTS_PATH}/abort ; mkdir -p ${TESTS_PATH}/abort
@@ -55,7 +61,7 @@ rm -r ${FIXTS_PATH}/panic ; mkdir -p ${FIXTS_PATH}/panic
5561
rm -r ${FIXTS_PATH}/abort ; mkdir -p ${FIXTS_PATH}/abort
5662
rm -r ${FIXTS_PATH}/log ; mkdir -p ${FIXTS_PATH}/log
5763
rm -r ${FIXTS_PATH}/log_data ; mkdir -p ${FIXTS_PATH}/log_data
58-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/panic -o ${FIXTS_PATH}/panic -s ./impl/lib/libsolfuzz_agave_v2.0.so
59-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/abort -o ${FIXTS_PATH}/abort -s ./impl/lib/libsolfuzz_agave_v2.0.so
60-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/log -o ${FIXTS_PATH}/log -s ./impl/lib/libsolfuzz_agave_v2.0.so
61-
HARNESS_TYPE=SyscallHarness solana-test-suite create-fixtures -i ${TESTS_PATH}/log_data -o ${FIXTS_PATH}/log_data -s ./impl/lib/libsolfuzz_agave_v2.0.so
64+
solana-test-suite create-fixtures -i ${TESTS_PATH}/panic -o ${FIXTS_PATH}/panic -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness
65+
solana-test-suite create-fixtures -i ${TESTS_PATH}/abort -o ${FIXTS_PATH}/abort -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness
66+
solana-test-suite create-fixtures -i ${TESTS_PATH}/log -o ${FIXTS_PATH}/log -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness
67+
solana-test-suite create-fixtures -i ${TESTS_PATH}/log_data -o ${FIXTS_PATH}/log_data -s ${LIB_SOLFUZZ_AGAVE} -h SyscallHarness

generators/precompile_secp256r1.py

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
import fd58
2+
import hashlib
3+
import test_suite.context_pb2 as context_pb
4+
import test_suite.txn_pb2 as txn_pb
5+
from dataclasses import dataclass
6+
import binascii
7+
import requests
8+
import json
9+
10+
OUTPUT_DIR = "./test-vectors/txn/tests/precompile/secp256r1"
11+
12+
program_id = "Secp256r1SigVerify1111111111111111111111111"
13+
accounts = []
14+
15+
msg0 = bytes("hello", "ascii")
16+
x = bytes.fromhex("d8c82b3791c8b51cfe44aa50226217159596ca26e6075aaf8bf8be2d351b96ae")
17+
pub0 = bytes([2]) + x
18+
r = bytes.fromhex("a940d67c9560a47c5dafb45ab1f39eb68c8fac9b51fc8c4e30b1f0e63e4967d3")
19+
s = bytes.fromhex("586569a56364c3b03eefd421aa7fc750f6fa187210c3206c55602f96e0ecaa4d")
20+
sig0 = r + s
21+
22+
msg = bytes.fromhex("deadbeef0002")
23+
r = bytes.fromhex("16b60d19ba508f8bdac4c768bc868b9d2d458fc1c61a94af9423f6f850970eeb")
24+
s = bytes.fromhex("3c18d9642122ce6f02c1a9b2a489feb3862e9682f21ea0c7c70a839ea2152f92")
25+
x = bytes.fromhex("7929da089cf19abefaa8c13ade1de9b1c01291a261ebe9ae9a8f798936d56592")
26+
y = bytes.fromhex("d46cb01e60e978e0a19730b6be922ecaec6e4f2e78707b6b3ead2453503bb111")
27+
pub = bytes([3]) + x
28+
sig = r + s
29+
30+
offset0 = len(pub0 + sig0 + msg0)
31+
32+
# fmt: off
33+
test_vectors_agave = [
34+
# InvalidDataOffsets (result: 4)
35+
# invalid offset
36+
[1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
37+
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
38+
[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
39+
# invalid data offset
40+
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 1, 0, 0, 0],
41+
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 1, 0, 0, 0],
42+
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 232, 3, 0, 0],
43+
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0],
44+
# invalid pubkey offset
45+
[1, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0],
46+
[1, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0],
47+
# invalid signature offset
48+
[1, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
49+
[1, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
50+
# Ok() (result: 0)
51+
# valid signature
52+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg0), 0, 255, 255] + list(pub0) + list(sig0) + list(msg0),
53+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg), 0, 255, 255] + list(pub) + list(sig) + list(msg),
54+
[2, 0] + [63, 0, 255, 255, 30, 0, 255, 255, 127, 0, len(msg0), 0, 255, 255] + [63+offset0, 0, 255, 255, 30+offset0, 0, 255, 255, 127+offset0, 0, len(msg), 0, 255, 255] + list(pub0) + list(sig0) + list(msg0) + list(pub) + list(sig) + list(msg),
55+
]
56+
57+
58+
zero = bytes.fromhex("0000000000000000000000000000000000000000000000000000000000000000")
59+
n = bytes.fromhex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551")
60+
n_half = bytes.fromhex("7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a8")
61+
62+
sig_r_0 = zero + s
63+
sig_r_n = n + s
64+
sig_s_0 = r + zero
65+
sig_s_n = r + n
66+
sig_s_n_half = r + n_half
67+
68+
pub_0 = bytes([0]) + x
69+
pub_1 = bytes([1]) + x
70+
pub_4 = bytes([4]) + x
71+
pub_5 = bytes([5]) + x
72+
73+
pub_4_valid = pub_4 + y
74+
75+
# manual code cov
76+
test_vectors_manual = [
77+
# InvalidInstructionDataSize
78+
# https://github.com/anza-xyz/agave/blob/v2.1.4/sdk/secp256r1-program/src/lib.rs#L185-L187
79+
[0],
80+
# https://github.com/anza-xyz/agave/blob/v2.1.4/sdk/secp256r1-program/src/lib.rs#L189-L191
81+
[0, 0],
82+
[0, 2],
83+
# https://github.com/anza-xyz/agave/blob/v2.1.4/sdk/secp256r1-program/src/lib.rs#L192-L194
84+
[9, 0],
85+
# https://github.com/anza-xyz/agave/blob/v2.1.4/sdk/secp256r1-program/src/lib.rs#L201-L203
86+
[1],
87+
[0, 0, 0],
88+
[1, 0, 0],
89+
# https://github.com/anza-xyz/agave/blob/v2.1.4/sdk/secp256r1-program/src/lib.rs#L259-L262
90+
91+
# https://github.com/anza-xyz/agave/blob/v2.1.4/sdk/secp256r1-program/src/lib.rs#L265-L272
92+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg), 0, 255, 255] + list(pub) + list(sig_r_0) + list(msg),
93+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg), 0, 255, 255] + list(pub) + list(sig_r_n) + list(msg),
94+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg), 0, 255, 255] + list(pub) + list(sig_s_0) + list(msg),
95+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg), 0, 255, 255] + list(pub) + list(sig_s_n) + list(msg),
96+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg), 0, 255, 255] + list(pub) + list(sig_s_n_half) + list(msg),
97+
98+
# https://github.com/anza-xyz/agave/blob/v2.1.4/sdk/secp256r1-program/src/lib.rs#L279-L284
99+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg), 0, 255, 255] + list(pub_0) + list(sig) + list(msg),
100+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg), 0, 255, 255] + list(pub_1) + list(sig) + list(msg),
101+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg), 0, 255, 255] + list(pub_4) + list(sig) + list(msg),
102+
[1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0] + [len(msg), 0, 255, 255] + list(pub_5) + list(sig) + list(msg),
103+
104+
[1, 0, 81, 0, 255, 255, 16, 0, 255, 255, 145, 0] + [len(msg), 0, 255, 255] + list(pub_4_valid) + list(sig) + list(msg),
105+
]
106+
# fmt: on
107+
108+
109+
# test vectors from wycheproofs and cctv
110+
@dataclass
111+
class EddsaVerify:
112+
tcId: int
113+
comment: str
114+
msg: bytes
115+
sig: bytes
116+
pub: bytes
117+
ok: bool
118+
strict: bool
119+
120+
121+
def simd_tests(name, max_lines=200):
122+
req = requests.get(
123+
f"https://raw.githubusercontent.com/Bunkr-2FA/SIMD-48-Testing/refs/heads/main/test_vectors/vectors_{name}.jsonl"
124+
)
125+
tests = []
126+
n = int.from_bytes(
127+
bytes.fromhex(
128+
"ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"
129+
),
130+
byteorder="big",
131+
)
132+
n_half = (n - 1) // 2
133+
for j, line in enumerate(req.iter_lines()):
134+
test = json.loads(line)
135+
r = bytes.fromhex(test.get("r"))
136+
s = bytes.fromhex(test.get("s"))
137+
s_int = int.from_bytes(s, byteorder="big")
138+
s_norm = s_int if s_int <= n_half else n - s_int
139+
if s_norm < 0:
140+
continue
141+
s_norm = s_norm.to_bytes(32, "big")
142+
x = bytes.fromhex(test.get("x"))
143+
y = bytes.fromhex(test.get("y"))
144+
y_last_byte_mod2 = y[len(y) - 1] % 2
145+
msg = bytes.fromhex(test.get("msg"))
146+
147+
# print("-------")
148+
# print("r = ", binascii.hexlify(r))
149+
# print("s = ", binascii.hexlify(s))
150+
# print("x = ", binascii.hexlify(x))
151+
# print("y = ", binascii.hexlify(y))
152+
# print("pub = ", binascii.hexlify(bytes([2 + y_last_byte_mod2]) + x))
153+
# print("msg = ", binascii.hexlify(msg))
154+
155+
tests.append(
156+
{
157+
"sig": r + s_norm,
158+
"pub": bytes([2 + y_last_byte_mod2]) + x,
159+
"msg": msg,
160+
}
161+
)
162+
if j < 5:
163+
tests.append(
164+
{
165+
"sig": r + s_norm,
166+
"pub": bytes([3 - y_last_byte_mod2]) + x,
167+
"msg": msg,
168+
}
169+
)
170+
171+
if j >= max_lines:
172+
break
173+
return tests
174+
175+
176+
def simd_tests_wycheproof():
177+
req = requests.get(
178+
f"https://raw.githubusercontent.com/Bunkr-2FA/SIMD-48-Testing/refs/heads/main/test_vectors/vectors_wycheproof.jsonl"
179+
)
180+
tests = []
181+
for line in req.iter_lines():
182+
test = json.loads(line)
183+
r = bytes.fromhex(test.get("r"))
184+
s = bytes.fromhex(test.get("s"))
185+
x = bytes.fromhex(test.get("x"))
186+
y = bytes.fromhex(test.get("y"))
187+
y_last_byte_mod2 = y[len(y) - 1] % 2
188+
msg = bytes.fromhex(test.get("msg"))
189+
190+
if len(r) != 32:
191+
print("len r")
192+
if len(s) != 32:
193+
print("len s")
194+
if len(x) != 32:
195+
print("len x")
196+
197+
tests.append(
198+
{
199+
"sig": r + s,
200+
"pub": bytes([2 + y_last_byte_mod2]) + x,
201+
"msg": msg,
202+
}
203+
)
204+
return tests
205+
206+
207+
# fmt: off
208+
cache = {}
209+
def _into_instr_data(key_prefix, verify_tests):
210+
test_vectors = []
211+
prefix = [1, 0, 49, 0, 255, 255, 16, 0, 255, 255, 113, 0]
212+
for j, test in enumerate(verify_tests):
213+
pub = test.get("pub")
214+
sig = test.get("sig")
215+
msg = test.get("msg")
216+
k = pub+sig+msg
217+
if k in cache:
218+
continue
219+
else:
220+
cache[k] = True
221+
l = len(msg)
222+
if l >= 1 << 16:
223+
print("msg too long, skipping")
224+
continue
225+
data = (
226+
prefix
227+
+ [l % 256, l >> 8, 255, 255]
228+
+ list(pub)
229+
+ list(sig)
230+
+ list(msg)
231+
)
232+
key = key_prefix + str(j)
233+
test_vectors.append((key, data))
234+
return test_vectors
235+
# fmt: on
236+
237+
238+
def _into_key_data(key_prefix, test_vectors):
239+
return [(key_prefix + str(j), data) for j, data in enumerate(test_vectors)]
240+
241+
242+
print("Generating secp256r1 tests...")
243+
244+
test_vectors = (
245+
_into_key_data("a", test_vectors_agave)
246+
+ _into_key_data("m", test_vectors_manual)
247+
+ _into_instr_data("vm", simd_tests("random_mixed", 3))
248+
+ _into_instr_data("w", simd_tests_wycheproof())
249+
)
250+
251+
program_id = fd58.dec32(bytes(program_id, "utf-8"))
252+
253+
signer = fd58.dec32(bytes("BWbmXj5ckAaWCAtzMZ97qnJhBAKegoXtgNrv9BUpAB11", "utf-8"))
254+
signer_owner = bytes([0] * 32)
255+
signer_lamports = 1_000_000_000
256+
257+
for key, test in test_vectors:
258+
txn_ctx = txn_pb.TxnContext()
259+
260+
txn_ctx.tx.message.account_keys.extend(
261+
[
262+
signer,
263+
program_id,
264+
]
265+
)
266+
267+
signer_account = context_pb.AcctState()
268+
signer_account.address = signer
269+
signer_account.owner = signer_owner
270+
signer_account.lamports = signer_lamports
271+
txn_ctx.tx.message.account_shared_data.extend([signer_account])
272+
273+
ix = txn_pb.CompiledInstruction()
274+
ix.program_id_index = 1
275+
ix.data = bytes(test)
276+
txn_ctx.tx.message.instructions.extend([ix])
277+
278+
txn_ctx.epoch_ctx.features.features.extend(
279+
[
280+
0x2C38E34FF071060D, # enable_secp256r1_precompile
281+
]
282+
)
283+
284+
serialized_txn = txn_ctx.SerializeToString(deterministic=True)
285+
filename = str(key) + "_" + hashlib.sha3_256(serialized_txn).hexdigest()[:16]
286+
with open(f"{OUTPUT_DIR}/{filename}.bin", "wb") as f:
287+
f.write(serialized_txn)
288+
289+
print("done!")

0 commit comments

Comments
 (0)