diff --git a/beacon-light-client/circom/circuits/light_client.circom b/beacon-light-client/circom/circuits/light_client.circom index 7a4fa4933..31887f453 100644 --- a/beacon-light-client/circom/circuits/light_client.circom +++ b/beacon-light-client/circom/circuits/light_client.circom @@ -18,11 +18,14 @@ template LightClient(N) { signal input prevHeaderHash[256]; signal input nextHeaderHash[256]; - signal input prevHeaderStateRoot[256]; - signal input prevHeaderStateRootBranch[3][256]; + signal input prevFinalizedHeaderRoot[256]; + signal input prevFinalizedHeaderRootBranch[9][256]; + + signal input prevHeaderFinalizedStateRoot[256]; + signal input prevHeaderFinalizedStateRootBranch[3][256]; signal input prevHeaderFinalizedSlot; - signal input prevHeaderFinalizedSlotBranch[9][256]; + signal input prevHeaderFinalizedSlotBranch[3][256]; signal input nextHeaderSlot; signal input nextHeaderSlotBranch[3][256]; @@ -52,22 +55,6 @@ template LightClient(N) { signal output output_commitment[2]; - component isValidMerkleBranchPrevHeaderStateRoot = IsValidMerkleBranch(3); - - for(var i = 0; i < 256; i++) { - isValidMerkleBranchPrevHeaderStateRoot.leaf[i] <== prevHeaderStateRoot[i]; - isValidMerkleBranchPrevHeaderStateRoot.root[i] <== prevHeaderHash[i]; - } - - for(var i = 0; i < 3; i++) { - for(var j = 0; j < 256; j++) { - isValidMerkleBranchPrevHeaderStateRoot.branch[i][j] <== prevHeaderStateRootBranch[i][j]; - } - } - - isValidMerkleBranchPrevHeaderStateRoot.index <== 11; - isValidMerkleBranchPrevHeaderStateRoot.out === 1; - component signatureSlotGreaterThanNext = GreaterThan(64); signatureSlotGreaterThanNext.in[0] <== signatureSlot; signatureSlotGreaterThanNext.in[1] <== nextHeaderSlot; @@ -104,22 +91,38 @@ template LightClient(N) { component nextHeaderSlotSSZ = SSZNum(64); nextHeaderSlotSSZ.in <== nextHeaderSlot; - component isValidMerkleBranchPrevHeaderSlot = IsValidMerkleBranch(9); + component isValidMerkleBranchPrevHeaderSlot = IsValidMerkleBranch(3); for(var i = 0; i < 256; i++) { isValidMerkleBranchPrevHeaderSlot.leaf[i] <== prevHeaderFinalizedSlotSSZ.out[i]; - isValidMerkleBranchPrevHeaderSlot.root[i] <== prevHeaderStateRoot[i]; + isValidMerkleBranchPrevHeaderSlot.root[i] <== prevFinalizedHeaderRoot[i]; } - for(var i = 0; i < 9; i++) { + for(var i = 0; i < 3; i++) { for(var j = 0; j < 256; j++) { isValidMerkleBranchPrevHeaderSlot.branch[i][j] <== prevHeaderFinalizedSlotBranch[i][j]; } } - isValidMerkleBranchPrevHeaderSlot.index <== 840; + isValidMerkleBranchPrevHeaderSlot.index <== 8; isValidMerkleBranchPrevHeaderSlot.out === 1; + component isValidMerkleBranchPrevHeaderFinalizedStateRoot = IsValidMerkleBranch(3); + + for(var i = 0; i < 256; i++) { + isValidMerkleBranchPrevHeaderFinalizedStateRoot.leaf[i] <== prevHeaderFinalizedStateRoot[i]; + isValidMerkleBranchPrevHeaderFinalizedStateRoot.root[i] <== prevFinalizedHeaderRoot[i]; + } + + for(var i = 0; i < 3; i++) { + for(var j = 0; j < 256; j++) { + isValidMerkleBranchPrevHeaderFinalizedStateRoot.branch[i][j] <== prevHeaderFinalizedStateRootBranch[i][j]; + } + } + + isValidMerkleBranchPrevHeaderFinalizedStateRoot.index <== 11; + isValidMerkleBranchPrevHeaderFinalizedStateRoot.out === 1; + component isValidMerkleBranchNextHeaderSlot = IsValidMerkleBranch(3); @@ -203,6 +206,23 @@ template LightClient(N) { hasher.aggregatedKey[i] <== aggregatedKey[i]; } + component isValidMerkleBranchPrevFinality = IsValidMerkleBranch(9); + + for(var i = 0; i < 9; i++) { + for(var j = 0; j < 256; j++) { + isValidMerkleBranchPrevFinality.branch[i][j] <== prevFinalizedHeaderRootBranch[i][j]; + } + } + + for(var i = 0; i < 256; i++) { + isValidMerkleBranchPrevFinality.leaf[i] <== prevFinalizedHeaderRoot[i]; + isValidMerkleBranchPrevFinality.root[i] <== prevHeaderHash[i]; + } + + isValidMerkleBranchPrevFinality.index <== 745; + + isValidMerkleBranchPrevFinality.out === 1; + component isValidMerkleBranchFinality = IsValidMerkleBranch(9); for(var i = 0; i < 9; i++) { @@ -250,7 +270,7 @@ template LightClient(N) { } for(var i = 0; i < 256; i++) { - isValidMerkleBranchSyncCommittee.root[i] <== prevHeaderStateRoot[i]; + isValidMerkleBranchSyncCommittee.root[i] <== prevHeaderFinalizedStateRoot[i]; } component arePeriodsEqual = IsEqual(); diff --git a/beacon-light-client/circom/scripts/light_client/config.json b/beacon-light-client/circom/scripts/light_client/config.json index 9e294eee3..1fd582c1b 100644 --- a/beacon-light-client/circom/scripts/light_client/config.json +++ b/beacon-light-client/circom/scripts/light_client/config.json @@ -6,5 +6,8 @@ "rapidSnarkProverPath": "../../../../../vendor/rapidsnark/build/prover", "zkeyFilePath": "../../../build/light_client/light_client_0.zkey", "witnessGeneratorPath": "../../../build/light_client/light_client_cpp/light_client", - "updatePolingTime": 60000 + "updatePolingTime": 450000, + "lightClientAddress": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", + "etherJsonRpcProvider": "http://127.0.0.1:8545", + "privateKey": "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" } diff --git a/beacon-light-client/circom/scripts/light_client/get_ligth_client_input.ts b/beacon-light-client/circom/scripts/light_client/get_ligth_client_input.ts index 865debaf1..f820730f6 100644 --- a/beacon-light-client/circom/scripts/light_client/get_ligth_client_input.ts +++ b/beacon-light-client/circom/scripts/light_client/get_ligth_client_input.ts @@ -133,7 +133,7 @@ export async function getProofInput( let finalizedHeaderHash = ssz.phase0.BeaconBlockHeader.hashTreeRoot(finalizedHeader); - let finalityBranch = update.finality_branch.map(x => hexToBits(x)); + let finalityBranchBits = update.finality_branch.map(x => hexToBits(x)); let finalizedHeaderBodyRootProof = getMerkleProof( ssz.phase0.BeaconBlockHeader, @@ -148,12 +148,20 @@ export async function getProofInput( let prevFinalizedHeader = ssz.phase0.BeaconBlockHeader.fromJson( prevUpdate.finalized_header.beacon, ); + + let prevFinalizedHeaderBranch = prevUpdate.finality_branch; let prevHeaderFinalizedSlotBranch = getMerkleProof( ssz.phase0.BeaconBlockHeader, ['slot'], prevFinalizedHeader, ).map(x => hexToBits(x)); + let prevFinalizedHeaderStateProof = getMerkleProof( + ssz.phase0.BeaconBlockHeader, + ['state_root'], + prevFinalizedHeader, + ); + const executionPayload = ExecutionPayload.fromJson( update.finalized_header.execution, ); @@ -176,16 +184,27 @@ export async function getProofInput( return { prevHeaderHash: hexToBits(bytesToHex(prevBlockHeaderHash)), nextHeaderHash: hexToBits(bytesToHex(nextBlockHeaderHash)), - - prevHeaderStateRoot: hexToBits(bytesToHex(prevBlockHeader.stateRoot)), - prevHeaderStateRootBranch: prevBlockHeaderStateRootProof, - - prevHeaderFinalizedSlot: prevFinalizedHeader.slot, - prevHeaderFinalizedSlotBranch: [ - ...prevHeaderFinalizedSlotBranch, + prevFinalizedHeaderRoot: hexToBits( + bytesToHex( + ssz.phase0.BeaconBlockHeader.hashTreeRoot(prevFinalizedHeader), + ), + ), + prevFinalizedHeaderRootBranch: [ ...prevHeaderFinalizedBranch, + ...prevBlockHeaderStateRootProof, ], + prevHeaderFinalizedStateRoot: hexToBits( + prevUpdate.finalized_header.beacon.state_root, + ), + prevHeaderFinalizedStateRootBranch: prevFinalizedHeaderStateProof.map(x => + hexToBits(x), + ), + // prevHeaderStateRoot: hexToBits(bytesToHex(prevBlockHeader.stateRoot)), + // prevHeaderStateRootBranch: prevBlockHeaderStateRootProof, + + prevHeaderFinalizedSlot: prevFinalizedHeader.slot, + prevHeaderFinalizedSlotBranch: [...prevHeaderFinalizedSlotBranch], nextHeaderSlot: nextBlockHeader.slot, nextHeaderSlotBranch: nextHeaderSlotBranch, @@ -195,16 +214,17 @@ export async function getProofInput( Number(update.signature_slot), ), finalizedHeaderSlotSyncCommitteePeriod: computeSyncCommitteePeriodAt( - Number(prevUpdate.finalized_header.beacon.slot), + prevFinalizedHeader.slot, ), - finalizedHeaderRoot: hexToBits(bytesToHex(finalizedHeaderHash)), finalizedHeaderBranch: [ - ...finalityBranch, + ...finalityBranchBits, ...nextBlockHeaderStateRootProof, ], - execution_state_root: hexToBits(bytesToHex(executionPayload.state_root)), + execution_state_root: hexToBits( + update.finalized_header.execution.state_root, + ), execution_state_root_branch: [ ...executionPayloadStateProof, ...update.finalized_header.execution_branch, @@ -225,11 +245,7 @@ export async function getProofInput( bigint_to_array(55, 7, x.toAffine()[1].value), ]), aggregatedKey: hexToBits(prevUpdate.sync_committee.aggregate_pubkey, 384), - syncCommitteeBranch: [ - ...syncCommitteeBranch, - ...prevBlockHeaderStateRootProof, - ], - + syncCommitteeBranch: [...syncCommitteeBranch], bitmask: bitmask.toBoolArray().map(x => (x ? '1' : '0')), signature: [ [ diff --git a/beacon-light-client/circom/scripts/light_client/light_client.abi.json b/beacon-light-client/circom/scripts/light_client/light_client.abi.json new file mode 100644 index 000000000..050f30595 --- /dev/null +++ b/beacon-light-client/circom/scripts/light_client/light_client.abi.json @@ -0,0 +1,141 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "__optimistic_header_root", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "__finalized_header_root", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "__execution_state_root", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "execution_state_root", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "finalized_header_root", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "attested_header_root", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "finalized_header_root", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "finalized_execution_state_root", + "type": "bytes32" + }, + { + "internalType": "uint256[2]", + "name": "a", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2][2]", + "name": "b", + "type": "uint256[2][2]" + }, + { + "internalType": "uint256[2]", + "name": "c", + "type": "uint256[2]" + } + ], + "internalType": "struct BeaconLightClient.LightClientUpdate", + "name": "update", + "type": "tuple" + } + ], + "name": "light_client_update", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "optimistic_header_root", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "a", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2][2]", + "name": "b", + "type": "uint256[2][2]" + }, + { + "internalType": "uint256[2]", + "name": "c", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2]", + "name": "input", + "type": "uint256[2]" + } + ], + "name": "verifyProof", + "outputs": [ + { + "internalType": "bool", + "name": "r", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/beacon-light-client/circom/scripts/light_client/relayer-helper.ts b/beacon-light-client/circom/scripts/light_client/relayer-helper.ts index 0780edc80..f9432f497 100644 --- a/beacon-light-client/circom/scripts/light_client/relayer-helper.ts +++ b/beacon-light-client/circom/scripts/light_client/relayer-helper.ts @@ -7,6 +7,7 @@ export const UPDATE_POLING_QUEUE = 'update_poling'; export const WITNESS_GENERATOR_QUEUE = 'witness'; export const INPUT_GENERATOR_QUEUE = 'input'; export const PROOF_GENERATOR_QUEUE = 'proof'; +export const PUBLISH_ONCHAIN_QUEUE = 'publish_on_chain'; export const EPOCHS_PER_SYNC_COMMITTEE_PERIOD = 256; export const SLOTS_PER_EPOCH = 32; @@ -23,6 +24,13 @@ export type ProofInputType = { proofInput: WitnessGeneratorInput; }; +export type ProofResultType = { + prevUpdateSlot: number; + updateSlot: number; + proof: Proof; + proofInput: WitnessGeneratorInput; +} + export type State = { lastDownloadedUpdate: number; lastUpdateOnChain: number; @@ -83,15 +91,22 @@ export interface SyncAggregate { sync_committee_signature: string; } +export interface GetUpdate { + prevUpdateSlot: number; + updateSlot: number; +} + export interface WitnessGeneratorInput { + prevFinalizedHeaderRoot: string[]; + prevFinalizedHeaderRootBranch: string[][]; + prevHeaderFinalizedStateRoot: string[]; + prevHeaderFinalizedStateRootBranch: string[][]; points: string[][][]; signatureSlot: string; signatureSlotSyncCommitteePeriod: number; finalizedHeaderSlotSyncCommitteePeriod: number; prevHeaderHash: string[]; nextHeaderHash: string[]; - prevHeaderStateRoot: string[]; - prevHeaderStateRootBranch: string[][]; prevHeaderFinalizedSlotBranch: string[][]; prevHeaderFinalizedSlot: number; nextHeaderSlotBranch: string[][]; diff --git a/beacon-light-client/circom/scripts/light_client/relayer.ts b/beacon-light-client/circom/scripts/light_client/relayer.ts index 6296e1877..599834417 100644 --- a/beacon-light-client/circom/scripts/light_client/relayer.ts +++ b/beacon-light-client/circom/scripts/light_client/relayer.ts @@ -1,8 +1,7 @@ import { Queue, QueueEvents } from 'bullmq'; -import { readFile, writeFile } from 'fs/promises'; import { PROOF_GENERATOR_QUEUE, - State, + PUBLISH_ONCHAIN_QUEUE, UPDATE_POLING_QUEUE, } from './relayer-helper'; import * as config from './config.json'; @@ -15,7 +14,7 @@ const updateQueue = new Queue(UPDATE_POLING_QUEUE, { }); updateQueue.add('downloadUpdate', undefined, { - repeat: { every: config.updatePolingTime }, + repeat: { every: config.updatePolingTime, immediately: true }, }); const proofGeneratorEvents = new QueueEvents(PROOF_GENERATOR_QUEUE, { @@ -25,23 +24,6 @@ const proofGeneratorEvents = new QueueEvents(PROOF_GENERATOR_QUEUE, { }, }); -proofGeneratorEvents.on('completed', async (job, data) => { - // here will publish proof on chain - while (true) { - const state: State = JSON.parse(await readFile('state.json', 'utf-8')); - let reutrnValue = job.returnvalue as any; - if (reutrnValue.prevUpdateSlot === state.lastUpdateOnChain) { - state.lastUpdateOnChain = reutrnValue.updateSlot; - await writeFile('state.json', JSON.stringify(state)); - console.log('WORK DONE'); - return; - } else { - // WAIT UNTIL IT IS TIME FOR YOU POLL EVERY 5 seconds - await new Promise(r => setTimeout(r, 5000)); - } - } -}); - proofGeneratorEvents.on('failed', error => { console.error('Proofing generation failed'); @@ -60,3 +42,16 @@ getUpdateEvents.on('failed', error => { console.log(error); }); + +const publishOnChainEvents = new QueueEvents(PUBLISH_ONCHAIN_QUEUE, { + connection: { + host: config.redisHost, + port: config.redisPort, + }, +}); + +publishOnChainEvents.on('failed', error => { + console.log('error while publishing on chain'); + + console.log(error); +}); diff --git a/beacon-light-client/circom/scripts/light_client/state.json b/beacon-light-client/circom/scripts/light_client/state.json index 845a06dd0..3edbdffed 100644 --- a/beacon-light-client/circom/scripts/light_client/state.json +++ b/beacon-light-client/circom/scripts/light_client/state.json @@ -1 +1 @@ -{"lastDownloadedUpdate":185668,"lastUpdateOnChain":157463} \ No newline at end of file +{"lastDownloadedUpdate":237593,"lastUpdateOnChain":237556} \ No newline at end of file diff --git a/beacon-light-client/circom/scripts/light_client/workers/get-update-worker.ts b/beacon-light-client/circom/scripts/light_client/workers/get-update-worker.ts index ad68a19e8..e42a194a5 100644 --- a/beacon-light-client/circom/scripts/light_client/workers/get-update-worker.ts +++ b/beacon-light-client/circom/scripts/light_client/workers/get-update-worker.ts @@ -86,7 +86,7 @@ async function extendPrevUpdateWithSyncCommittee( signature_slot: number, ) { const beaconStateSZZ = await fetch( - `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v2/debug/beacon/states/${prevUpdate.data.attested_header.beacon.slot}`, + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v2/debug/beacon/states/${prevUpdate.data.finalized_header.beacon.slot}`, { headers: { Accept: 'application/octet-stream', diff --git a/beacon-light-client/circom/scripts/light_client/workers/proof-generator-worker.ts b/beacon-light-client/circom/scripts/light_client/workers/proof-generator-worker.ts index e873c1841..9fcd0306f 100644 --- a/beacon-light-client/circom/scripts/light_client/workers/proof-generator-worker.ts +++ b/beacon-light-client/circom/scripts/light_client/workers/proof-generator-worker.ts @@ -1,6 +1,6 @@ -import { Worker } from 'bullmq'; +import { Worker, Queue } from 'bullmq'; import { exec as _exec } from 'child_process'; -import { rm, writeFile } from 'fs/promises'; +import { readFile, rm, writeFile } from 'fs/promises'; import path from 'path'; import { promisify } from 'util'; import { @@ -9,11 +9,20 @@ import { PROOF_GENERATOR_QUEUE, RELAYER_INPUTS_FOLDER, RELAYER_PROOFS_FOLDER, + PUBLISH_ONCHAIN_QUEUE, + ProofResultType, } from '../relayer-helper'; import * as config from '../config.json'; const exec = promisify(_exec); +const proofPublishQueue = new Queue(PUBLISH_ONCHAIN_QUEUE, { + connection: { + host: config.redisHost, + port: config.redisPort, + }, +}); + new Worker( PROOF_GENERATOR_QUEUE, async job => { @@ -70,10 +79,41 @@ new Worker( ), ); - return { + const proof = JSON.parse( + await readFile( + path.join( + __dirname, + '..', + RELAYER_PROOFS_FOLDER, + `proof_${job.data.prevUpdateSlot}_${job.data.updateSlot}.json`, + ), + 'utf-8', + ), + ); + + const publicVars = JSON.parse( + await readFile( + path.join( + __dirname, + '..', + RELAYER_PROOFS_FOLDER, + `public_${job.data.prevUpdateSlot}_${job.data.updateSlot}.json`, + ), + 'utf-8', + ), + ); + + await proofPublishQueue.add('proofGenerate', { prevUpdateSlot: job.data.prevUpdateSlot, updateSlot: job.data.updateSlot, - }; + proofInput: job.data.proofInput, + proof: { + pi_a: proof.pi_a, + pi_b: proof.pi_b, + pi_c: proof.pi_c, + public: publicVars, + }, + }); }, { connection: { diff --git a/beacon-light-client/circom/scripts/light_client/workers/publish-on-chain-worker.ts b/beacon-light-client/circom/scripts/light_client/workers/publish-on-chain-worker.ts new file mode 100644 index 000000000..63033f615 --- /dev/null +++ b/beacon-light-client/circom/scripts/light_client/workers/publish-on-chain-worker.ts @@ -0,0 +1,95 @@ +import { Worker, Queue } from 'bullmq'; +import { ethers } from 'ethers'; +import { readFileSync } from 'fs'; +import { readFile, writeFile } from 'fs/promises'; +import * as config from '../config.json'; +import { + ProofResultType, + PUBLISH_ONCHAIN_QUEUE, + State, +} from '../relayer-helper'; +import { groth16 } from 'snarkjs'; + +const provider = new ethers.providers.JsonRpcProvider( + config.etherJsonRpcProvider, +); + +const wallet = new ethers.Wallet(config.privateKey, provider); + +const light_client_abi = JSON.parse( + readFileSync('../light_client.abi.json', 'utf-8'), +); + +const lightClientContract = new ethers.Contract( + config.lightClientAddress, + light_client_abi, + wallet, +); + +new Worker( + PUBLISH_ONCHAIN_QUEUE, + async job => { + while (true) { + const state: State = JSON.parse(await readFile('../state.json', 'utf-8')); + console.log('here'); + console.log(job.data.prevUpdateSlot); + if (state.lastUpdateOnChain === job.data.prevUpdateSlot) { + const calldata = await groth16.exportSolidityCallData( + job.data.proof, + job.data.proof.public, + ); + + const argv: string[] = calldata + .replace(/["[\]\s]/g, '') + .split(',') + .map(x => BigInt(x).toString()); + + const a = [argv[0], argv[1]]; + const b = [ + [argv[2], argv[3]], + [argv[4], argv[5]], + ]; + const c = [argv[6], argv[7]]; + + const transaction = await lightClientContract.light_client_update( + { + attested_header_root: + '0x' + + BigInt('0b' + job.data.proofInput.nextHeaderHash.join('')) + .toString(16) + .padStart(64, '0'), + finalized_header_root: + '0x' + + BigInt('0b' + job.data.proofInput.finalizedHeaderRoot.join('')) + .toString(16) + .padStart(64, '0'), + finalized_execution_state_root: + '0x' + + BigInt('0b' + job.data.proofInput.execution_state_root.join('')) + .toString(16) + .padStart(64, '0'), + a: a, + b: b, + c: c, + }, + { gasLimit: 1000000 }, + ); + + await transaction.wait(); + + state.lastUpdateOnChain = job.data.updateSlot; + await writeFile('../state.json', JSON.stringify(state)); + return; + } else { + // WAIT FOR THE UPDATE + await new Promise(r => setTimeout(r, 5000)); + } + } + }, + { + connection: { + host: config.redisHost, + port: config.redisPort, + }, + }, +); diff --git a/beacon-light-client/solidity/contracts/bridge/src/truth/eth/BeaconLightClient.sol b/beacon-light-client/solidity/contracts/bridge/src/truth/eth/BeaconLightClient.sol index 2e16590d4..2ec0472d0 100644 --- a/beacon-light-client/solidity/contracts/bridge/src/truth/eth/BeaconLightClient.sol +++ b/beacon-light-client/solidity/contracts/bridge/src/truth/eth/BeaconLightClient.sol @@ -7,33 +7,39 @@ import '../../spec/BeaconChain.sol'; contract BeaconLightClient is LightClientUpdateVerifier { struct LightClientUpdate { bytes32 attested_header_root; - bytes32 finalized_header_root; - bytes32 finalized_execution_state_root; - uint256[2] a; uint256[2][2] b; uint256[2] c; } - bytes32 optimistic_header_root; + bytes32 _optimistic_header_root; - bytes32 finalized_header_root; + bytes32 _finalized_header_root; - bytes32 finalized_execution_state_root; + bytes32 _finalized_execution_state_root; constructor( - bytes32 _finalized_header_root - bytes32 _execution_state_root + bytes32 __optimistic_header_root, + bytes32 __finalized_header_root, + bytes32 __execution_state_root ) { - optimistic_header_root = _finalized_header_root; - finalized_header_root = _finalized_header_root; - finalized_execution_state_root = _execution_state_root; + _optimistic_header_root = __optimistic_header_root; + _finalized_header_root = __finalized_header_root; + _finalized_execution_state_root = __execution_state_root; + } + + function execution_state_root() public view returns (bytes32) { + return _finalized_execution_state_root; + } + + function optimistic_header_root() public view returns (bytes32) { + return _optimistic_header_root; } - function state_root() public view returns (bytes32) { - return finalized_header.state_root; + function finalized_header_root() public view returns (bytes32) { + return _finalized_header_root; } function light_client_update(LightClientUpdate calldata update) @@ -45,18 +51,16 @@ contract BeaconLightClient is LightClientUpdateVerifier { update.a, update.b, update.c, - finalized_header_root, + optimistic_header_root(), update.attested_header_root, update.finalized_header_root, - update.finalized_execution_state_root, + update.finalized_execution_state_root ), '!proof' ); - // Maybe we should also validate if header.slot > finalized_header.slot - - optimistic_header_root = update.attested_header_root; - finalized_header_root = update.finalized_header; - finalized_execution_state_root = update.finalized_execution_state_root; + _optimistic_header_root = update.attested_header_root; + _finalized_header_root = update.finalized_header_root; + _finalized_execution_state_root = update.finalized_execution_state_root; } } diff --git a/beacon-light-client/solidity/contracts/bridge/src/utils/LightClientUpdateVerifier.sol b/beacon-light-client/solidity/contracts/bridge/src/utils/LightClientUpdateVerifier.sol index 6a68a67a9..07b0b0c03 100644 --- a/beacon-light-client/solidity/contracts/bridge/src/utils/LightClientUpdateVerifier.sol +++ b/beacon-light-client/solidity/contracts/bridge/src/utils/LightClientUpdateVerifier.sol @@ -19,37 +19,14 @@ contract LightClientUpdateVerifier is Verifier { execution_state_root ); - uint256 commitment1 = reverseBits( - (uint256(commitment) & (((1 << 253) - 1) << 3)) >> 3, - 253 - ); - - uint256 commitment2 = reverseBits( - (uint256(commitment) & ((1 << 3) - 1)), - 3 - ); - uint256[2] memory input; - input[0] = prev_header_hash1; - input[1] = prev_header_hash2; + input[0] = (uint256(commitment) & (((1 << 253) - 1) << 3)) >> 3; + input[1] = (uint256(commitment) & ((1 << 3) - 1)); return verifyProof(a, b, c, input); } - // TODO handle this in circuit - function reverseBits(uint256 x, uint256 size) private pure returns (uint256) { - require(size <= 256); - - uint256 result = 0; - for (uint256 i = 0; i < size; i++) { - result = (result << 1) | (x & 1); - x = x >> 1; - } - - return result; - } - function hash( bytes32 a, bytes32 b, diff --git a/beacon-light-client/solidity/contracts/bridge/src/utils/Verifier.sol b/beacon-light-client/solidity/contracts/bridge/src/utils/Verifier.sol index 1ed76eaeb..7098422d6 100644 --- a/beacon-light-client/solidity/contracts/bridge/src/utils/Verifier.sol +++ b/beacon-light-client/solidity/contracts/bridge/src/utils/Verifier.sol @@ -14,30 +14,37 @@ pragma solidity 0.8.9; library Pairing { - struct G1Point { - uint X; - uint Y; - } - // Encoding of field elements is: X[0] * z + X[1] - struct G2Point { - uint[2] X; - uint[2] Y; - } - /// @return the generator of G1 - function P1() internal pure returns (G1Point memory) { - return G1Point(1, 2); - } - /// @return the generator of G2 - function P2() internal pure returns (G2Point memory) { - // Original code point - return G2Point( - [11559732032986387107991004021392285783925812861821192530917403151452391805634, - 10857046999023057135944570762232829481370756359578518086990519993285655852781], - [4082367875863433681332203403145435568316851327593401208105741076214120093531, - 8495653923123431417604973247489272438418190587263600148770280649306958101930] - ); + struct G1Point { + uint256 X; + uint256 Y; + } + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint256[2] X; + uint256[2] Y; + } + + /// @return the generator of G1 + function P1() internal pure returns (G1Point memory) { + return G1Point(1, 2); + } -/* + /// @return the generator of G2 + function P2() internal pure returns (G2Point memory) { + // Original code point + return + G2Point( + [ + 11559732032986387107991004021392285783925812861821192530917403151452391805634, + 10857046999023057135944570762232829481370756359578518086990519993285655852781 + ], + [ + 4082367875863433681332203403145435568316851327593401208105741076214120093531, + 8495653923123431417604973247489272438418190587263600148770280649306958101930 + ] + ); + + /* // Changed by Jordi point return G2Point( [10857046999023057135944570762232829481370756359578518086990519993285655852781, @@ -46,226 +53,286 @@ library Pairing { 4082367875863433681332203403145435568316851327593401208105741076214120093531] ); */ + } + + /// @return r the negation of p, i.e. p.addition(p.negate()) should be zero. + function negate(G1Point memory p) internal pure returns (G1Point memory r) { + // The prime q in the base field F_q for G1 + uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + if (p.X == 0 && p.Y == 0) return G1Point(0, 0); + return G1Point(p.X, q - (p.Y % q)); + } + + /// @return r the sum of two points of G1 + function addition(G1Point memory p1, G1Point memory p2) + internal + view + returns (G1Point memory r) + { + uint256[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success + case 0 { + invalid() + } } - /// @return r the negation of p, i.e. p.addition(p.negate()) should be zero. - function negate(G1Point memory p) internal pure returns (G1Point memory r) { - // The prime q in the base field F_q for G1 - uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - if (p.X == 0 && p.Y == 0) - return G1Point(0, 0); - return G1Point(p.X, q - (p.Y % q)); - } - /// @return r the sum of two points of G1 - function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) { - uint[4] memory input; - input[0] = p1.X; - input[1] = p1.Y; - input[2] = p2.X; - input[3] = p2.Y; - bool success; - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } - require(success,"pairing-add-failed"); - } - /// @return r the product of a point on G1 and a scalar, i.e. - /// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p. - function scalar_mul(G1Point memory p, uint s) internal view returns (G1Point memory r) { - uint[3] memory input; - input[0] = p.X; - input[1] = p.Y; - input[2] = s; - bool success; - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } - require (success,"pairing-mul-failed"); - } - /// @return the result of computing the pairing check - /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 - /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should - /// return true. - function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) { - require(p1.length == p2.length,"pairing-lengths-failed"); - uint elements = p1.length; - uint inputSize = elements * 6; - uint[] memory input = new uint[](inputSize); - for (uint i = 0; i < elements; i++) - { - input[i * 6 + 0] = p1[i].X; - input[i * 6 + 1] = p1[i].Y; - input[i * 6 + 2] = p2[i].X[0]; - input[i * 6 + 3] = p2[i].X[1]; - input[i * 6 + 4] = p2[i].Y[0]; - input[i * 6 + 5] = p2[i].Y[1]; - } - uint[1] memory out; - bool success; - // solium-disable-next-line security/no-inline-assembly - assembly { - success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) - // Use "invalid" to make gas estimation work - switch success case 0 { invalid() } - } - require(success,"pairing-opcode-failed"); - return out[0] != 0; - } - /// Convenience method for a pairing check for two pairs. - function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) internal view returns (bool) { - G1Point[] memory p1 = new G1Point[](2); - G2Point[] memory p2 = new G2Point[](2); - p1[0] = a1; - p1[1] = b1; - p2[0] = a2; - p2[1] = b2; - return pairing(p1, p2); + require(success, 'pairing-add-failed'); + } + + /// @return r the product of a point on G1 and a scalar, i.e. + /// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p. + function scalar_mul(G1Point memory p, uint256 s) + internal + view + returns (G1Point memory r) + { + uint256[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success + case 0 { + invalid() + } } - /// Convenience method for a pairing check for three pairs. - function pairingProd3( - G1Point memory a1, G2Point memory a2, - G1Point memory b1, G2Point memory b2, - G1Point memory c1, G2Point memory c2 - ) internal view returns (bool) { - G1Point[] memory p1 = new G1Point[](3); - G2Point[] memory p2 = new G2Point[](3); - p1[0] = a1; - p1[1] = b1; - p1[2] = c1; - p2[0] = a2; - p2[1] = b2; - p2[2] = c2; - return pairing(p1, p2); + require(success, 'pairing-mul-failed'); + } + + /// @return the result of computing the pairing check + /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should + /// return true. + function pairing(G1Point[] memory p1, G2Point[] memory p2) + internal + view + returns (bool) + { + require(p1.length == p2.length, 'pairing-lengths-failed'); + uint256 elements = p1.length; + uint256 inputSize = elements * 6; + uint256[] memory input = new uint256[](inputSize); + for (uint256 i = 0; i < elements; i++) { + input[i * 6 + 0] = p1[i].X; + input[i * 6 + 1] = p1[i].Y; + input[i * 6 + 2] = p2[i].X[0]; + input[i * 6 + 3] = p2[i].X[1]; + input[i * 6 + 4] = p2[i].Y[0]; + input[i * 6 + 5] = p2[i].Y[1]; } - /// Convenience method for a pairing check for four pairs. - function pairingProd4( - G1Point memory a1, G2Point memory a2, - G1Point memory b1, G2Point memory b2, - G1Point memory c1, G2Point memory c2, - G1Point memory d1, G2Point memory d2 - ) internal view returns (bool) { - G1Point[] memory p1 = new G1Point[](4); - G2Point[] memory p2 = new G2Point[](4); - p1[0] = a1; - p1[1] = b1; - p1[2] = c1; - p1[3] = d1; - p2[0] = a2; - p2[1] = b2; - p2[2] = c2; - p2[3] = d2; - return pairing(p1, p2); + uint256[1] memory out; + bool success; + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall( + sub(gas(), 2000), + 8, + add(input, 0x20), + mul(inputSize, 0x20), + out, + 0x20 + ) + // Use "invalid" to make gas estimation work + switch success + case 0 { + invalid() + } } + require(success, 'pairing-opcode-failed'); + return out[0] != 0; + } + + /// Convenience method for a pairing check for two pairs. + function pairingProd2( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2 + ) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](2); + G2Point[] memory p2 = new G2Point[](2); + p1[0] = a1; + p1[1] = b1; + p2[0] = a2; + p2[1] = b2; + return pairing(p1, p2); + } + + /// Convenience method for a pairing check for three pairs. + function pairingProd3( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2, + G1Point memory c1, + G2Point memory c2 + ) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](3); + G2Point[] memory p2 = new G2Point[](3); + p1[0] = a1; + p1[1] = b1; + p1[2] = c1; + p2[0] = a2; + p2[1] = b2; + p2[2] = c2; + return pairing(p1, p2); + } + + /// Convenience method for a pairing check for four pairs. + function pairingProd4( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2, + G1Point memory c1, + G2Point memory c2, + G1Point memory d1, + G2Point memory d2 + ) internal view returns (bool) { + G1Point[] memory p1 = new G1Point[](4); + G2Point[] memory p2 = new G2Point[](4); + p1[0] = a1; + p1[1] = b1; + p1[2] = c1; + p1[3] = d1; + p2[0] = a2; + p2[1] = b2; + p2[2] = c2; + p2[3] = d2; + return pairing(p1, p2); + } } -contract Verifier { - using Pairing for *; - struct VerifyingKey { - Pairing.G1Point alfa1; - Pairing.G2Point beta2; - Pairing.G2Point gamma2; - Pairing.G2Point delta2; - Pairing.G1Point[] IC; - } - struct Proof { - Pairing.G1Point A; - Pairing.G2Point B; - Pairing.G1Point C; - } - function verifyingKey() internal pure returns (VerifyingKey memory vk) { - vk.alfa1 = Pairing.G1Point( - 20491192805390485299153009773594534940189261866228447918068658471970481763042, - 9383485363053290200918347156157836566562967994039712273449902621266178545958 - ); - vk.beta2 = Pairing.G2Point( - [4252822878758300859123897981450591353533073413197771768651442665752259397132, - 6375614351688725206403948262868962793625744043794305715222011528459656738731], - [21847035105528745403288232691147584728191162732299865338377159692350059136679, - 10505242626370262277552901082094356697409835680220590971873171140371331206856] - ); - vk.gamma2 = Pairing.G2Point( - [11559732032986387107991004021392285783925812861821192530917403151452391805634, - 10857046999023057135944570762232829481370756359578518086990519993285655852781], - [4082367875863433681332203403145435568316851327593401208105741076214120093531, - 8495653923123431417604973247489272438418190587263600148770280649306958101930] - ); - vk.delta2 = Pairing.G2Point( - [11559732032986387107991004021392285783925812861821192530917403151452391805634, - 10857046999023057135944570762232829481370756359578518086990519993285655852781], - [4082367875863433681332203403145435568316851327593401208105741076214120093531, - 8495653923123431417604973247489272438418190587263600148770280649306958101930] - ); - vk.IC = new Pairing.G1Point[](5); +contract Verifier { + using Pairing for *; + struct VerifyingKey { + Pairing.G1Point alfa1; + Pairing.G2Point beta2; + Pairing.G2Point gamma2; + Pairing.G2Point delta2; + Pairing.G1Point[] IC; + } + struct Proof { + Pairing.G1Point A; + Pairing.G2Point B; + Pairing.G1Point C; + } - vk.IC[0] = Pairing.G1Point( - 12341254398012831539511529514141920531233310925559640868133205893740926624749, - 1898346778999733876099328904356803438781336221250567894820659652669409421709 - ); + function verifyingKey() internal pure returns (VerifyingKey memory vk) { + vk.alfa1 = Pairing.G1Point( + 20491192805390485299153009773594534940189261866228447918068658471970481763042, + 9383485363053290200918347156157836566562967994039712273449902621266178545958 + ); - vk.IC[1] = Pairing.G1Point( - 20581758020956979671791380396676180914933879489933044097984142919872524264433, - 9909799947047067906035029346433967103598628542141844188201546463877414532390 - ); + vk.beta2 = Pairing.G2Point( + [ + 4252822878758300859123897981450591353533073413197771768651442665752259397132, + 6375614351688725206403948262868962793625744043794305715222011528459656738731 + ], + [ + 21847035105528745403288232691147584728191162732299865338377159692350059136679, + 10505242626370262277552901082094356697409835680220590971873171140371331206856 + ] + ); + vk.gamma2 = Pairing.G2Point( + [ + 11559732032986387107991004021392285783925812861821192530917403151452391805634, + 10857046999023057135944570762232829481370756359578518086990519993285655852781 + ], + [ + 4082367875863433681332203403145435568316851327593401208105741076214120093531, + 8495653923123431417604973247489272438418190587263600148770280649306958101930 + ] + ); + vk.delta2 = Pairing.G2Point( + [ + 11559732032986387107991004021392285783925812861821192530917403151452391805634, + 10857046999023057135944570762232829481370756359578518086990519993285655852781 + ], + [ + 4082367875863433681332203403145435568316851327593401208105741076214120093531, + 8495653923123431417604973247489272438418190587263600148770280649306958101930 + ] + ); + vk.IC = new Pairing.G1Point[](3); - vk.IC[2] = Pairing.G1Point( - 9676508711308354042906200636781077532751603523420398102911817991603787384838, - 3283726592693011886356839062982995108864203785773230930373125221081605006490 - ); + vk.IC[0] = Pairing.G1Point( + 2392822642995661005171555009488844088176954870319064822645652947726621638605, + 19622522825149758925846860792412025256115126012155814217617363920694413818355 + ); - vk.IC[3] = Pairing.G1Point( - 13173001357742665986032466031029094851321843920842980071903261453747149717823, - 20227736002115979502147501163867970082744770988953245358764176832339037911954 - ); + vk.IC[1] = Pairing.G1Point( + 18954309538486684917164156025490115873263619829298537818491608339093110373239, + 9185979323690985945295229185629666150037261362996364025399775430832808015475 + ); - vk.IC[4] = Pairing.G1Point( - 12503202302840927457518240189095194534947514058111893826856048640111724282474, - 10324849082459144926597406829440562859648647791510582416705714907864823023456 - ); + vk.IC[2] = Pairing.G1Point( + 16010199862829093037919343395088636768729563763409851148470370813861437396580, + 16457624759801426240698376800316458056002370626757831987377706347248123201023 + ); + } + function verify(uint256[] memory input, Proof memory proof) + internal + view + returns (uint256) + { + uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + VerifyingKey memory vk = verifyingKey(); + require(input.length + 1 == vk.IC.length, 'verifier-bad-input'); + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + for (uint256 i = 0; i < input.length; i++) { + require(input[i] < snark_scalar_field, 'verifier-gte-snark-scalar-field'); + vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i])); } - function verify(uint[] memory input, Proof memory proof) internal view returns (uint) { - uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - VerifyingKey memory vk = verifyingKey(); - require(input.length + 1 == vk.IC.length,"verifier-bad-input"); - // Compute the linear combination vk_x - Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); - for (uint i = 0; i < input.length; i++) { - require(input[i] < snark_scalar_field,"verifier-gte-snark-scalar-field"); - vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i])); - } - vk_x = Pairing.addition(vk_x, vk.IC[0]); - if (!Pairing.pairingProd4( - Pairing.negate(proof.A), proof.B, - vk.alfa1, vk.beta2, - vk_x, vk.gamma2, - proof.C, vk.delta2 - )) return 1; - return 0; + vk_x = Pairing.addition(vk_x, vk.IC[0]); + if ( + !Pairing.pairingProd4( + Pairing.negate(proof.A), + proof.B, + vk.alfa1, + vk.beta2, + vk_x, + vk.gamma2, + proof.C, + vk.delta2 + ) + ) return 1; + return 0; + } + + /// @return r bool true if proof is valid + function verifyProof( + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c, + uint256[2] memory input + ) public view returns (bool r) { + Proof memory proof; + proof.A = Pairing.G1Point(a[0], a[1]); + proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); + proof.C = Pairing.G1Point(c[0], c[1]); + uint256[] memory inputValues = new uint256[](input.length); + for (uint256 i = 0; i < input.length; i++) { + inputValues[i] = input[i]; } - /// @return r bool true if proof is valid - function verifyProof( - uint[2] memory a, - uint[2][2] memory b, - uint[2] memory c, - uint[4] memory input - ) public view returns (bool r) { - Proof memory proof; - proof.A = Pairing.G1Point(a[0], a[1]); - proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); - proof.C = Pairing.G1Point(c[0], c[1]); - uint[] memory inputValues = new uint[](input.length); - for(uint i = 0; i < input.length; i++){ - inputValues[i] = input[i]; - } - if (verify(inputValues, proof) == 0) { - return true; - } else { - return false; - } + if (verify(inputValues, proof) == 0) { + return true; + } else { + return false; } + } } diff --git a/beacon-light-client/solidity/hardhat.config.ts b/beacon-light-client/solidity/hardhat.config.ts index 7996b158b..e46f1d05e 100644 --- a/beacon-light-client/solidity/hardhat.config.ts +++ b/beacon-light-client/solidity/hardhat.config.ts @@ -58,10 +58,10 @@ export default { url: `https://ropsten.infura.io/v3/${mandatoryConf.INFURA_API_KEY}`, accounts: [optionalConf.USER_PRIVATE_KEY], }, - mainnet: { + goerli: { url: `https://goerli.infura.io/v3/${mandatoryConf.INFURA_API_KEY}`, accounts: [optionalConf.USER_PRIVATE_KEY], - }, + } }, mocha: { timeout: 100000000, diff --git a/beacon-light-client/solidity/tasks/deploy.ts b/beacon-light-client/solidity/tasks/deploy.ts index 08fc74aec..4aaa6eac6 100644 --- a/beacon-light-client/solidity/tasks/deploy.ts +++ b/beacon-light-client/solidity/tasks/deploy.ts @@ -11,7 +11,7 @@ task('deploy', 'Deploy the beacon light client contract').setAction( const beaconLightClient = await ( await ethers.getContractFactory('BeaconLightClient') - ).deploy(...getConstructorArgs(network.name)); + ).deploy(...(await getConstructorArgs(network.name))); console.log('>>> Waiting for BeaconLightClient deployment...'); diff --git a/beacon-light-client/solidity/tasks/utils/index.ts b/beacon-light-client/solidity/tasks/utils/index.ts index 2d2ab3041..d800f3b8b 100644 --- a/beacon-light-client/solidity/tasks/utils/index.ts +++ b/beacon-light-client/solidity/tasks/utils/index.ts @@ -1,15 +1,27 @@ -import { hashTreeRootBeaconBlock } from '../../test/utils/format'; +import { bytesToHex } from '../../../../libs/typescript/ts-utils/bls'; +import { getBlockHeaderFromUpdate } from '../../../../libs/typescript/ts-utils/ssz-utils'; +import * as UPDATE0 from '../../../circom/scripts/light_client/relayer_updates/update_237215.json'; -export const getConstructorArgs = (network: string) => { +export const getConstructorArgs = async (network: string) => { network = network === 'hardhat' ? 'mainnet' : network; - const UPDATE0 = require(`../../../../vendor/eth2-light-client-updates/${network}/updates/00290.json`); + const { ssz } = await import('@lodestar/types'); + + console.log( + 'instantiate optimistic', + bytesToHex( + ssz.phase0.BeaconBlockHeader.hashTreeRoot( + await getBlockHeaderFromUpdate(UPDATE0.data.attested_header.beacon), + ), + ), + ); return [ - parseInt(UPDATE0.attested_header.slot), - parseInt(UPDATE0.attested_header.proposer_index), - UPDATE0.attested_header.parent_root, - UPDATE0.attested_header.body_root, - UPDATE0.attested_header.state_root, - hashTreeRootBeaconBlock(UPDATE0.attested_header), + ssz.phase0.BeaconBlockHeader.hashTreeRoot( + await getBlockHeaderFromUpdate(UPDATE0.data.attested_header.beacon), + ), + ssz.phase0.BeaconBlockHeader.hashTreeRoot( + await getBlockHeaderFromUpdate(UPDATE0.data.finalized_header.beacon), + ), + UPDATE0.data.finalized_header.execution.state_root, ]; }; diff --git a/beacon-light-client/solidity/tasks/verify-contracts.ts b/beacon-light-client/solidity/tasks/verify-contracts.ts index dcd7a0bbd..e21e2e648 100644 --- a/beacon-light-client/solidity/tasks/verify-contracts.ts +++ b/beacon-light-client/solidity/tasks/verify-contracts.ts @@ -6,6 +6,6 @@ task('verify-contracts', 'Verify') .setAction(async (args, { run, network }) => { await run('verify:verify', { address: args.lightclient, - constructorArguments: getConstructorArgs(network.name), + constructorArguments: await getConstructorArgs(network.name), }); }); diff --git a/beacon-light-client/solidity/tsconfig.json b/beacon-light-client/solidity/tsconfig.json index dcd94289b..ce155dd67 100644 --- a/beacon-light-client/solidity/tsconfig.json +++ b/beacon-light-client/solidity/tsconfig.json @@ -1,5 +1,8 @@ { "extends": "../../tsconfig.json", "files": ["./hardhat.config.ts"], - "include": ["./test"] + "include": ["./test"], + "compilerOptions": { + "moduleResolution": "nodenext" + } } diff --git a/libs/typescript/ts-utils/ssz-utils.ts b/libs/typescript/ts-utils/ssz-utils.ts index 9b43ac696..4648d28df 100644 --- a/libs/typescript/ts-utils/ssz-utils.ts +++ b/libs/typescript/ts-utils/ssz-utils.ts @@ -1,13 +1,27 @@ -import { readJson } from './wasm-utils.js'; import { Type } from '@chainsafe/ssz'; +import { hexToBytes } from './bls'; +import { readFile } from 'fs/promises'; export async function jsonToSerializedBase64( sszType: Type, path: string, ) { - const jsonContent = await readJson(path); + const jsonContent = JSON.parse(await readFile(path, 'utf-8')); const data = sszType.fromJson(jsonContent); const serializedData = sszType.serialize(data); var b64Data = Buffer.from(serializedData).toString('base64'); return b64Data; } + +export async function getBlockHeaderFromUpdate(head) { + const { ssz } = await import('@lodestar/types'); + + const blockHeader = ssz.phase0.BeaconBlockHeader.defaultValue(); + blockHeader.slot = Number.parseInt(head.slot); + blockHeader.proposerIndex = Number.parseInt(head.proposer_index); + blockHeader.parentRoot = hexToBytes(head.parent_root); + blockHeader.stateRoot = hexToBytes(head.state_root); + blockHeader.bodyRoot = hexToBytes(head.body_root); + + return blockHeader; +} diff --git a/package.json b/package.json index 4a5fe5a70..ba8337774 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@cosmjs/proto-signing": "^0.29.3", "@cosmjs/stargate": "^0.29.3", "@iden3/binfileutils": "^0.0.11", - "@lodestar/types": "^0.41.0", + "@lodestar/types": "1.4.1", "@nomiclabs/hardhat-ethers": "^2.1.1", "@nomiclabs/hardhat-etherscan": "^3.1.0", "@nomiclabs/hardhat-waffle": "^2.0.3", diff --git a/yarn.lock b/yarn.lock index 09e6f20c3..08d2ebbf7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1749,27 +1749,20 @@ __metadata: languageName: node linkType: hard -"@lodestar/params@npm:^0.41.0": - version: 0.41.0 - resolution: "@lodestar/params@npm:0.41.0" - checksum: 36bc8ca074a517c67c2755c667084c65abff3618201e14eeb9b9299bdc82d5edc90d142e7ffb711d257e99f383aa7da72536812fdc6da1a9c7abfacc6dc55de3 - languageName: node - linkType: hard - -"@lodestar/params@npm:^1.4.3": +"@lodestar/params@npm:^1.4.1, @lodestar/params@npm:^1.4.3": version: 1.4.3 resolution: "@lodestar/params@npm:1.4.3" checksum: c6285dc00dd402e376739198b014c87207b5ce72cea40386ec351d048606dbbed642b1df8557e958ad98e8de1acea80ad206524b4f3e0e4a84b1a7725d248b13 languageName: node linkType: hard -"@lodestar/types@npm:^0.41.0": - version: 0.41.0 - resolution: "@lodestar/types@npm:0.41.0" +"@lodestar/types@npm:1.4.1": + version: 1.4.1 + resolution: "@lodestar/types@npm:1.4.1" dependencies: "@chainsafe/ssz": ^0.9.2 - "@lodestar/params": ^0.41.0 - checksum: 0255ba43c088e25151e86ee8760e69eb501f9b16def25c6eb5224173478aa151d9ca2ea79a1fdc70bee5ba78b77f34a251cfa987657947cba91cdb23a07c6d88 + "@lodestar/params": ^1.4.1 + checksum: 6d61de7480ea0f5023eff10e3c23e8aa96969805d0ee59bdc1487478e79500f585514528e0fb3e12ebb96be812544c1de3f150e53e41f5610b0c29ee03f17688 languageName: node linkType: hard @@ -2902,7 +2895,7 @@ __metadata: "@cosmjs/proto-signing": ^0.29.3 "@cosmjs/stargate": ^0.29.3 "@iden3/binfileutils": ^0.0.11 - "@lodestar/types": ^0.41.0 + "@lodestar/types": 1.4.1 "@noble/bls12-381": ^1.3.0 "@nomiclabs/hardhat-ethers": ^2.1.1 "@nomiclabs/hardhat-etherscan": ^3.1.0