UNPKG

@unruggable/gateways

Version:

Trustless Ethereum Multichain CCIP-Read Gateway

204 lines (203 loc) 8.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PolygonPoSRollup = void 0; const rollup_js_1 = require("../rollup.cjs"); const types_js_1 = require("./types.cjs"); const constants_1 = require("ethers/constants"); const contract_1 = require("ethers/contract"); const hash_1 = require("ethers/hash"); const chains_js_1 = require("../chains.cjs"); const EthProver_js_1 = require("../eth/EthProver.cjs"); const utils_js_1 = require("../utils.cjs"); const rlp_js_1 = require("../rlp.cjs"); class PolygonPoSRollup extends rollup_js_1.AbstractRollup { // https://docs.polygon.technology/pos/reference/contracts/genesis-contracts/ static mainnetConfig = { chain1: chains_js_1.CHAINS.MAINNET, chain2: chains_js_1.CHAINS.POLYGON_POS, RootChain: '0x86E4Dc95c7FBdBf52e33D563BbDB00823894C287', apiURL: 'https://proof-generator.polygon.technology/api/v1/matic/', poster: { // https://polygonscan.com/tx/0x092f9929973fee6a4fa101e9ed45c2b6ce072ac6e2f338f49cac70b41cacbc73 address: '0x591663413423Dcf7c7806930E642951E0dDdf10B', blockNumberStart: 61150865n, topicHash: (0, hash_1.id)('NewRoot(bytes32)'), }, }; apiURL; RootChain; poster; constructor(providers, config) { super(providers); this.apiURL = config.apiURL; this.poster = config.poster; this.RootChain = new contract_1.Contract(config.RootChain, types_js_1.ROOT_CHAIN_ABI, this.provider1); } async findPosterEventBefore(l2BlockNumber) { // find the most recent post from poster // stop searching when earlier than poster deployment // (otherwise we scan back to genesis) const step = BigInt(this.getLogsStepSize); for (let i = l2BlockNumber; i > this.poster.blockNumberStart; i -= step) { const logs = await this.provider2.getLogs({ address: this.poster.address, topics: [this.poster.topicHash], fromBlock: i < step ? 0n : i - step, toBlock: i - 1n, }); if (logs.length) return logs[logs.length - 1]; } throw new Error(`no earlier root: ${l2BlockNumber}`); } async findPosterHeaderBefore(l2BlockNumber) { // find the most recent post that occurred before this block const event = await this.findPosterEventBefore(l2BlockNumber); // find the header that contained this transaction // 20240830: we want the header for the transaction // not the header containing the logged block hash return this.fetchAPIFindHeader(BigInt(event.blockNumber)); } async fetchJSON(url) { const res = await fetch(url); if (!res.ok) throw new Error(`${res.url}: HTTP(${res.status})`); return res.json(); } async fetchAPIFindHeader(l2BlockNumber) { const url = new URL(`./block-included/${l2BlockNumber}`, this.apiURL); const json = await this.fetchJSON(url); if (json.error) throw new Error(`Block(${l2BlockNumber}): ${json.message}`); const number = BigInt(json.headerBlockNumber); const l2BlockNumberStart = BigInt(json.start); const l2BlockNumberEnd = BigInt(json.end); const rootHash = json.root; return { number, l2BlockNumberStart, l2BlockNumberEnd, rootHash, }; } // async fetchAPIHeaderProof( // l2BlockNumber: bigint, // l2BlockNumberStart: bigint, // l2BlockNumberEnd: bigint // ) { // const url = new URL(`./fast-merkle-proof`, this.apiURL); // url.searchParams.set('start', l2BlockNumberStart.toString()); // url.searchParams.set('end', l2BlockNumberEnd.toString()); // url.searchParams.set('number', l2BlockNumber.toString()); // const json = await this.fetchJSON(url); // const v = ethers.getBytes(json.proof); // if (!v.length || v.length & 31) throw new Error('expected bytes32xN'); // return Array.from({ length: v.length >> 5 }, (_, i) => // v.subarray(i << 5, (i + 1) << 5) // ); // } async fetchAPIReceiptProof(txHash) { const url = new URL(`./exit-payload/${txHash}?eventSignature=${this.poster.topicHash}`, this.apiURL); const json = await this.fetchJSON(url); if (json.error) throw new Error(`receipt proof: ${json.message}`); return json.result; } async fetchLatestCommitIndex() { // find the end range of the last header const l2BlockNumberEnd = await this.RootChain.getLastChildBlock({ blockTag: this.latestBlockTag, }); // find the header before the end of the last header with a post const header = await this.findPosterHeaderBefore(l2BlockNumberEnd + 1n); return header.number; } async _fetchParentCommitIndex(commit) { const header = await this.findPosterHeaderBefore(commit.l2BlockNumberStart); return header.number; } async _fetchCommit(index) { // ensure checkpoint was finalized const { rootHash, l2BlockNumberStart, l2BlockNumberEnd } = await this.RootChain.headerBlocks(index); if (rootHash === constants_1.ZeroHash) { throw new Error(`null checkpoint hash`); } // ensure checkpoint contains post const events = await this.provider2.getLogs({ address: this.poster.address, topics: [this.poster.topicHash], fromBlock: l2BlockNumberStart, toBlock: l2BlockNumberEnd, }); if (!events.length) throw new Error('no poster'); const event = events[events.length - 1]; const prevBlockHash = event.topics[1]; // rlpEncodedProof: // 1. checkpoint index // 2. fast-merkle-proof => block in checkpoint // 3. receipt merkle patricia proof => tx in block // 4. receipt data: topic[1] w/prevBlockHash + logIndex // rlpEncodedBlock: // 5. hash() = prevBlockHash // 6. usable stateRoot! const [rlpEncodedProof, prevBlock] = await Promise.all([ this.fetchAPIReceiptProof(event.transactionHash), (0, utils_js_1.fetchBlockFromHash)(this.provider2, prevBlockHash), ]); const rlpEncodedBlock = (0, rlp_js_1.encodeRlpBlock)(prevBlock); // if (ethers.keccak256(rlpEncodedBlock) !== prevBlockHash) { // throw new Error('block hash mismatch`); // } const prover = new EthProver_js_1.EthProver(this.provider2, prevBlock.number); return { index, prover, rootHash, l2BlockNumberStart, l2BlockNumberEnd, rlpEncodedProof, rlpEncodedBlock, }; } encodeWitness(commit, proofSeq) { return utils_js_1.ABI_CODER.encode(['(bytes, bytes, bytes[], bytes)'], [ [ commit.rlpEncodedProof, commit.rlpEncodedBlock, proofSeq.proofs, proofSeq.order, ], ]); } windowFromSec(sec) { // finalization time is on-chain return sec; } // experimental idea: commit serialization JSONFromCommit(commit) { return { index: (0, utils_js_1.toUnpaddedHex)(commit.index), l2BlockNumber: commit.prover.block, l2BlockNumberStart: (0, utils_js_1.toUnpaddedHex)(commit.l2BlockNumberStart), l2BlockNumberEnd: (0, utils_js_1.toUnpaddedHex)(commit.l2BlockNumberEnd), rlpEncodedBlock: commit.rlpEncodedBlock, rlpEncodedProof: commit.rlpEncodedProof, rootHash: commit.rootHash, }; } commitFromJSON(json) { const commit = { index: BigInt(json.index), prover: new EthProver_js_1.EthProver(this.provider2, json.l2BlockNumber), l2BlockNumberStart: BigInt(json.l2BlockNumberStart), l2BlockNumberEnd: BigInt(json.l2BlockNumberEnd), rlpEncodedProof: json.rlpEncodedProof, rlpEncodedBlock: json.rlpEncodedBlock, rootHash: json.rootHash, }; this.configure?.(commit); return commit; } } exports.PolygonPoSRollup = PolygonPoSRollup;