diff --git a/src/ts/BaseProverHelper.ts b/src/ts/BaseProverHelper.ts deleted file mode 100644 index 6358305..0000000 --- a/src/ts/BaseProverHelper.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { Address, Hash, Hex, PublicClient, toHex, toRlp } from 'viem' -import fs from 'fs'; - -/** - * BaseProverHelper is a base class for prover helpers that provides common functionality - * for interacting prover contracts. - * - * It provides methods to get RLP encoded block headers and storage/account proofs. - */ -export abstract class BaseProverHelper { - constructor( - readonly homeChainClient: PublicClient, - readonly targetChainClient: PublicClient - ) {} - - /** - * Retrieves the RLP encoded block header for a given block hash. - * @param chain target or home chain - * @param blockHash Block hash to retrieve the block header for - * @returns The RLP encoded block header - * @throws Error if the block is not found - */ - protected async _getRlpBlockHeader( - chain: 'target' | 'home', - blockHash: Hash - ): Promise { - const client = - chain === 'target' ? this.targetChainClient : this.homeChainClient - const block: any = await client.transport.request({ - method: 'eth_getBlockByHash', - params: [blockHash, false], - }) - - if (!block) { - throw new Error('Block not found') - } - - return this._convertToRlpBlock(block) - } - - /** - * - * @param chain target or home chain - * @param blockHash Block hash to generate proofs against - * @param account Account to generate a proof for - * @param slot Storage slot to generate a proof for - * @returns The RLP encoded account proof, storage proof, and the slot value - * @throws Error if the block is not found - */ - protected async _getRlpStorageAndAccountProof( - chain: 'target' | 'home', - blockHash: Hash, - account: Address, - slot: bigint - ): Promise<{ rlpAccountProof: Hex; rlpStorageProof: Hex; slotValue: Hash }> { - const client = - chain === 'target' ? this.targetChainClient : this.homeChainClient - console.log("_getRlpStorageAndAccountProof"); - const block = await client.getBlock({ - blockHash, - includeTransactions: false, - }) - - //console.log("block", block); - - if (!block) { - throw new Error('Block not found') - } - - let proof: any; - - try{ - proof = await client.getProof({ - address: account, - storageKeys: [toHex(slot, { size: 32 })], - blockNumber: block.number, - }) - } catch (error) { - console.error("eth_getProof failed:", error); - throw new Error(`Failed to get proof for account ${account} at slot ${slot}: ${error}`) - } - - - // Convert slot value to proper bytes32 hex format - const rawValue = proof.storageProof[0].value - const slotValue = typeof rawValue === 'string' && rawValue.startsWith('0x') - ? (rawValue as Hash) - : toHex(BigInt(rawValue), { size: 32 }) - - const rlpAccountProof = toRlp(proof.accountProof) - const rlpStorageProof = toRlp(proof.storageProof[0].proof) - - return { - rlpAccountProof, - rlpStorageProof, - slotValue, - } - } - - /** - * Converts an RPC block response to RLP format. - * Works up to the Pectra fork. - * For reference on the block structure, see: - * https://github.com/ethereum/go-ethereum/blob/35dd84ce2999ecf5ca8ace50a4d1a6abc231c370/core/types/block.go#L75-L109 - * @param rpcBlock The block response from the RPC - * @returns The RLP encoded block - */ - protected _convertToRlpBlock(rpcBlock: any): Hex { - const encodeInt = (hex: string) => { - const value = BigInt(hex) - if (value === 0n) return '0x' - return cleanHex(value.toString(16)) as Hex - } - - const cleanHex = (hex: string) => { - const clean = hex.replace(/^0x/, '') - return `0x${clean.length % 2 === 0 ? clean : '0' + clean}` as Hex - } - - const headerFields: Hex[] = [ - cleanHex(rpcBlock.parentHash), - cleanHex(rpcBlock.sha3Uncles), - cleanHex(rpcBlock.miner), - cleanHex(rpcBlock.stateRoot), - cleanHex(rpcBlock.transactionsRoot), - cleanHex(rpcBlock.receiptsRoot), - cleanHex(rpcBlock.logsBloom), - encodeInt(rpcBlock.difficulty), - encodeInt(rpcBlock.number), - encodeInt(rpcBlock.gasLimit), - encodeInt(rpcBlock.gasUsed), - encodeInt(rpcBlock.timestamp), - cleanHex(rpcBlock.extraData), - cleanHex(rpcBlock.mixHash), - cleanHex(rpcBlock.nonce), - ] - - if (rpcBlock.baseFeePerGas) - headerFields.push(encodeInt(rpcBlock.baseFeePerGas)) - if (rpcBlock.withdrawalsRoot) - headerFields.push(cleanHex(rpcBlock.withdrawalsRoot)) - if (rpcBlock.blobGasUsed) headerFields.push(encodeInt(rpcBlock.blobGasUsed)) - if (rpcBlock.excessBlobGas) - headerFields.push(encodeInt(rpcBlock.excessBlobGas)) - if (rpcBlock.parentBeaconBlockRoot) - headerFields.push(cleanHex(rpcBlock.parentBeaconBlockRoot)) - if (rpcBlock.requestsHash) - headerFields.push(cleanHex(rpcBlock.requestsHash)) - - return toRlp(headerFields) - } -} diff --git a/src/ts/ChildToParentProverHelper.ts b/src/ts/ChildToParentProverHelper.ts deleted file mode 100644 index dbc7051..0000000 --- a/src/ts/ChildToParentProverHelper.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { - Address, - BlockTag, - encodeAbiParameters, - getContract, - GetContractReturnType, - Hash, - Hex, - hexToBigInt, - keccak256, - PublicClient, -} from 'viem' -import { IProverHelper } from './IProverHelper' -import { BaseProverHelper } from './BaseProverHelper' -import { childToParentProverAbi, iBufferAbi } from '../../wagmi/abi' - -/** - * ChildToParentProverHelper is a class that provides helper methods for interacting - * with the child to parent IStateProver contract. - * - * It extends the BaseProverHelper class and implements the IProverHelper interface. - * - * buildInputForGetTargetBlockHash and buildInputForVerifyTargetBlockHash methods - * are currently not implemented and return a hardcoded block hash. - * - * buildInputForVerifyStorageSlot is fully implemented and requires no changes - * unless the prover's verifyStorageSlot function is modified. - */ -export class ChildToParentProverHelper - extends BaseProverHelper - implements IProverHelper -{ - readonly bufferAddress: Address = '0x0000000048C4Ed10cF14A02B9E0AbDDA5227b071' - readonly blockHashMappingSlot: bigint = 51n - - /** - * @see IProverHelper.buildInputForGetTargetBlockHash - */ - async buildInputForGetTargetBlockHash(): Promise<{ - input: Hex - targetStateCommitment: Hash - }> { - const { targetStateCommitment, targetBlockNumber } = - await this._findLatestAvailableTargetChainBlock( - await this.homeChainClient.getBlockNumber() - ) - return { - input: encodeAbiParameters([{ type: 'uint256' }], [targetBlockNumber]), - targetStateCommitment, - } - } - - async buildInputForGetTargetBlockHashByBlockNumber(blockNumber: bigint): Promise<{ - input: Hex - targetStateCommitment: Hash - }> { - //// TODO - - return { - input: encodeAbiParameters([{ type: 'uint256' }], [blockNumber]), - targetStateCommitment: '0x' as `0x${string}`, - } - } - - /** - * @see IProverHelper.buildInputForGetTargetBlockHash - */ - async buildInputForVerifyTargetBlockHash( - homeBlockHash: Hash - ): Promise<{ input: Hex; targetStateCommitment: Hash }> { - const homeBlockNumber = ( - await this.homeChainClient.getBlock({ blockHash: homeBlockHash }) - ).number - const { targetStateCommitment, targetBlockNumber } = - await this._findLatestAvailableTargetChainBlock(homeBlockNumber) - - const slot = hexToBigInt( - keccak256( - encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256' }], - [targetBlockNumber, this.blockHashMappingSlot] - ) - ) - ) - - const rlpBlockHeader = await this._getRlpBlockHeader('home', homeBlockHash) - const { rlpAccountProof, rlpStorageProof } = - await this._getRlpStorageAndAccountProof( - 'home', - homeBlockHash, - this.bufferAddress, - slot - ) - - const input = encodeAbiParameters( - [ - { type: 'bytes' }, // block header - { type: 'uint256' }, // target block number - { type: 'bytes' }, // account proof - { type: 'bytes' }, // storage proof - ], - [rlpBlockHeader, targetBlockNumber, rlpAccountProof, rlpStorageProof] - ) - - return { - input, - targetStateCommitment, - } - } - - /** - * @see IProverHelper.buildInputForVerifyStorageSlot - */ - async buildInputForVerifyStorageSlot( - targetStateCommitment: Hash, - account: Address, - slot: bigint - ): Promise<{ input: Hex; slotValue: Hash }> { - const rlpBlockHeader = await this._getRlpBlockHeader( - 'target', - targetStateCommitment - ) - const { rlpAccountProof, rlpStorageProof, slotValue } = - await this._getRlpStorageAndAccountProof( - 'target', - targetStateCommitment, - account, - slot - ) - - console.log("BBB"); - - const input = encodeAbiParameters( - [ - { type: 'bytes' }, // block header - { type: 'address' }, // account - { type: 'uint256' }, // slot - { type: 'bytes' }, // account proof - { type: 'bytes' }, // storage proof - ], - [rlpBlockHeader, account, slot, rlpAccountProof, rlpStorageProof] - ) - - return { input, slotValue } - } - - async _findLatestAvailableTargetChainBlock(homeBlockNumber: bigint): Promise<{ - targetBlockNumber: bigint - targetStateCommitment: Hash - }> { - const bufferContract = this._bufferContract() - const targetBlockNumber = await bufferContract.read.newestBlockNumber({ - blockNumber: homeBlockNumber, - }) - const targetStateCommitment = await bufferContract.read.parentChainBlockHash( - [targetBlockNumber], - { blockNumber: homeBlockNumber } - ) - - return { - targetBlockNumber, - targetStateCommitment, - } - } - - _bufferContract(): GetContractReturnType { - return getContract({ - address: this.bufferAddress, - abi: iBufferAbi, - client: this.homeChainClient, - }) - } -} diff --git a/src/ts/IProverHelper.ts b/src/ts/IProverHelper.ts deleted file mode 100644 index 5806668..0000000 --- a/src/ts/IProverHelper.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Address, Hash, Hex } from 'viem' - -/** - * IProverHelper defines the interface that IStateProver helper classes must implement. - * - * Implementations should avoid relying on specialized RPC capabilities such as large log queries. - */ -export interface IProverHelper { - /** - * Builds the bytes input argument for the IStateProver::getTargetStateCommitment function. - * Finds the newest block hash that can be returned by getTargetStateCommitment on the prover. - * @returns The input bytes and the resulting target block hash. - */ - buildInputForGetTargetBlockHash(): Promise<{ - input: Hex - targetStateCommitment: Hash - }> - - /** - * Build the bytes input argument for the IStateProver::verifyTargetStateCommitment function. - * Finds the newest block hash that can be returned by verifyTargetStateCommitment on the prover given the home block hash. - * @param homeBlockHash Home chain block hash that will be passed to the prover and proven against - */ - buildInputForVerifyTargetBlockHash( - homeBlockHash: Hash - ): Promise<{ input: Hex; targetStateCommitment: Hash }> - - /** - * Build the bytes input argument for the IStateProver::verifyStorageSlot function. - * @param targetStateCommitment Target chain block hash that will be passed to the prover and proven against - * @param account The account to prove the storage slot for - * @param slot The storage slot to prove - * @returns The input bytes and the slot value - */ - buildInputForVerifyStorageSlot( - targetStateCommitment: Hash, - account: Address, - slot: bigint - ): Promise<{ input: Hex; slotValue: Hash }> - - buildInputForGetTargetBlockHashByBlockNumber(blockNumber: bigint): Promise<{ - input: Hex - targetStateCommitment: Hash - }> -} diff --git a/src/ts/ParentToChildProverHelper.ts b/src/ts/ParentToChildProverHelper.ts deleted file mode 100644 index 6a14d92..0000000 --- a/src/ts/ParentToChildProverHelper.ts +++ /dev/null @@ -1,234 +0,0 @@ -import { - Address, - encodeAbiParameters, - getContract, - GetContractEventsReturnType, - GetContractReturnType, - Hash, - Hex, - hexToBigInt, - keccak256, - PublicClient, -} from 'viem' -import { IProverHelper } from './IProverHelper' -import { BaseProverHelper } from './BaseProverHelper' -import { iOutboxAbi, parentToChildProverAbi } from '../../wagmi/abi' - -export class ParentToChildProverHelper - extends BaseProverHelper - implements IProverHelper -{ - // todo: document - public readonly defaultLogBlockRangeSize = 10_000n - public readonly defaultMaxLogLookback = 1_000_000n - - constructor( - public readonly proverAddress: Address, - homeChainClient: PublicClient, - targetChainClient: PublicClient - ) { - super(homeChainClient, targetChainClient) - } - - /** - * @see IProverHelper.buildInputForGetTargetBlockHash - */ - async buildInputForGetTargetBlockHash(): Promise<{ - input: Hex - targetStateCommitment: Hash - }> { - const { targetStateCommitment, sendRoot } = - await this._findLatestAvailableTargetChainBlock( - await this.homeChainClient.getBlockNumber() - ) - return { - input: encodeAbiParameters([{ type: 'bytes32' }], [sendRoot]), - targetStateCommitment, - } - } - - async buildInputForGetTargetBlockHashByBlockNumber(blockNumber: bigint): Promise<{ - input: Hex - targetStateCommitment: Hash - }> { - console.log("blockNumber", blockNumber); - - // const targetBlock = await this.targetChainClient.getBlock({ blockNumber }); - // console.log("targetBlock", targetBlock); - // // @ts-ignore - // console.log("sendRoot", targetBlock.sendRoot); - - const { targetStateCommitment, sendRoot } = await this._findLatestAvailableTargetChainBlock(blockNumber); - - return { - // @ts-ignore - input: encodeAbiParameters([{ type: 'bytes32' }], [sendRoot]), - targetStateCommitment: targetStateCommitment, - } - } - - /** - * @see IProverHelper.buildInputForVerifyTargetBlockHash - */ - async buildInputForVerifyTargetBlockHash( - homeBlockHash: Hash - ): Promise<{ input: Hex; targetStateCommitment: Hash }> { - const { targetStateCommitment, sendRoot } = - await this._findLatestAvailableTargetChainBlock( - (await this.homeChainClient.getBlock({ blockHash: homeBlockHash })) - .number - ) - - const slot = hexToBigInt( - keccak256( - encodeAbiParameters( - [{ type: 'bytes32' }, { type: 'uint256' }], - [sendRoot, await this._proverContract().read.rootsSlot()] - ) - ) - ) - - const rlpBlockHeader = await this._getRlpBlockHeader('home', homeBlockHash) - const { rlpAccountProof, rlpStorageProof } = - await this._getRlpStorageAndAccountProof( - 'home', - homeBlockHash, - await this._proverContract().read.outbox(), - slot - ) - - const input = encodeAbiParameters( - [ - { type: 'bytes' }, // block header - { type: 'bytes32' }, // send root - { type: 'bytes' }, // account proof - { type: 'bytes' }, // storage proof - ], - [rlpBlockHeader, sendRoot, rlpAccountProof, rlpStorageProof] - ) - - - - return { - input, - targetStateCommitment, - } - } - - /** - * @see IProverHelper.buildInputForVerifyStorageSlot - */ - async buildInputForVerifyStorageSlot( - targetStateCommitment: Hash, - account: Address, - slot: bigint - ): Promise<{ input: Hex; slotValue: Hash }> { - const rlpBlockHeader = await this._getRlpBlockHeader( - 'target', - targetStateCommitment - ) - const { rlpAccountProof, rlpStorageProof, slotValue } = - await this._getRlpStorageAndAccountProof( - 'target', - targetStateCommitment, - account, - slot - ) - - console.log("get proof BBB"); - - const input = encodeAbiParameters( - [ - { type: 'bytes' }, // block header - { type: 'address' }, // account - { type: 'uint256' }, // slot - { type: 'bytes' }, // account proof - { type: 'bytes' }, // storage proof - ], - [rlpBlockHeader, account, slot, rlpAccountProof, rlpStorageProof] - ) - - return { input, slotValue } - } - - /** - * Find the latest target chain block hash that is available as of the given home block number. - * @param homeBlockNumber home chain block number to search up to - * @param overrides Optional parameters to control the log query size and maximum lookback - * @returns The latest target chain block hash and the corresponding send root - */ - async _findLatestAvailableTargetChainBlock( - homeBlockNumber: bigint, - overrides?: { logBlockRangeSize?: bigint; maxLogLookback?: bigint } - ): Promise<{ - sendRoot: Hash - targetStateCommitment: Hash - }> { - const logBlockRangeSize = - overrides?.logBlockRangeSize ?? this.defaultLogBlockRangeSize - const maxLogLookback = - overrides?.maxLogLookback ?? this.defaultMaxLogLookback - - if (logBlockRangeSize < 1n || maxLogLookback < 1n) { - throw new Error(`logBlockRangeSize and maxLogLookback must be at least 1`) - } - - const outboxContract = await this._outboxContract() - - let fromBlock = homeBlockNumber - logBlockRangeSize + 1n - let latestEvent: - | GetContractEventsReturnType[0] - | null = null - while ( - latestEvent === null && - fromBlock > homeBlockNumber - logBlockRangeSize - ) { - const toBlock = fromBlock + logBlockRangeSize - 1n - const events = await outboxContract.getEvents.SendRootUpdated( - {}, - { - fromBlock, - toBlock, - } - ) - - if (events.length > 0) { - latestEvent = events[events.length - 1] - } - - fromBlock -= logBlockRangeSize - } - - if (!latestEvent) { - throw new Error( - 'No SendRootUpdated event found, consider increasing maxLogLookback' - ) - } - - return { - sendRoot: latestEvent.args.outputRoot!, - targetStateCommitment: latestEvent.args.l2BlockHash!, - } - } - - _proverContract(): GetContractReturnType< - typeof parentToChildProverAbi, - PublicClient - > { - return getContract({ - address: this.proverAddress, - abi: parentToChildProverAbi, - client: this.homeChainClient, - }) - } - - async _outboxContract(): Promise< - GetContractReturnType - > { - return getContract({ - address: await this._proverContract().read.outbox(), - abi: iOutboxAbi, - client: this.homeChainClient, - }) - } -} diff --git a/src/ts/index.ts b/src/ts/index.ts deleted file mode 100644 index 173f26e..0000000 --- a/src/ts/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { ChildToParentProverHelper } from './ChildToParentProverHelper' -export { ParentToChildProverHelper } from './ParentToChildProverHelper' -export { IProverHelper } from './IProverHelper' - -// Optimism helpers -export { OptimismChildToParentProverHelper } from './optimism/ChildToParentProverHelper' diff --git a/src/ts/optimism/ChildToParentProverHelper.ts b/src/ts/optimism/ChildToParentProverHelper.ts deleted file mode 100644 index 8dde1ec..0000000 --- a/src/ts/optimism/ChildToParentProverHelper.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { - Address, - encodeAbiParameters, - Hash, - Hex, -} from 'viem' -import { IProverHelper } from '../IProverHelper' -import { BaseProverHelper } from '../BaseProverHelper' - -/** - * ChildToParentProverHelper for Optimism - * - * This helper generates proofs for the Optimism ChildToParentProver contract. - * It reads L1 block hashes from the L1Block predeploy on Optimism. - * - * Optimism L1Block predeploy: 0x4200000000000000000000000000000000000015 - * L1 block hash storage slot: 2 - */ -export class OptimismChildToParentProverHelper - extends BaseProverHelper - implements IProverHelper -{ - readonly l1BlockPredeploy: Address = '0x4200000000000000000000000000000000000015' - readonly l1BlockHashSlot: bigint = 2n // hash is at slot 2 - - /** - * Build input for getTargetStateCommitment() - * For Optimism, this reads the L1Block predeploy directly, so input can be empty - */ - async buildInputForGetTargetBlockHash(): Promise<{ - input: Hex - targetStateCommitment: Hash - }> { - // Read the L1 block hash directly from the L1Block predeploy - const targetStateCommitment = await this.homeChainClient.getStorageAt({ - address: this.l1BlockPredeploy, - slot: `0x${this.l1BlockHashSlot.toString(16)}` as Hex, - }) as Hash - - // getTargetStateCommitment() on Optimism doesn't need any input - // It reads the predeploy directly - return { - input: '0x' as Hex, - targetStateCommitment, - } - } - - /** - * Build input for verifyTargetStateCommitment() - * This requires Merkle proofs of the L1Block predeploy's storage - */ - async buildInputForVerifyTargetBlockHash( - homeBlockHash: Hash - ): Promise<{ input: Hex; targetStateCommitment: Hash }> { - const homeBlockNumber = ( - await this.homeChainClient.getBlock({ blockHash: homeBlockHash }) - ).number - - // Read the L1 block hash from the predeploy at this specific block - const targetStateCommitment = await this.homeChainClient.getStorageAt({ - address: this.l1BlockPredeploy, - slot: `0x${this.l1BlockHashSlot.toString(16)}` as Hex, - blockNumber: homeBlockNumber, - }) as Hash - - // Get the RLP-encoded block header - const rlpBlockHeader = await this._getRlpBlockHeader('home', homeBlockHash) - - // Get Merkle proofs for the L1Block predeploy storage - const { rlpAccountProof, rlpStorageProof } = - await this._getRlpStorageAndAccountProof( - 'home', - homeBlockHash, - this.l1BlockPredeploy, - this.l1BlockHashSlot - ) - - // Encode: (bytes blockHeader, bytes accountProof, bytes storageProof) - const input = encodeAbiParameters( - [ - { type: 'bytes' }, // block header - { type: 'bytes' }, // account proof - { type: 'bytes' }, // storage proof - ], - [rlpBlockHeader, rlpAccountProof, rlpStorageProof] - ) - - return { - input, - targetStateCommitment, - } - } - - /** - * Build input for verifyStorageSlot() - * This verifies a storage slot on the target chain (Ethereum L1) - */ - async buildInputForVerifyStorageSlot( - targetStateCommitment: Hash, - account: Address, - slot: bigint - ): Promise<{ input: Hex; slotValue: Hash }> { - const rlpBlockHeader = await this._getRlpBlockHeader( - 'target', - targetStateCommitment - ) - const { rlpAccountProof, rlpStorageProof, slotValue } = - await this._getRlpStorageAndAccountProof( - 'target', - targetStateCommitment, - account, - slot - ) - - // Encode: (bytes blockHeader, address account, uint256 slot, bytes accountProof, bytes storageProof) - const input = encodeAbiParameters( - [ - { type: 'bytes' }, // block header - { type: 'address' }, // account - { type: 'uint256' }, // slot - { type: 'bytes' }, // account proof - { type: 'bytes' }, // storage proof - ], - [rlpBlockHeader, account, slot, rlpAccountProof, rlpStorageProof] - ) - - return { input, slotValue } - } -} -