diff --git a/beacon-light-client/circom/scripts/light_client/config.json b/beacon-light-client/circom/scripts/light_client/config.json index 1fd582c1b..8eed16735 100644 --- a/beacon-light-client/circom/scripts/light_client/config.json +++ b/beacon-light-client/circom/scripts/light_client/config.json @@ -7,7 +7,8 @@ "zkeyFilePath": "../../../build/light_client/light_client_0.zkey", "witnessGeneratorPath": "../../../build/light_client/light_client_cpp/light_client", "updatePolingTime": 450000, - "lightClientAddress": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0", - "etherJsonRpcProvider": "http://127.0.0.1:8545", - "privateKey": "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + "slotsJump": 32, + "lightClientAddress": "0x6b7f6ad5890D5a1C1262227878062574226D3FbD", + "etherJsonRpcProvider": "localhost", + "privateKey": "privKey" } 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 f820730f6..afc6b3583 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 @@ -12,47 +12,35 @@ import { bigint_to_array, bytesToHex, formatHex, - hexToBytes, } from '../../../../libs/typescript/ts-utils/bls'; import { Tree } from '@chainsafe/persistent-merkle-tree'; import { Config } from '../../../solidity/test/utils/constants'; import { computeSyncCommitteePeriodAt, - Data, + SyncAggregate, + SyncCommittee, WitnessGeneratorInput, } from './relayer-helper'; +import { ValueOfFields } from '@chainsafe/ssz/lib/view/container'; const ExecutionPayload = new ContainerType({ - parent_hash: new ByteVectorType(32), - fee_recipient: new ByteVectorType(20), - state_root: new ByteVectorType(32), - receipts_root: new ByteVectorType(32), - logs_bloom: new ByteVectorType(256), - prev_randao: new ByteVectorType(32), - block_number: new UintNumberType(8), - gas_limit: new UintNumberType(8), - gas_used: new UintNumberType(8), + parentHash: new ByteVectorType(32), + feeRecipient: new ByteVectorType(20), + stateRoot: new ByteVectorType(32), + receiptsRoot: new ByteVectorType(32), + logsBloom: new ByteVectorType(256), + prevRandao: new ByteVectorType(32), + blockNumber: new UintNumberType(8), + gasLimit: new UintNumberType(8), + gasUsed: new UintNumberType(8), timestamp: new UintNumberType(8), - extra_data: new ByteListType(32), - base_fee_per_gas: new UintBigintType(32), - block_hash: new ByteVectorType(32), - transactions_root: new ByteVectorType(32), - withdrawals_root: new ByteVectorType(32), + extraData: new ByteListType(32), + baseFeePerGas: new UintBigintType(32), + blockHash: new ByteVectorType(32), + transactionsRoot: new ByteVectorType(32), + withdrawalsRoot: new ByteVectorType(32), }); -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; -} - function getMerkleProof( container: ContainerType, path: JsonPath, @@ -72,28 +60,70 @@ function hexToBits(hex: string, numbersOfBits = 256) { .split(''); } -export async function getProofInput( - prevUpdate: Data & { sync_committee: any; sync_committee_branch: any }, - update: Data, - config: Config, -): Promise { +type BeaconBlockHeader = ValueOfFields<{ + slot: UintNumberType; + proposerIndex: UintNumberType; + parentRoot: ByteVectorType; + stateRoot: ByteVectorType; + bodyRoot: ByteVectorType; +}>; + +export async function getProofInput({ + syncCommittee, + syncCommitteeBranch, + sync_aggregate, + prevBlockHeader, + nextBlockHeader, + finalizedHeader, + finalityBranch, + finalizedHeaderExecutionBranch, + prevFinalizedHeader, + prevFinalityBranch, + executionPayload, + signature_slot, + config, +}: { + syncCommittee: SyncCommittee; + syncCommitteeBranch: string[]; + sync_aggregate: SyncAggregate; + prevBlockHeader: BeaconBlockHeader; + nextBlockHeader: BeaconBlockHeader; + finalizedHeader: BeaconBlockHeader; + finalityBranch: string[]; + finalizedHeaderExecutionBranch: string[]; + prevFinalizedHeader: BeaconBlockHeader; + prevFinalityBranch: string[]; + executionPayload: ValueOfFields<{ + withdrawalsRoot: ByteVectorType; + transactionsRoot: ByteVectorType; + blockHash: ByteVectorType; + parentHash: ByteVectorType; + feeRecipient: ByteVectorType; + stateRoot: ByteVectorType; + receiptsRoot: ByteVectorType; + logsBloom: ByteVectorType; + prevRandao: ByteVectorType; + blockNumber: UintNumberType; + gasLimit: UintNumberType; + gasUsed: UintNumberType; + timestamp: UintNumberType; + extraData: ByteListType; + baseFeePerGas: UintBigintType; + }>; + signature_slot: number; + config: Config; +}): Promise { const { ssz } = await import('@lodestar/types'); - let syncCommitteePubkeys: PointG1[] = prevUpdate.sync_committee.pubkeys.map( - x => PointG1.fromHex(x.slice(2)), + let syncCommitteePubkeys: PointG1[] = syncCommittee.pubkeys.map(x => + PointG1.fromHex(x.slice(2)), ); const SyncCommitteeBits = new BitVectorType(512); - let bitmask = SyncCommitteeBits.fromJson( - update.sync_aggregate.sync_committee_bits, - ); + let bitmask = SyncCommitteeBits.fromJson(sync_aggregate.sync_committee_bits); let signature: PointG2 = PointG2.fromSignature( - formatHex(update.sync_aggregate.sync_committee_signature), - ); - - const prevBlockHeader = await getBlockHeaderFromUpdate( - prevUpdate.attested_header.beacon, + formatHex(sync_aggregate.sync_committee_signature), ); const prevBlockHeaderStateRootProof = getMerkleProof( @@ -105,9 +135,6 @@ export async function getProofInput( const prevBlockHeaderHash = ssz.phase0.BeaconBlockHeader.hashTreeRoot(prevBlockHeader); - const nextBlockHeader = await getBlockHeaderFromUpdate( - update.attested_header.beacon, - ); const nextBlockHeaderHash = ssz.phase0.BeaconBlockHeader.hashTreeRoot(nextBlockHeader); const nextBlockHeaderStateRootProof = getMerkleProof( @@ -122,18 +149,12 @@ export async function getProofInput( nextBlockHeader, ).map(x => hexToBits(x)); - let syncCommitteeBranch = prevUpdate.sync_committee_branch.map(x => - hexToBits(x), - ); - - let finalizedHeader = await getBlockHeaderFromUpdate( - update.finalized_header.beacon, - ); + let syncCommitteeBranchBits = syncCommitteeBranch.map(x => hexToBits(x)); let finalizedHeaderHash = ssz.phase0.BeaconBlockHeader.hashTreeRoot(finalizedHeader); - let finalityBranchBits = update.finality_branch.map(x => hexToBits(x)); + let finalityBranchBits = finalityBranch.map(x => hexToBits(x)); let finalizedHeaderBodyRootProof = getMerkleProof( ssz.phase0.BeaconBlockHeader, @@ -141,41 +162,32 @@ export async function getProofInput( finalizedHeader, ); - let prevHeaderFinalizedBranch = prevUpdate.finality_branch.map(x => - hexToBits(x), - ); + let prevHeaderFinalizedBranch = prevFinalityBranch.map(x => hexToBits(x)); - 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 prevHeaderFinalizedRoot = hexToBits( + bytesToHex(ssz.phase0.BeaconBlockHeader.hashTreeRoot(prevFinalizedHeader)), + ); + let prevFinalizedHeaderStateProof = getMerkleProof( ssz.phase0.BeaconBlockHeader, ['state_root'], prevFinalizedHeader, - ); + ).map(x => hexToBits(x)); - const executionPayload = ExecutionPayload.fromJson( - update.finalized_header.execution, - ); const executionPayloadStateProof = getMerkleProof( ExecutionPayload, - ['state_root'], + ['stateRoot'], executionPayload, ); let dataView = new DataView(new ArrayBuffer(8)); - dataView.setBigUint64( - 0, - BigInt(update.finalized_header.beacon.proposer_index), - ); + dataView.setBigUint64(0, BigInt(finalizedHeader.proposerIndex)); let proposer_index = dataView.getBigUint64(0, true); proposer_index = BigInt( '0x' + proposer_index.toString(16).padStart(16, '0').padEnd(64, '0'), @@ -184,21 +196,15 @@ export async function getProofInput( return { prevHeaderHash: hexToBits(bytesToHex(prevBlockHeaderHash)), nextHeaderHash: hexToBits(bytesToHex(nextBlockHeaderHash)), - prevFinalizedHeaderRoot: hexToBits( - bytesToHex( - ssz.phase0.BeaconBlockHeader.hashTreeRoot(prevFinalizedHeader), - ), - ), + prevFinalizedHeaderRoot: prevHeaderFinalizedRoot, prevFinalizedHeaderRootBranch: [ ...prevHeaderFinalizedBranch, ...prevBlockHeaderStateRootProof, ], prevHeaderFinalizedStateRoot: hexToBits( - prevUpdate.finalized_header.beacon.state_root, - ), - prevHeaderFinalizedStateRootBranch: prevFinalizedHeaderStateProof.map(x => - hexToBits(x), + bytesToHex(prevFinalizedHeader.stateRoot), ), + prevHeaderFinalizedStateRootBranch: prevFinalizedHeaderStateProof, // prevHeaderStateRoot: hexToBits(bytesToHex(prevBlockHeader.stateRoot)), // prevHeaderStateRootBranch: prevBlockHeaderStateRootProof, @@ -208,11 +214,10 @@ export async function getProofInput( nextHeaderSlot: nextBlockHeader.slot, nextHeaderSlotBranch: nextHeaderSlotBranch, - signatureSlot: update.signature_slot, + signatureSlot: signature_slot.toString(), - signatureSlotSyncCommitteePeriod: computeSyncCommitteePeriodAt( - Number(update.signature_slot), - ), + signatureSlotSyncCommitteePeriod: + computeSyncCommitteePeriodAt(signature_slot), finalizedHeaderSlotSyncCommitteePeriod: computeSyncCommitteePeriodAt( prevFinalizedHeader.slot, ), @@ -222,12 +227,10 @@ export async function getProofInput( ...nextBlockHeaderStateRootProof, ], - execution_state_root: hexToBits( - update.finalized_header.execution.state_root, - ), + execution_state_root: hexToBits(bytesToHex(executionPayload.stateRoot)), execution_state_root_branch: [ ...executionPayloadStateProof, - ...update.finalized_header.execution_branch, + ...finalizedHeaderExecutionBranch, ...finalizedHeaderBodyRootProof, ].map(x => hexToBits(x)), @@ -244,8 +247,9 @@ export async function getProofInput( bigint_to_array(55, 7, x.toAffine()[0].value), bigint_to_array(55, 7, x.toAffine()[1].value), ]), - aggregatedKey: hexToBits(prevUpdate.sync_committee.aggregate_pubkey, 384), - syncCommitteeBranch: [...syncCommitteeBranch], + aggregatedKey: hexToBits(syncCommittee.aggregate_pubkey, 384), + syncCommitteeBranch: [...syncCommitteeBranchBits], + bitmask: bitmask.toBoolArray().map(x => (x ? '1' : '0')), signature: [ [ 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 f9432f497..c25e6aeae 100644 --- a/beacon-light-client/circom/scripts/light_client/relayer-helper.ts +++ b/beacon-light-client/circom/scripts/light_client/relayer-helper.ts @@ -29,7 +29,7 @@ export type ProofResultType = { updateSlot: number; proof: Proof; proofInput: WitnessGeneratorInput; -} +}; export type State = { lastDownloadedUpdate: number; diff --git a/beacon-light-client/circom/scripts/light_client/relayer.ts b/beacon-light-client/circom/scripts/light_client/relayer.ts index 599834417..396bbc046 100644 --- a/beacon-light-client/circom/scripts/light_client/relayer.ts +++ b/beacon-light-client/circom/scripts/light_client/relayer.ts @@ -14,7 +14,7 @@ const updateQueue = new Queue(UPDATE_POLING_QUEUE, { }); updateQueue.add('downloadUpdate', undefined, { - repeat: { every: config.updatePolingTime, immediately: true }, + repeat: { every: config.slotsJump * 12000, immediately: true }, }); const proofGeneratorEvents = new QueueEvents(PROOF_GENERATOR_QUEUE, { 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 e42a194a5..149e1132e 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 @@ -7,9 +7,7 @@ import { ProofInputType, PROOF_GENERATOR_QUEUE, RELAYER_INPUTS_FOLDER, - RELAYER_UPDATES_FOLDER, State, - Update, UPDATE_POLING_QUEUE, } from '../relayer-helper'; import { Tree } from '@chainsafe/persistent-merkle-tree'; @@ -28,48 +26,287 @@ const proofGenertorQueue = new Queue(PROOF_GENERATOR_QUEUE, { new Worker( UPDATE_POLING_QUEUE, async () => { + const { ssz } = await import('@lodestar/types'); + const state = await getState(); - const update = await getUpdate({ ...state }); + const currentHeadResult = await ( + await fetch( + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v1/beacon/headers/head`, + ) + ).json(); + + const currentHeadSlot = Number(currentHeadResult.data.header.message.slot); + + if (currentHeadSlot < state.lastDownloadedUpdate + config.slotsJump) { + console.log('No new enought slot'); + return; + } + + // get prevHeader + const prevBlockHeaderResult = await ( + await fetch( + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v1/beacon/headers/${state.lastDownloadedUpdate}`, + ) + ).json(); + + console.log('prevHeaderResult:', prevBlockHeaderResult); + + const prevBlockHeader = ssz.phase0.BeaconBlockHeader.fromJson( + prevBlockHeaderResult.data.header.message, + ); + + let nextBlockSlot = state.lastDownloadedUpdate + config.slotsJump; + + let nextBlockHeaderResult; + while (true) { + nextBlockHeaderResult = await ( + await fetch( + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v1/beacon/headers/${nextBlockSlot}`, + ) + ).json(); + + if (nextBlockHeaderResult.code !== 404) { + break; + } + + nextBlockSlot++; + } + + console.log('nextBlockHeaderResult:', nextBlockHeaderResult); + + const nextBlockHeader = ssz.phase0.BeaconBlockHeader.fromJson( + nextBlockHeaderResult.data.header.message, + ); + + const prevBeaconStateSZZ = await fetch( + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v2/debug/beacon/states/${state.lastDownloadedUpdate}`, + { + headers: { + Accept: 'application/octet-stream', + }, + }, + ) + .then(response => response.arrayBuffer()) + .then(buffer => new Uint8Array(buffer)); + + const prevBeaconSate = + ssz.capella.BeaconState.deserialize(prevBeaconStateSZZ); + const prevBeaconStateView = + ssz.capella.BeaconState.toViewDU(prevBeaconSate); + const prevStateTree = new Tree(prevBeaconStateView.node); + + const prevFinalizedHeaderResult = await ( + await fetch( + `http://${config.beaconRestApiHost}:${ + config.beaconRestApiPort + }/eth/v1/beacon/headers/${ + '0x' + bytesToHex(prevBeaconSate.finalizedCheckpoint.root) + }`, + ) + ).json(); + + console.log('prevUpdateFinalizedHeaderResult', prevFinalizedHeaderResult); + + const prevFinalizedHeader = ssz.phase0.BeaconBlockHeader.fromJson( + prevFinalizedHeaderResult.data.header.message, + ); + + const prevUpdateFinalizedSyncCommmitteePeriod = + computeSyncCommitteePeriodAt(prevFinalizedHeader.slot); + const currentSyncCommitteePeriod = computeSyncCommitteePeriodAt( + nextBlockHeader.slot, + ); + + const prevFinalizedHeaderBeaconStateSZZ = await fetch( + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v2/debug/beacon/states/${prevFinalizedHeader.slot}`, + { + headers: { + Accept: 'application/octet-stream', + }, + }, + ) + .then(response => response.arrayBuffer()) + .then(buffer => new Uint8Array(buffer)); - if (!update) return; + const prevFinalizedBeaconState = ssz.capella.BeaconState.deserialize( + prevFinalizedHeaderBeaconStateSZZ, + ); + const prevFinalizedBeaconStateView = ssz.capella.BeaconState.toViewDU( + prevFinalizedBeaconState, + ); + const prevFinalizedBeaconStateTree = new Tree( + prevFinalizedBeaconStateView.node, + ); + + const syncCommitteeBranch = prevFinalizedBeaconStateTree + .getSingleProof( + ssz.capella.BeaconState.getPathInfo([ + prevUpdateFinalizedSyncCommmitteePeriod === currentSyncCommitteePeriod + ? 'current_sync_committee' + : 'next_sync_committee', + ]).gindex, + ) + .map(x => '0x' + bytesToHex(x)); - const prevUpdate: Update = JSON.parse( - await readFile( - path.join( - __dirname, - '..', - RELAYER_UPDATES_FOLDER, - `update_${state.lastDownloadedUpdate}.json`, + const syncCommittee = { + pubkeys: prevFinalizedBeaconState[ + prevUpdateFinalizedSyncCommmitteePeriod === currentSyncCommitteePeriod + ? 'currentSyncCommittee' + : 'nextSyncCommittee' + ].pubkeys.map(x => '0x' + bytesToHex(x)), + aggregate_pubkey: + '0x' + + bytesToHex( + prevFinalizedBeaconState[ + prevUpdateFinalizedSyncCommmitteePeriod === + currentSyncCommitteePeriod + ? 'currentSyncCommittee' + : 'nextSyncCommittee' + ].aggregatePubkey, ), - 'utf8', - ), + }; + + const prevFinalityBranch = prevStateTree + .getSingleProof( + ssz.capella.BeaconState.getPathInfo(['finalized_checkpoint', 'root']) + .gindex, + ) + .map(x => '0x' + bytesToHex(x)); + + const nextBeaconStateSZZ = await fetch( + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v2/debug/beacon/states/${nextBlockSlot}`, + { + headers: { + Accept: 'application/octet-stream', + }, + }, + ) + .then(response => response.arrayBuffer()) + .then(buffer => new Uint8Array(buffer)); + + const nextBeaconSate = + ssz.capella.BeaconState.deserialize(nextBeaconStateSZZ); + const nextBeaconStateView = + ssz.capella.BeaconState.toViewDU(nextBeaconSate); + const nextStateTree = new Tree(nextBeaconStateView.node); + + const nextFinalizedHeaderResult = await ( + await fetch( + `http://${config.beaconRestApiHost}:${ + config.beaconRestApiPort + }/eth/v1/beacon/headers/${ + '0x' + bytesToHex(nextBeaconSate.finalizedCheckpoint.root) + }`, + ) + ).json(); + + console.log('nextFinalizedHeaderResult', nextFinalizedHeaderResult); + + const finalizedHeader = ssz.phase0.BeaconBlockHeader.fromJson( + nextFinalizedHeaderResult.data.header.message, ); - await extendPrevUpdateWithSyncCommittee( - prevUpdate, - Number(update.data.signature_slot), + const finalityBranch = nextStateTree + .getSingleProof( + ssz.capella.BeaconState.getPathInfo(['finalized_checkpoint', 'root']) + .gindex, + ) + .map(x => '0x' + bytesToHex(x)); + + let signature_slot = nextBlockSlot + 1; + let blockHeaderBodyResult; + + while (true) { + blockHeaderBodyResult = await ( + await fetch( + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v2/beacon/blocks/${signature_slot}`, + ) + ).json(); + + if (blockHeaderBodyResult.code !== 404) { + break; + } + + signature_slot++; + } + + console.log('blockHeaderBodyResult', blockHeaderBodyResult); + + const sync_aggregate = + blockHeaderBodyResult.data.message.body.sync_aggregate; + + const finalizedBlockBodyResult = await ( + await fetch( + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v2/beacon/blocks/${finalizedHeader.slot}`, + ) + ).json(); + + const finalizedBlockBody = ssz.capella.BeaconBlockBody.fromJson( + finalizedBlockBodyResult.data.message.body, ); - const proofInput = await getProofInput( - prevUpdate.data as any, - update.data, - ZHEAJIANG_TESNET, + const finalizedBlockBodyView = + ssz.capella.BeaconBlockBody.toViewDU(finalizedBlockBody); + const finalizedBlockBodyTree = new Tree(finalizedBlockBodyView.node); + + const finalizedHeaderExecutionBranch = finalizedBlockBodyTree + .getSingleProof( + ssz.capella.BeaconBlockBody.getPathInfo(['execution_payload']).gindex, + ) + .map(x => '0x' + bytesToHex(x)); + + const finalizedBeaconStateSSZ = await fetch( + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v2/debug/beacon/states/${finalizedHeader.slot}`, + { + headers: { + Accept: 'application/octet-stream', + }, + }, + ) + .then(response => response.arrayBuffer()) + .then(buffer => new Uint8Array(buffer)); + + const finalizedBeaconState = ssz.capella.BeaconState.deserialize( + finalizedBeaconStateSSZ, + ); + + state.lastDownloadedUpdate = nextBlockSlot; + + await writeFile( + path.join(__dirname, '..', 'state.json'), + JSON.stringify(state), ); + const proofInput = await getProofInput({ + prevBlockHeader, + nextBlockHeader, + prevFinalizedHeader, + syncCommitteeBranch, + syncCommittee, + config: ZHEAJIANG_TESNET, + prevFinalityBranch, + signature_slot: signature_slot, + finalizedHeader, + finalityBranch, + executionPayload: finalizedBeaconState.latestExecutionPayloadHeader, + finalizedHeaderExecutionBranch, + sync_aggregate, + }); + await writeFile( path.join( __dirname, '..', RELAYER_INPUTS_FOLDER, - `input_${prevUpdate.data.attested_header.beacon.slot}_${update.data.attested_header.beacon.slot}.json`, + `input_${prevBlockHeader.slot}_${nextBlockHeader.slot}.json`, ), JSON.stringify(proofInput), ); proofGenertorQueue.add('proofGenerate', { - prevUpdateSlot: Number(prevUpdate.data.attested_header.beacon.slot), - updateSlot: Number(update.data.attested_header.beacon.slot), + prevUpdateSlot: Number(prevBlockHeader.slot), + updateSlot: Number(nextBlockHeader.slot), proofInput: proofInput, }); }, @@ -81,96 +318,6 @@ new Worker( }, ); -async function extendPrevUpdateWithSyncCommittee( - prevUpdate: Update, - signature_slot: number, -) { - const beaconStateSZZ = await fetch( - `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v2/debug/beacon/states/${prevUpdate.data.finalized_header.beacon.slot}`, - { - headers: { - Accept: 'application/octet-stream', - }, - }, - ) - .then(response => response.arrayBuffer()) - .then(buffer => new Uint8Array(buffer)); - - const { ssz } = await import('@lodestar/types'); - const beaconState = ssz.capella.BeaconState.deserialize(beaconStateSZZ); - const beaconStateView = ssz.capella.BeaconState.toViewDU(beaconState); - const tree = new Tree(beaconStateView.node); - - const prevUpdateFinalizedSyncCommmitteePeriod = computeSyncCommitteePeriodAt( - Number(prevUpdate.data.finalized_header.beacon.slot), - ); - const currentSyncCommitteePeriod = - computeSyncCommitteePeriodAt(signature_slot); - - const sync_committee_branch = tree - .getSingleProof( - ssz.capella.BeaconState.getPathInfo([ - prevUpdateFinalizedSyncCommmitteePeriod === currentSyncCommitteePeriod - ? 'current_sync_committee' - : 'next_sync_committee', - ]).gindex, - ) - .map(x => '0x' + bytesToHex(x)); - - prevUpdate.data['sync_committee_branch'] = sync_committee_branch; - prevUpdate.data['sync_committee'] = { - pubkeys: beaconState[ - prevUpdateFinalizedSyncCommmitteePeriod === currentSyncCommitteePeriod - ? 'currentSyncCommittee' - : 'nextSyncCommittee' - ].pubkeys.map(x => '0x' + bytesToHex(x)), - aggregate_pubkey: - '0x' + - bytesToHex( - beaconState[ - prevUpdateFinalizedSyncCommmitteePeriod === currentSyncCommitteePeriod - ? 'currentSyncCommittee' - : 'nextSyncCommittee' - ].aggregatePubkey, - ), - }; -} - -async function getUpdate(state: State): Promise { - const update = await ( - await fetch( - `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v1/beacon/light_client/finality_update`, - ) - ) - .json() - .catch(e => console.log(e)); - - const updateSlot = Number(update.data.attested_header.beacon.slot); - - if (state.lastDownloadedUpdate >= updateSlot) { - console.log('There is no new header to update'); - return undefined; - } - - state.lastDownloadedUpdate = updateSlot; - await writeFile( - path.join(__dirname, '..', 'state.json'), - JSON.stringify(state), - ); - - await writeFile( - path.join( - __dirname, - '..', - RELAYER_UPDATES_FOLDER, - `update_${updateSlot}.json`, - ), - JSON.stringify(update), - ); - - return update; -} - async function getState(): Promise { return JSON.parse( await readFile(path.join(__dirname, '..', 'state.json'), 'utf-8'), 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 index 63033f615..bb0a97529 100644 --- 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 @@ -1,4 +1,4 @@ -import { Worker, Queue } from 'bullmq'; +import { Worker } from 'bullmq'; import { ethers } from 'ethers'; import { readFileSync } from 'fs'; import { readFile, writeFile } from 'fs/promises'; @@ -30,10 +30,26 @@ 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 header_root_on_chain = + await lightClientContract.optimistic_header_root(); + + console.log(header_root_on_chain); + + const onChainHeaderResult = await ( + await fetch( + `http://${config.beaconRestApiHost}:${config.beaconRestApiPort}/eth/v1/beacon/headers/${header_root_on_chain}`, + ) + ).json(); + + const lastSlotOnChain = Number( + onChainHeaderResult.data.header.message.slot, + ); + + if (lastSlotOnChain > job.data.prevUpdateSlot) { + return; + } + + if (lastSlotOnChain === job.data.prevUpdateSlot) { const calldata = await groth16.exportSolidityCallData( job.data.proof, job.data.proof.public, @@ -51,42 +67,45 @@ new Worker( ]; 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 }, - ); + 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, + }); + + console.log(transaction); await transaction.wait(); + const state: State = JSON.parse( + await readFile('../state.json', 'utf-8'), + ); 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)); + await new Promise(r => setTimeout(r, 15000)); } } }, { + concurrency: 100, connection: { host: config.redisHost, port: config.redisPort, diff --git a/beacon-light-client/solidity/hardhat.config.ts b/beacon-light-client/solidity/hardhat.config.ts index e46f1d05e..f1fe69a05 100644 --- a/beacon-light-client/solidity/hardhat.config.ts +++ b/beacon-light-client/solidity/hardhat.config.ts @@ -61,7 +61,7 @@ export default { goerli: { url: `https://goerli.infura.io/v3/${mandatoryConf.INFURA_API_KEY}`, accounts: [optionalConf.USER_PRIVATE_KEY], - } + }, }, mocha: { timeout: 100000000,