1+ /**
2+ * helios-verifier.ts
3+ *
4+ * Experimental light-client verification layer using Helios (@a16z/helios).
5+ * Only active on Ethereum mainnet (chainId 0x1).
6+ */
7+
18export interface VerificationResult {
29 verified : boolean ;
310 tampered : boolean ;
@@ -7,26 +14,70 @@ export interface VerificationResult {
714
815const SKIP : VerificationResult = { verified : false , tampered : false , message : '' } ;
916const MAINNET_CHAIN_ID = '0x1' ;
17+ const DEFAULT_CONSENSUS_RPC = 'https://ethereum.operationsolarstorm.org' ;
18+ const CHECKPOINT_FETCH_TIMEOUT_MS = 5_000 ;
1019
11- // DEMO MODE: simulate Helios detecting a lying RPC
12- let heliosProvider : any = {
13- waitSynced : ( ) => Promise . resolve ( ) ,
14- request : async ( args : any ) => {
15- if ( args . method === 'eth_call' ) {
16- return '0x0000000000000000000000000000000000000000000000000000000000000001' ;
17- }
18- return '0x0' ;
19- }
20- } ;
20+ let heliosProvider : any = null ;
2121let initInProgress = false ;
22- let isSynced = true ;
22+ let isSynced = false ;
23+ let currentExecutionRpc = '' ;
24+
25+ async function fetchFreshCheckpoint ( consensusRpc : string ) : Promise < string | undefined > {
26+ const url = `${ consensusRpc . replace ( / \/ $ / , '' ) } /eth/v1/beacon/headers/finalized` ;
27+ const controller = new AbortController ( ) ;
28+ const timer = setTimeout ( ( ) => controller . abort ( ) , CHECKPOINT_FETCH_TIMEOUT_MS ) ;
29+ try {
30+ const res = await fetch ( url , { signal : controller . signal } ) ;
31+ if ( ! res . ok ) return undefined ;
32+ const json = ( await res . json ( ) ) as { data ?: { root ?: string } } ;
33+ const root = json ?. data ?. root ;
34+ if ( typeof root === 'string' && root . startsWith ( '0x' ) ) return root ;
35+ return undefined ;
36+ } catch {
37+ return undefined ;
38+ } finally {
39+ clearTimeout ( timer ) ;
40+ }
41+ }
2342
24- export async function initHelios ( executionRpc : string , consensusRpc ?: string ) : Promise < void > {
25- return ;
43+ export async function initHelios (
44+ executionRpc : string ,
45+ consensusRpc : string = DEFAULT_CONSENSUS_RPC ,
46+ ) : Promise < void > {
47+ if ( initInProgress ) return ;
48+ if ( heliosProvider && currentExecutionRpc === executionRpc ) return ;
49+ initInProgress = true ;
50+ heliosProvider = null ;
51+ isSynced = false ;
52+ currentExecutionRpc = executionRpc ;
53+ try {
54+ const checkpoint = await fetchFreshCheckpoint ( consensusRpc ) ;
55+ const { createHeliosProvider } = await import ( '@a16z/helios' ) ;
56+ const config : Record < string , string > = {
57+ executionRpc,
58+ consensusRpc,
59+ network : 'mainnet' ,
60+ } ;
61+ if ( checkpoint ) config [ 'checkpoint' ] = checkpoint ;
62+ heliosProvider = await createHeliosProvider ( config , 'ethereum' ) ;
63+ heliosProvider
64+ . waitSynced ( )
65+ . then ( ( ) => { isSynced = true ; console . log ( '[helios-verifier] synced and ready' ) ; } )
66+ . catch ( ( err : unknown ) => { console . warn ( '[helios-verifier] sync failed:' , err ) ; resetHelios ( ) ; } ) ;
67+ } catch ( err ) {
68+ console . warn ( '[helios-verifier] failed to initialise:' , err ) ;
69+ heliosProvider = null ;
70+ currentExecutionRpc = '' ;
71+ } finally {
72+ initInProgress = false ;
73+ }
2674}
2775
2876export function resetHelios ( ) : void {
29- return ;
77+ heliosProvider = null ;
78+ isSynced = false ;
79+ initInProgress = false ;
80+ currentExecutionRpc = '' ;
3081}
3182
3283function encodeBalanceOf ( address : string ) : string {
@@ -47,36 +98,29 @@ export async function verifyErc20Balance(
4798 chainId : string ,
4899 executionRpc : string ,
49100) : Promise < VerificationResult > {
50- console . log ( '[helios-verifier] chainId check:' , chainId , MAINNET_CHAIN_ID ) ;
51101 if ( chainId . toLowerCase ( ) !== MAINNET_CHAIN_ID ) return SKIP ;
102+ if ( ! heliosProvider && ! initInProgress ) void initHelios ( executionRpc ) ;
52103 if ( ! isSynced || ! heliosProvider ) return SKIP ;
53-
54104 let heliosBalanceHex : string ;
55105 try {
56106 heliosBalanceHex = ( await heliosProvider . request ( {
57107 method : 'eth_call' ,
58108 params : [ { to : contractAddress , data : encodeBalanceOf ( walletAddress ) } , 'latest' ] ,
59109 } ) ) as string ;
60110 } catch ( err ) {
61- console . warn ( '[helios-verifier] eth_call via Helios failed:' , err ) ;
111+ console . warn ( '[helios-verifier] eth_call failed:' , err ) ;
62112 return SKIP ;
63113 }
64-
65114 const rpcValue = decodeUint256 ( rpcBalance ) ;
66115 const heliosValue = decodeUint256 ( heliosBalanceHex ) ;
67-
68- console . log ( '[helios-verifier] rpcValue:' , rpcValue . toString ( ) , 'heliosValue:' , heliosValue . toString ( ) ) ;
69-
70116 if ( rpcValue === heliosValue ) {
71117 return { verified : true , tampered : false , message : 'Balance verified by Helios light client.' , provenBalance : heliosBalanceHex } ;
72118 }
73-
74- console . error ( '[helios-verifier] MISMATCH DETECTED - RPC is lying!' ) ;
75-
119+ console . error ( `[helios-verifier] MISMATCH: RPC=${ rpcValue } Helios=${ heliosValue } ` ) ;
76120 return {
77121 verified : false ,
78122 tampered : true ,
79- message : `Your RPC provider returned a balance of ${ rpcValue . toString ( ) } but the Helios light client cryptographically proved the real on-chain balance is ${ heliosValue . toString ( ) } . Your RPC provider may be lying. Consider switching to a trusted provider .` ,
123+ message : `Your RPC provider returned ${ rpcValue . toString ( ) } but Helios proved the real balance is ${ heliosValue . toString ( ) } . Your RPC provider may be lying.` ,
80124 provenBalance : heliosBalanceHex ,
81125 } ;
82126}
0 commit comments