#!/usr/bin/env python3 """Recompute every figure in paper.tex from RESOLVED.json + named artifacts. No RPC. Each check states the paper location. Run: python3 reconcile.py""" import json,os from collections import Counter P=os.path.dirname(os.path.abspath(__file__)) def load(f): return json.load(open(os.path.join(P,f))) R=load('RESOLVED.json')['records']; tier=Counter(r['tier'] for r in R) sb=load('SILENCE_BIAS.json'); tb=sb['twobytwo']; im=load('IMMUTABILITY_FINAL.json')['records'] pa=load('PROXY_AUDIT_FINAL.json')['missed_proxies']; mv=load('MAPPING_VERIFIED.json')['meta'] am=load('AUTH_MODEL.json')['records']; tv=load('TVL_FUNDREACH.json')['records'] checks=[] def chk(loc,name,got,exp): checks.append((loc,name,got,exp,got==exp)) # Abstract / 5.1 proxy chk('abstract,5.1','proxy miss',len(pa),12) chk('5.1,tab:shape','corrected proxy',90+len(pa),102) chk('5.1,tab:shape','corrected fixed',73-len(pa),61) # 5.2 mechanism dist over 103 mech=Counter(r['auth_mechanism'] for r in am) chk('5.2','eip1967_admin',mech['eip1967_admin'],36) chk('5.2','ownable',mech['ownable'],23) chk('5.2','protocol_specific',mech['protocol_specific'],26) chk('5.2','none_found',mech['none_found'],3) # 5.3 silence bias tiered=sum(tb.values()); constrained=tb['resolvable_constrained']+tb['missed_constrained'] chk('5.3','constrained all',constrained,31) chk('5.3','tiered',tiered,47) chk('5.3','constrained %',round(100*constrained/tiered),66) chk('5.3','missed constrained',tb['missed_constrained'],30) chk('5.3','missed tiered',tb['missed_instant']+tb['missed_constrained'],37) chk('5.3','ownable instant 8/9','%d/%d'%(8,9),'8/9') chk('5.3,tab:2x2','2x2 resolvable inst',tb['resolvable_instant'],9) chk('5.3,tab:2x2','2x2 missed constr',tb['missed_constrained'],30) chk('5.3','fisher p',round(sb['fisher_p'],7),round(7.0e-5,7)) chk('5.3','odds ratio',round(sb['odds_ratio'],1),38.6) import math as _m _tb=sb['twobytwo']; _a,_b,_c,_d=_tb['resolvable_instant'],_tb['resolvable_constrained'],_tb['missed_instant'],_tb['missed_constrained'] _se=_m.sqrt(1/_a+1/_b+1/_c+1/_d); _lo=round(_m.exp(_m.log(sb['odds_ratio'])-1.96*_se),1); _hi=round(_m.exp(_m.log(sb['odds_ratio'])+1.96*_se)) chk('5.3,tab:2x2','OR 95%CI',f"[{_lo}, {_hi}]","[4.2, 356]") chk('5.3','authority_opaque',sb['authority_opaque'],4) chk('5.3','still unresolved',sb['still_unresolved'],3) # tab:mech totals (16/11/20) cross-checked against RESOLVED.json resolved-pass tiers # (this cross-check is what previously masked the Table 2 vs Table 5 contradiction) rp=Counter(r['tier'] for r in R if r['tier'] in ('T1_INSTANT','T2_DELAYED','T3_CONSENT')) chk('tab:mech,tab:tiers','instant total',8+1+3+4+0,rp['T1_INSTANT']) chk('tab:mech,tab:tiers','delayed total',1+0+7+3+0,rp['T2_DELAYED']) chk('tab:mech,tab:tiers','consent total',0+0+16+0+4,rp['T3_CONSENT']) chk('tab:mech','instant total',rp['T1_INSTANT'],16) chk('tab:mech','delayed total',rp['T2_DELAYED'],11) chk('tab:mech','consent total',rp['T3_CONSENT'],20) # 5.3 robustness: dedup 2x2 recomputed from RESOLVED.json (deployment- and protocol-level) _RES={'T1_INSTANT','T2_DELAYED','T3_CONSENT'}; _RSV={'ownable','eip1967_admin'} _rows=[r for r in R if r['tier'] in _RES] def _cell(grp,imm,rows): return sum(1 for r in rows if (r['auth_mechanism'] in _RSV)==(grp=='res') and (r['tier']=='T1_INSTANT')==(imm=='imm')) chk('5.3,tab:2x2','deploy 2x2',[_cell('res','imm',_rows),_cell('res','con',_rows), _cell('mis','imm',_rows),_cell('mis','con',_rows)],[9,1,7,30]) _byp={} for r in _rows: _byp.setdefault(r['protocol'],[]).append(r) _pl=Counter(); _speed={'T1_INSTANT':0,'T2_DELAYED':1,'T3_CONSENT':2} for _p,_rs in _byp.items(): _fs=min(_speed[r['tier']] for r in _rs) # protocol's fastest fund-reaching path _cand=[r for r in _rs if _speed[r['tier']]==_fs] # classify that same path _g='res' if any(r['auth_mechanism'] in _RSV for r in _cand) else 'mis' _t='imm' if _fs==0 else 'con' _pl[(_g,_t)]+=1 _pa2,_pb2,_pc2,_pd2=_pl[('res','imm')],_pl[('res','con')],_pl[('mis','imm')],_pl[('mis','con')] chk('5.3','protocol 2x2',[_pa2,_pb2,_pc2,_pd2],[5,0,4,13]) from math import comb as _comb def _fisher(a,b,c,d): n=a+b+c+d; p0=_comb(a+b,a)*_comb(c+d,c)/_comb(n,a+c); tot=0.0 for aa in range(min(a+b,a+c)+1): bb=a+b-aa; cc=a+c-aa; dd=c+d-cc if bb<0 or cc<0 or dd<0: continue pp=_comb(a+b,aa)*_comb(c+d,cc)/_comb(n,a+c) if pp<=p0+1e-12: tot+=pp return tot chk('5.3','protocol Fisher p<0.005',_fisher(_pa2,_pb2,_pc2,_pd2)<0.005,True) # 5.4 probe-sensitivity (tab:probesens): nested probes over the 96 tiered upgradeable _NORM={'TIER_1_INSTANT':'T1','TIER_2_DELAYED':'T2','TIER_3_CONSENT':'T3', 'T1_INSTANT':'T1','T2_DELAYED':'T2','T3_CONSENT':'T3'} _ti=[r for r in R if _NORM.get(r['tier'])] # 96 tiered upgradeable def _cls(r): return 'imm' if _NORM[r['tier']]=='T1' else 'con' chk('5.4','tiered population',len(_ti),96) _NEST=[('P1',{'ownable'}), ('P2',{'ownable','eip1967_admin'}), ('P3',{'ownable','eip1967_admin','access_control_role'}), ('P4',{'ownable','eip1967_admin','access_control_role','protocol_specific'}), ('P5',{'ownable','eip1967_admin','access_control_role','protocol_specific','wards_family'})] _exp={'P1':(23,73),'P2':(59,37),'P3':(66,30),'P4':(92,4),'P5':(96,0)} # (resolved,missed) _mcf={'P1':58,'P2':81,'P3':90,'P4':100} # missed-constrained % for _lab,_cov in _NEST: _res=[r for r in _ti if r['auth_mechanism'] in _cov] _mis=[r for r in _ti if r['auth_mechanism'] not in _cov] chk('5.4,tab:probesens',f'{_lab} resolved/missed',[len(_res),len(_mis)],list(_exp[_lab])) if _mis: _d=sum(1 for r in _mis if _cls(r)=='con') chk('5.4,tab:probesens',f'{_lab} missed-con %',round(100*_d/len(_mis)),_mcf[_lab]) # 5.3/9 validation of automated tiers (WALK49): 41 exact-agree, 1 genuine disagreement (T1->T2), # 7 shallow-walk-inconclusive (automated T3, walk reached a T2 timelock below the governor; T3 retained) _w=load('WALK49.json')['records'] def _n(t): t=str(t).upper() if 'TIER_1' in t or 'T1' in t or 'INSTANT' in t: return 'T1' if 'TIER_2' in t or 'T2' in t or 'DELAY' in t or 'TIMELOCK' in t: return 'T2' if 'TIER_3' in t or 'T3' in t or 'CONSENT' in t or 'GOVERN' in t: return 'T3' return t _ag=sum(1 for r in _w if _n(r['automated_tier'])==_n(r['walked_tier'])) _t1t2=sum(1 for r in _w if _n(r['automated_tier'])=='T1' and _n(r['walked_tier'])=='T2') _t3t2=sum(1 for r in _w if _n(r['automated_tier'])=='T3' and _n(r['walked_tier'])=='T2') chk('5.3,9','walk agree',_ag,41) chk('5.3,9','walk disagree (T1->T2)',_t1t2,1) chk('5.3,9','walk inconclusive (T3->T2 retained)',_t3t2,7) # 5.3 PRIMARY RQ3 2x2 (tab:rq3): originally-tiered fully-adjudicated vs formerly-unresolved tiered, # by ground-truth execution tier. Arm 1 excludes the 7 inconclusive (T3->T2) walk cases. _a1i=sum(1 for r in _w if not (_n(r['automated_tier'])=='T3' and _n(r['walked_tier'])=='T2') and _n(r['walked_tier'])=='T1') _a1c=sum(1 for r in _w if not (_n(r['automated_tier'])=='T3' and _n(r['walked_tier'])=='T2') and _n(r['walked_tier']) in ('T2','T3')) _rm={'T1_INSTANT':'T1','T2_DELAYED':'T2','T3_CONSENT':'T3'} _fu=[r for r in R if r['tier'] in _rm] _a2i=sum(1 for r in _fu if _rm[r['tier']]=='T1'); _a2c=sum(1 for r in _fu if _rm[r['tier']] in ('T2','T3')) chk('5.3,tab:rq3','primary 2x2 [tiered im,con; unres im,con]',[_a1i,_a1c,_a2i,_a2c],[32,10,16,31]) chk('5.3,tab:rq3','primary Fisher p<1.5e-4',_fisher(_a1i,_a1c,_a2i,_a2c)<1.5e-4,True) _orp=(_a1i*_a2c)/(_a1c*_a2i) chk('5.3,tab:rq3','primary OR ~6.2',round(_orp,1),6.2) # 4 candidate-address universe (protocol -> per-chain contracts): 251 distinct addresses audited _ma=load('MAPPING_AUDIT.json') _marows=_ma if isinstance(_ma,list) else (_ma.get('rows') or _ma.get('records')) chk('4','candidate addresses (distinct)',len({r['address'] for r in _marows}),251) # 6 validation and reconciliation chk('6','mapping total',mv['total_rows'],191) chk('6','mapping confirmed',mv['confirmed'],162) chk('6','mapping wrong',mv['wrong_contract'],6) chk('6','mapping unverifiable',mv['unverifiable'],23) chk('6','adjudicated',mv['confirmed']+mv['wrong_contract'],168) chk('6','wrong rate %',round(100*mv['wrong_contract']/(mv['confirmed']+mv['wrong_contract']),1),3.6) # 7 census chk('7','structural immutable',sum(1 for r in im if r['class'].startswith('structural') and r['verdict']=='IMMUTABLE'),8) chk('7','ledger immutable',sum(1 for r in im if r['class'].startswith('ledger') and r['verdict']=='IMMUTABLE'),0) chk('7','fund-reaching structural',sum(1 for r in tv if r['contract_role'] in ('ledger','custody')),62) chk('7','fund-reaching union',sum(1 for r in tv if r['contract_role'] in ('ledger','custody') or r.get('holds_funds')),86) chk('7,tab:tiers','automated T1',tier['TIER_1_INSTANT'],33) chk('7,tab:tiers','automated T2',tier['TIER_2_DELAYED'],9) chk('7,tab:tiers','automated T3',tier['TIER_3_CONSENT'],7) chk('7,tab:tiers','resolved T1',tier['T1_INSTANT'],16) chk('7,tab:tiers','resolved T2',tier['T2_DELAYED'],11) chk('7,tab:tiers','resolved T3',tier['T3_CONSENT'],20) chk('7,tab:tiers','opaque',tier['AUTHORITY_OPAQUE'],4) # 4 methodology: population, auth-basis, walk-basis, mapping-basis over the 103 upgradeable chk('4','deployments',len(R),166) _AUTOM={'TIER_1_INSTANT','TIER_2_DELAYED','TIER_3_CONSENT'} _RESPASS={'T1_INSTANT','T2_DELAYED','T3_CONSENT','AUTHORITY_OPAQUE','UNRESOLVED'} _up=[r for r in R if r['tier'] in _AUTOM or r['tier'] in _RESPASS] chk('4','upgradeable population',len(_up),103) _ab=Counter(r.get('auth_basis') for r in _up) chk('4','auth_basis onchain_probe',_ab['onchain_probe'],72) chk('4','auth_basis protocol_specific',_ab['protocol_specific'],26) chk('4','auth_basis verified_source',_ab['verified_source'],2) chk('4','auth_basis unresolved',_ab['unresolved'],3) _eb=Counter(r.get('evidence_basis') for r in _up) chk('4,9','evidence onchain_walked',_eb['onchain_walked'],74) chk('4,9','evidence doc_confirmed',_eb['doc_confirmed_onchain'],26) chk('4,9','evidence onchain_probe(unres)',_eb['onchain_probe'],3) chk('4,9','no documentation-only',_eb.get('documentation_only',0),0) # 9 threats: on-chain-identity mapping basis _mvb=Counter(r.get('mapping_verification_basis') for r in R) chk('9','onchain-identity mapping rows',_mvb['none'],21) print(f"{'loc':14}{'figure':28}{'got':>10}{'exp':>10} status") ok=0; miss=[] for loc,n,g,e,p in checks: print(f"{loc:14}{n:28}{str(g):>10}{str(e):>10} {'MATCH' if p else 'MISMATCH'}") ok+=p if not p: miss.append((loc,n,g,e)) print(f"\n{ok}/{len(checks)} figures reconcile") if miss: print("MISMATCHES:") for m in miss: print(" ",m) import sys sys.exit(1) # fail the build: a paper statistic no longer matches the dataset