-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun_vectors.py
More file actions
116 lines (97 loc) · 4.15 KB
/
Copy pathrun_vectors.py
File metadata and controls
116 lines (97 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
"""
Load every ARE vector (both MINIMAL and MAINNET presets) and assert
expected == actual verifier output. Prints "N/M passed". Exit 1 on any mismatch.
Trusted committees (never carried in a vector):
- MINIMAL vectors: the 32 seeded committee pubkeys, rebuilt from SEED.
- MAINNET vector: the real 512 sync-committee pubkeys loaded from
../vectors/real-data/bootstrap_committee.json (the trusted
LightClientBootstrap).
Each vector declares its preset in `preset.name`; the runner selects the active
preset (SYNC_COMMITTEE_SIZE / fork schedule) and the right committee per vector.
"""
import glob
import json
import os
import random
import sys
import are_constants as C
from are_codec import envelope_from_json, anchor_from_json
from are_bls import sk_to_pk
from are_verify import verify, VerifierConfig
VECTORS_DIR = os.path.join(os.path.dirname(__file__), "..", "vectors")
RD = os.path.join(VECTORS_DIR, "real-data")
SEED = 42
BLS_CURVE_ORDER = 52435875175126190479447740508185965837690552500527637822603658699938581184513
def minimal_committee(period):
"""Rebuild the 32 seeded MINIMAL committee pubkeys (trusted config)."""
rng = random.Random(SEED)
pks = []
for _ in range(32):
sk = rng.randrange(1, BLS_CURVE_ORDER)
pks.append(sk_to_pk(sk))
return {period: pks}
def mainnet_committee(period):
"""Load the real 512 sync-committee pubkeys from the trusted bootstrap."""
bs = json.load(open(os.path.join(RD, "bootstrap_committee.json")))
pks = [bytes.fromhex(p[2:]) for p in bs["current_sync_committee"]["pubkeys"]]
return {period: pks}
def run_one(v):
preset = v["preset"]["name"]
C.select_preset(preset)
env = envelope_from_json(v["envelope"])
anchor = anchor_from_json(v["anchor"])
sig_slot = anchor.signature_slot
period = C.compute_sync_committee_period_at_slot(sig_slot)
if preset == "MAINNET":
committees = mainnet_committee(period)
head_slot = anchor.attested_header.slot + 8
max_staleness = 64
else:
committees = minimal_committee(period)
# The verifier's own head tracks the attested header (freshness step 6 is a
# verifier-config check, not a vector field). Use attested + 8, which holds
# for every MINIMAL vector regardless of its (possibly deep-history) read slot.
head_slot = anchor.attested_header.slot + 8
max_staleness = 64
# provider_sig vectors carry the trusted provider key + dispute flag in
# `preset` (the verifier's INDEPENDENT trust config, never the envelope).
pcfg = v["preset"]
dispute_mode = bool(pcfg.get("dispute_mode", False))
resolver = None
if "provider_pubkey" in pcfg:
trusted_pub = bytes.fromhex(pcfg["provider_pubkey"][2:])
trusted_hint = bytes.fromhex(pcfg["provider_key_hint"][2:])
def resolver(hint, sig_alg, _pub=trusted_pub, _hint=trusted_hint):
# INDEPENDENT key resolution: match the hint against trusted config.
if hint == _hint and sig_alg == C.SIG_ALG_ED25519:
return _pub
return None
cfg = VerifierConfig(
chain_id=1, genesis_validators_root=C.GENESIS_VALIDATORS_ROOT,
committees=committees, head_slot=head_slot,
max_staleness_slots=max_staleness,
dispute_mode=dispute_mode, resolve_provider_key=resolver)
res = verify(env, anchor, cfg)
return res[0] if isinstance(res, tuple) else res
def main():
files = sorted(glob.glob(os.path.join(VECTORS_DIR, "*.json")))
passed = 0
total = len(files)
for path in files:
with open(path) as f:
v = json.load(f)
actual = run_one(v)
expected = v["expected"]["result"]
ok = (actual == expected)
name = os.path.basename(path)
preset = v["preset"]["name"]
print(f" [{'PASS' if ok else 'FAIL'}] {name:28s} ({preset:7s}) "
f"expected={expected:14s} actual={actual}")
if ok:
passed += 1
else:
print(f" full result mismatch")
print(f"\n{passed}/{total} passed")
sys.exit(0 if passed == total else 1)
if __name__ == "__main__":
main()