upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/scripts/verify-utxo-proof.py
diff options
context:
space:
mode:
authorBitflash <admin@bitflash.network>2026-08-12 09:08:14 -0300
committerBitflash <admin@bitflash.network>2026-08-12 09:08:14 -0300
commit5b2d9df37e33fcd17158b25cbd749372d59c18da (patch)
treece72921d72611dab1aafb78521b68da0fd11383d /scripts/verify-utxo-proof.py
parenta83fae37099cb102beb5a4c174a4abe6ef3f24a4 (diff)
parentcaa6a9668fc2b2ed1192f7ebdc0b78621f45898d (diff)
Merge pull request 'tools: harden chain audit scripts' (#25) from tooling-hardening into main
Diffstat (limited to 'scripts/verify-utxo-proof.py')
-rw-r--r--scripts/verify-utxo-proof.py29
1 files changed, 6 insertions, 23 deletions
diff --git a/scripts/verify-utxo-proof.py b/scripts/verify-utxo-proof.py
index 31099f7..50d9356 100644
--- a/scripts/verify-utxo-proof.py
+++ b/scripts/verify-utxo-proof.py
@@ -7,32 +7,15 @@ import json
7import sys 7import sys
8from pathlib import Path 8from pathlib import Path
9 9
10import bitflash_chain as chain
10 11
11SCHEMA = "bitflash-utxo-inclusion-proof-1" 12SCHEMA = "bitflash-utxo-inclusion-proof-1"
12LEAF_DOMAIN = "BTFUTXO1"
13NODE_DOMAIN = b"BTFNODE1|"
14 13
15 14
16def canonical_bytes(obj): 15def canonical_bytes(obj):
17 return (json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + "\n").encode("ascii") 16 return (json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + "\n").encode("ascii")
18 17
19 18
20def sha256(data):
21 return hashlib.sha256(data).digest()
22
23
24def utxo_leaf_hash(outpoint, utxo):
25 payload = "|".join([
26 LEAF_DOMAIN,
27 outpoint,
28 str(utxo["value_satoshis"]),
29 utxo["script_pub_key_hex"],
30 str(utxo["created_height"]),
31 utxo["created_txid"],
32 ]).encode("ascii")
33 return sha256(payload)
34
35
36def load_proof(path): 19def load_proof(path):
37 try: 20 try:
38 return json.loads(Path(path).read_text(encoding="ascii")) 21 return json.loads(Path(path).read_text(encoding="ascii"))
@@ -47,9 +30,9 @@ def verify(proof, expected_root=None):
47 commitment = proof.get("commitment", {}) 30 commitment = proof.get("commitment", {})
48 if commitment.get("algorithm") != "sorted-outpoint-merkle-sha256": 31 if commitment.get("algorithm") != "sorted-outpoint-merkle-sha256":
49 failures.append("unexpected commitment algorithm") 32 failures.append("unexpected commitment algorithm")
50 if commitment.get("leaf_domain") != LEAF_DOMAIN: 33 if commitment.get("leaf_domain") != chain.UTXO_LEAF_DOMAIN:
51 failures.append("unexpected leaf domain") 34 failures.append("unexpected leaf domain")
52 if commitment.get("node_domain") != "BTFNODE1": 35 if commitment.get("node_domain") != chain.UTXO_NODE_DOMAIN_TAG:
53 failures.append("unexpected node domain") 36 failures.append("unexpected node domain")
54 37
55 target = proof.get("target", {}) 38 target = proof.get("target", {})
@@ -59,7 +42,7 @@ def verify(proof, expected_root=None):
59 failures.append("target outpoint does not match txid:vout") 42 failures.append("target outpoint does not match txid:vout")
60 43
61 try: 44 try:
62 current = utxo_leaf_hash(outpoint, utxo) 45 current = chain.utxo_leaf_hash(outpoint, utxo)
63 leaf_ok = target.get("leaf_hash") == current.hex() 46 leaf_ok = target.get("leaf_hash") == current.hex()
64 except Exception as e: 47 except Exception as e:
65 failures.append("could not hash target leaf: %s" % e) 48 failures.append("could not hash target leaf: %s" % e)
@@ -79,9 +62,9 @@ def verify(proof, expected_root=None):
79 failures.append("bad sibling hash length at proof level %d" % i) 62 failures.append("bad sibling hash length at proof level %d" % i)
80 side = step.get("side") 63 side = step.get("side")
81 if side == "right": 64 if side == "right":
82 current = sha256(NODE_DOMAIN + current + sibling) 65 current = chain.sha256(chain.UTXO_NODE_DOMAIN + current + sibling)
83 elif side == "left": 66 elif side == "left":
84 current = sha256(NODE_DOMAIN + sibling + current) 67 current = chain.sha256(chain.UTXO_NODE_DOMAIN + sibling + current)
85 else: 68 else:
86 failures.append("bad sibling side at proof level %d" % i) 69 failures.append("bad sibling side at proof level %d" % i)
87 70