UNPKG

@unruggable/gateways

Version:

Trustless Ethereum Multichain CCIP-Read Gateway

118 lines (117 loc) 5.25 kB
import { AbstractRollup } from '../rollup.mjs'; import { keccak256 } from 'ethers/crypto'; import { Contract } from 'ethers/contract'; import { LineaProver } from './LineaProver.mjs'; import { ROLLUP_ABI } from './types.mjs'; import { ABI_CODER, fetchBlock, MAINNET_BLOCK_SEC } from '../utils.mjs'; export class UnfinalizedLineaRollup extends AbstractRollup { minAgeBlocks; L1MessageService; constructor(providers, config, minAgeBlocks) { super(providers); this.minAgeBlocks = minAgeBlocks; this.L1MessageService = new Contract(config.L1MessageService, ROLLUP_ABI, this.provider1); } // WARNING: this doesn't work because the stateRoots are sparse merkle // and the block stateRoots are merkle-patricia // requirements for operation: // 1. shomei node with unfinalized proof generation // 2. shomei stateRoot to l2Block indexer // this is likely too inefficient and requires external stateRoot => blockHash indexer // async findL2BlockBefore(beforeTimestamp: number) { // const step = 1000; // let block = await fetchBlock(this.provider2); // while (block && parseInt(block.timestamp) > beforeTimestamp) { // console.log(parseInt(block.number), block.hash); // block = await fetchBlock(this.provider2, parseInt(block.number) - step); // } // if (!block) throw new Error(`expected block before: ${beforeTimestamp}`); // return parseInt(block.number) + step - 1; // } // async findL2BlockWithStateRoot( // beforeTimestamp: number, // stateRoot: HexString32 // ) { // const step = 100; // for ( // let i = await this.findL2BlockBefore(beforeTimestamp); // i >= 0; // i -= step // ) { // const start = Math.max(0, i - step); // const blocks = await Promise.all( // Array.from({ length: i - start }, (_, i) => // fetchBlock(this.provider2, start + i) // ) // ); // console.log(start, i); // const block = blocks.find((x) => x.stateRoot === stateRoot); // if (block) return BigInt(block.number); // } // throw new Error(`unable to find block with stateRoot: ${stateRoot}`); // } get unfinalized() { return true; } async fetchLatestCommitIndex() { const l1BlockInfo = await fetchBlock(this.provider1, this.latestBlockTag); const l1BlockNumber = parseInt(l1BlockInfo.number) - this.minAgeBlocks; const step = this.getLogsStepSize; for (let i = l1BlockNumber; i >= 0; i -= step) { const logs = await this.L1MessageService.queryFilter(this.L1MessageService.filters.DataSubmittedV3(), i < step ? 0 : i + 1 - step, i); if (logs.length) return BigInt(logs[logs.length - 1].blockNumber); } throw new Error(`no earlier shnarf: ${l1BlockNumber}`); } async _fetchParentCommitIndex(commit) { const [event] = await this.L1MessageService.queryFilter(this.L1MessageService.filters.DataSubmittedV3(null, commit.parentShnarf)); return event ? BigInt(event.blockNumber) : -1n; } async _fetchCommit(index) { const [event] = await this.L1MessageService.queryFilter(this.L1MessageService.filters.DataSubmittedV3(), index, index); if (!event) throw new Error(`no DataSubmittedV3`); const tx = await event.getTransaction(); if (!tx || !tx.blockNumber || !tx.blobVersionedHashes) { throw new Error(`no submit tx: ${event.transactionHash}`); } const desc = this.L1MessageService.interface.parseTransaction(tx); if (!desc) throw new Error(`unable to parse tx`); const blobs = desc.args.blobSubmissionData; if (!blobs.length) throw new Error('expected blobs'); const parentShnarf = desc.args.parentShnarf; let computedShnarf = parentShnarf; let abiEncodedTuple; for (let i = 0; i < blobs.length; i++) { const blob = blobs[i]; const currentDataEvaluationPoint = keccak256(ABI_CODER.encode(['bytes32', 'bytes32'], [blob.snarkHash, tx.blobVersionedHashes[i]])); abiEncodedTuple = ABI_CODER.encode(['bytes32', 'bytes32', 'bytes32', 'bytes32', 'uint256'], [ computedShnarf, blob.snarkHash, blob.finalStateRootHash, currentDataEvaluationPoint, blob.dataEvaluationClaim, ]); computedShnarf = keccak256(abiEncodedTuple); } if (computedShnarf !== desc.args.finalBlobShnarf) { throw new Error('shnarf mismatch'); } throw new Error(`block number from shnarf not implemented`); return { index, prover: new LineaProver(this.provider2, 0), abiEncodedTuple, parentShnarf, }; } encodeWitness(commit, proofSeq) { return ABI_CODER.encode(['(uint256, bytes, bytes[], bytes)'], [[commit.index, commit.abiEncodedTuple, proofSeq.proofs, proofSeq.order]]); } windowFromSec(sec) { return Math.ceil(sec / MAINNET_BLOCK_SEC); // units of L1Block } }