upleb.uk

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

summaryrefslogtreecommitdiff
path: root/scripts/prove-utxo.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/prove-utxo.py')
-rw-r--r--scripts/prove-utxo.py50
1 files changed, 17 insertions, 33 deletions
diff --git a/scripts/prove-utxo.py b/scripts/prove-utxo.py
index 7a10d2a..105e677 100644
--- a/scripts/prove-utxo.py
+++ b/scripts/prove-utxo.py
@@ -7,25 +7,13 @@ included in the deterministic UTXO root produced by verify-utxo-set.py.
7 7
8import argparse 8import argparse
9import hashlib 9import hashlib
10import importlib.util
11import json 10import json
12import os
13import sys 11import sys
14from pathlib import Path 12from pathlib import Path
15 13
14import bitflash_chain as chain
16 15
17SCHEMA = "bitflash-utxo-inclusion-proof-1" 16SCHEMA = "bitflash-utxo-inclusion-proof-1"
18NODE_DOMAIN = b"BTFNODE1|"
19
20
21def load_utxo_tool():
22 path = Path(__file__).with_name("verify-utxo-set.py")
23 spec = importlib.util.spec_from_file_location("bitflash_verify_utxo_set", path)
24 if spec is None or spec.loader is None:
25 raise SystemExit("could not load %s" % path)
26 mod = importlib.util.module_from_spec(spec)
27 spec.loader.exec_module(mod)
28 return mod
29 17
30 18
31def canonical_bytes(obj): 19def canonical_bytes(obj):
@@ -72,7 +60,7 @@ def build_merkle_proof(keys, leaf_hashes, index):
72 for i in range(0, len(layer), 2): 60 for i in range(0, len(layer), 2):
73 left = layer[i] 61 left = layer[i]
74 right = layer[i + 1] if i + 1 < len(layer) else left 62 right = layer[i + 1] if i + 1 < len(layer) else left
75 next_layer.append(hashlib.sha256(NODE_DOMAIN + left + right).digest()) 63 next_layer.append(hashlib.sha256(chain.UTXO_NODE_DOMAIN + left + right).digest())
76 layer = next_layer 64 layer = next_layer
77 current_index //= 2 65 current_index //= 2
78 level += 1 66 level += 1
@@ -80,25 +68,27 @@ def build_merkle_proof(keys, leaf_hashes, index):
80 68
81 69
82def build_proof(args): 70def build_proof(args):
83 tool = load_utxo_tool()
84 txid, vout = split_outpoint(args.outpoint) 71 txid, vout = split_outpoint(args.outpoint)
85 outpoint = "%s:%d" % (txid, vout) 72 outpoint = "%s:%d" % (txid, vout)
86 73
87 raw_blocks = tool.read_blocks(args.datadir, args.max_blocks) 74 raw_blocks = chain.read_blocks(args.datadir, args.max_blocks)
88 if not raw_blocks: 75 if not raw_blocks:
89 raise SystemExit("no Bitflash blocks found in %s" % args.datadir) 76 raise SystemExit("no Bitflash blocks found in %s" % args.datadir)
90 blocks = tool.select_main_chain(raw_blocks) 77 try:
91 state = tool.apply_blocks(blocks) 78 blocks = chain.select_main_chain(raw_blocks)
79 state = chain.apply_blocks(blocks, strict_duplicates=args.strict_duplicates)
80 except chain.ParseError as e:
81 raise SystemExit(str(e))
92 utxos = state["utxos"] 82 utxos = state["utxos"]
93 stats = state["stats"] 83 stats = state["stats"]
94 if outpoint not in utxos: 84 if outpoint not in utxos:
95 raise SystemExit("outpoint is not unspent in the scanned chain: %s" % outpoint) 85 raise SystemExit("outpoint is not unspent in the scanned chain: %s" % outpoint)
96 86
97 keys = sorted(utxos) 87 keys = sorted(utxos)
98 leaf_hashes = [tool.utxo_leaf_hash(k, utxos[k]) for k in keys] 88 leaf_hashes = [chain.utxo_leaf_hash(k, utxos[k]) for k in keys]
99 index = keys.index(outpoint) 89 index = keys.index(outpoint)
100 proof_path, root = build_merkle_proof(keys, leaf_hashes, index) 90 proof_path, root = build_merkle_proof(keys, leaf_hashes, index)
101 direct_root = tool.utxo_root(utxos) 91 direct_root = chain.utxo_root(utxos)
102 if root != direct_root: 92 if root != direct_root:
103 raise SystemExit("internal proof root mismatch") 93 raise SystemExit("internal proof root mismatch")
104 94
@@ -110,13 +100,13 @@ def build_proof(args):
110 "chain": { 100 "chain": {
111 "name": "Bitflash", 101 "name": "Bitflash",
112 "ticker": "BTF", 102 "ticker": "BTF",
113 "message_start_hex": tool.MAGIC.hex(), 103 "message_start_hex": chain.MAGIC.hex(),
114 "coin": tool.COIN, 104 "coin": chain.COIN,
115 }, 105 },
116 "commitment": { 106 "commitment": {
117 "algorithm": "sorted-outpoint-merkle-sha256", 107 "algorithm": "sorted-outpoint-merkle-sha256",
118 "leaf_domain": "BTFUTXO1", 108 "leaf_domain": chain.UTXO_LEAF_DOMAIN,
119 "node_domain": "BTFNODE1", 109 "node_domain": chain.UTXO_NODE_DOMAIN_TAG,
120 "root": root, 110 "root": root,
121 "leaf_count": len(keys), 111 "leaf_count": len(keys),
122 }, 112 },
@@ -169,20 +159,14 @@ def print_text(proof):
169 print("Verdict: %s" % proof["verdict"]) 159 print("Verdict: %s" % proof["verdict"])
170 160
171 161
172def default_datadir():
173 if os.name == "nt" and os.environ.get("APPDATA"):
174 return os.path.join(os.environ["APPDATA"], "Bitflash")
175 if os.environ.get("HOME"):
176 return os.path.join(os.environ["HOME"], ".bitflash")
177 return "."
178
179
180def main(argv): 162def main(argv):
181 ap = argparse.ArgumentParser(description=__doc__) 163 ap = argparse.ArgumentParser(description=__doc__)
182 ap.add_argument("outpoint", help="unspent output to prove, as txid:vout") 164 ap.add_argument("outpoint", help="unspent output to prove, as txid:vout")
183 ap.add_argument("--datadir", default=default_datadir(), help="Bitflash data directory") 165 ap.add_argument("--datadir", default=chain.default_datadir(), help="Bitflash data directory")
184 ap.add_argument("--max-blocks", type=int, default=0, 166 ap.add_argument("--max-blocks", type=int, default=0,
185 help="number of blocks to scan; 0 means all block files") 167 help="number of blocks to scan; 0 means all block files")
168 ap.add_argument("--strict-duplicates", action="store_true",
169 help="fail instead of using legacy last-write-wins duplicate output handling")
186 ap.add_argument("--json", action="store_true", help="print canonical JSON") 170 ap.add_argument("--json", action="store_true", help="print canonical JSON")
187 ap.add_argument("--out", help="write canonical JSON proof to this path") 171 ap.add_argument("--out", help="write canonical JSON proof to this path")
188 args = ap.parse_args(argv) 172 args = ap.parse_args(argv)