diff --git a/package.json b/package.json index 7687efb7..e5a3f5c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@unruggable/gateways", - "version": "0.1.4", + "version": "0.1.5", "description": "Trustless Ethereum Multichain CCIP-Read Gateway", "publishConfig": { "access": "public" diff --git a/scripts/serve.ts b/scripts/serve.ts index 0c646e3b..f82cc701 100755 --- a/scripts/serve.ts +++ b/scripts/serve.ts @@ -20,11 +20,10 @@ import { EthSelfRollup } from '../src/eth/EthSelfRollup.js'; import { Contract } from 'ethers/contract'; import { SigningKey } from 'ethers/crypto'; import { toUnpaddedHex } from '../src/utils.js'; -import { TrustedRollup } from '../src/eth/TrustedRollup.js'; +import { TrustedRollup } from '../src/TrustedRollup.js'; import { EthProver } from '../src/eth/EthProver.js'; //import { LineaProver } from '../src/linea/LineaProver.js'; import { ZKSyncProver } from '../src/zksync/ZKSyncProver.js'; -import { AbstractProver, type LatestProverFactory } from '../src/vm.js'; // NOTE: you can use CCIPRewriter to test an existing setup against a local gateway! // [raffy] https://adraffy.github.io/ens-normalize.js/test/resolver.html#raffy.linea.eth.nb2hi4dthixs62dpnvss4ylooruxg5dvobuwiltdn5ws62duoryc6.ccipr.eth @@ -180,13 +179,22 @@ async function createGateway(name: string) { const slug = match[1].toUpperCase().replaceAll('-', '_'); if (slug in CHAINS) { const chain = CHAINS[slug as keyof typeof CHAINS]; - return new Gateway( - new TrustedRollup( - createProvider(chain), - getProverFactory(chain), - new SigningKey(signingKey) - ) - ); + const provider = createProvider(chain); + const key = new SigningKey(signingKey); + switch (chain) { + case CHAINS.ZKSYNC: + case CHAINS.ZKSYNC_SEPOLIA: + return new Gateway(new TrustedRollup(provider, ZKSyncProver, key)); + // NOTE: linea should use eth_getProof instead of linea_getProof + // NOTE: this probably needs "--latest" cli option too + // rollup => SMT w/Mimc root using linea_getProof + // chain => PMT w/Keccak root using eth_getProof + // case CHAINS.LINEA: + // case CHAINS.LINEA_SEPOLIA: + // return LineaProver; + default: + return new Gateway(new TrustedRollup(provider, EthProver, key)); + } } } switch (name) { @@ -287,7 +295,8 @@ async function createGateway(name: string) { return new Gateway(new ZKSyncRollup(createProviderPair(config), config)); } case 'base': - return createOPGateway(OPRollup.baseMainnetConfig); + // return createOPGateway(OPRollup.baseMainnetConfig); + return createOPFaultGateway(OPFaultRollup.baseMainnetConfig); case 'base-sepolia': return createOPFaultGateway(OPFaultRollup.baseSepoliaConfig); case 'unfinalized-base-sepolia': @@ -337,23 +346,6 @@ async function createGateway(name: string) { } } -function getProverFactory(chain: Chain): LatestProverFactory { - switch (chain) { - case CHAINS.ZKSYNC: - case CHAINS.ZKSYNC_SEPOLIA: - return ZKSyncProver; - // NOTE: linea should use eth_getProof instead of linea_getProof - // NOTE: this probably needs "--latest" cli option too - // rollup => SMT w/Mimc root using linea_getProof - // chain => PMT w/Keccak root using eth_getProof - // case CHAINS.LINEA: - // case CHAINS.LINEA_SEPOLIA: - // return LineaProver; - default: - return EthProver; - } -} - function createSelfGateway(chain: Chain) { return new Gateway(new EthSelfRollup(createProvider(chain))); } diff --git a/src/eth/TrustedRollup.ts b/src/TrustedRollup.ts similarity index 83% rename from src/eth/TrustedRollup.ts rename to src/TrustedRollup.ts index c18d8aac..2b1009cd 100755 --- a/src/eth/TrustedRollup.ts +++ b/src/TrustedRollup.ts @@ -3,12 +3,12 @@ import type { HexString32, ProofSequence, Provider, -} from '../types.js'; -import { AbstractProver, type LatestProverFactory } from '../vm.js'; -import { AbstractRollup, type RollupCommit } from '../rollup.js'; -import { ABI_CODER } from '../utils.js'; -import { CachedValue } from '../cached.js'; -import { VOID_PROVIDER } from '../VoidProvider.js'; +} from './types.js'; +import type { AbstractProver, LatestProverFactory } from './vm.js'; +import { AbstractRollup, type RollupCommit } from './rollup.js'; +import { ABI_CODER } from './utils.js'; +import { CachedValue } from './cached.js'; +import { VOID_PROVIDER } from './VoidProvider.js'; import { ZeroAddress } from 'ethers/constants'; import { SigningKey } from 'ethers/crypto'; import { computeAddress } from 'ethers/transaction'; @@ -30,7 +30,7 @@ export class TrustedRollup

extends AbstractRollup< readonly signingKey: SigningKey ) { super({ provider1: VOID_PROVIDER, provider2 }); - this.latest = new CachedValue>(async () => { + this.latest = new CachedValue(async () => { const prover = await factory.latest(this.provider2, this.latestBlockTag); const stateRoot = await prover.fetchStateRoot(); const signedAt = Math.ceil(Date.now() / 1000); @@ -57,8 +57,9 @@ export class TrustedRollup

extends AbstractRollup< return -1n; } protected override async _fetchCommit( - _index: bigint + index: bigint ): Promise> { + if (index) throw new Error('unsupported commit'); return this.latest.get(); } override encodeWitness( diff --git a/src/eth/EthProver.ts b/src/eth/EthProver.ts index e48d7511..7cc150be 100755 --- a/src/eth/EthProver.ts +++ b/src/eth/EthProver.ts @@ -13,7 +13,8 @@ import { withResolvers, toPaddedHex } from '../utils.js'; export class EthProver extends BlockProver { static readonly encodeProof = encodeProof; static readonly isContract = isContract; - override async isContract(target: HexAddress) { + static readonly latest = this._createLatest(); + override async isContract(target: HexAddress): Promise { target = target.toLowerCase(); if (this.fast) { return this.cache.get(target, async () => { diff --git a/src/eth/EthSelfRollup.ts b/src/eth/EthSelfRollup.ts index ad6c109e..0debf129 100755 --- a/src/eth/EthSelfRollup.ts +++ b/src/eth/EthSelfRollup.ts @@ -1,6 +1,6 @@ import type { HexString, ProofSequence, Provider } from '../types.js'; import { AbstractRollup, type RollupCommit } from '../rollup.js'; -import { ABI_CODER, fetchBlock, MAINNET_BLOCK_SEC } from '../utils.js'; +import { fetchBlockNumber, ABI_CODER, MAINNET_BLOCK_SEC } from '../utils.js'; import { EthProver } from './EthProver.js'; import { encodeRlpBlock } from '../rlp.js'; @@ -23,8 +23,9 @@ export class EthSelfRollup extends AbstractRollup { return index - (index % this.commitStep); } override async fetchLatestCommitIndex(): Promise { - const blockInfo = await fetchBlock(this.provider1, this.latestBlockTag); - return this.align(BigInt(blockInfo.number)); + return this.align( + await fetchBlockNumber(this.provider1, this.latestBlockTag) + ); } protected override async _fetchParentCommitIndex( commit: EthSelfCommit diff --git a/src/index.ts b/src/index.ts index f8b8e485..4fe0fe7e 100755 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ export * from './utils.js'; export * from './rlp.js'; export * from './chains.js'; export * from './VoidProvider.js'; +export * from './GatewayProvider.js'; //export * from './ops.js'; use GatewayRequest.Opcodes instead export * from './reader.js'; @@ -34,7 +35,7 @@ export * from './taiko/TaikoRollup.js'; export * from './scroll/ScrollRollup.js'; export * from './zksync/ZKSyncRollup.js'; export * from './eth/EthSelfRollup.js'; -export * from './eth/TrustedRollup.js'; +export * from './TrustedRollup.js'; export * from './gateway.js'; export * from './linea/LineaGatewayV1.js'; diff --git a/src/linea/LineaProver.ts b/src/linea/LineaProver.ts index b2afca69..9bb02f34 100755 --- a/src/linea/LineaProver.ts +++ b/src/linea/LineaProver.ts @@ -14,11 +14,22 @@ export class LineaProver extends BlockProver { static readonly isInclusionProof = isInclusionProof; static readonly isContract = isContract; static readonly encodeProof = encodeProof; + static readonly latest = this._createLatest(); stateRoot?: HexString32; override async fetchStateRoot() { if (!this.stateRoot) throw new Error(`unknown stateRoot`); return this.stateRoot; } + override async isContract(target: HexString): Promise { + if (this.fast) { + return this.cache.get(target, async () => { + const code = await this.provider.getCode(target, this.block); + return code.length > 2; + }); + } + const { accountProof } = await this.getProofs(target); + return isContract(accountProof); + } override async getStorage( target: HexString, slot: bigint, @@ -52,16 +63,6 @@ export class LineaProver extends BlockProver { ? proof.storageProofs[0].proof.value : ZeroHash; } - override async isContract(target: HexString) { - if (this.fast) { - return this.cache.get(target, async () => { - const code = await this.provider.getCode(target, this.block); - return code.length > 2; - }); - } - const { accountProof } = await this.getProofs(target); - return isContract(accountProof); - } protected override async _proveNeed( need: TargetNeed, accountRef: ProofRef, diff --git a/src/op/OPFaultRollup.ts b/src/op/OPFaultRollup.ts index f5071000..64d14a12 100755 --- a/src/op/OPFaultRollup.ts +++ b/src/op/OPFaultRollup.ts @@ -37,7 +37,6 @@ export class OPFaultRollup extends AbstractOPRollup { OptimismPortal: '0xbEb5Fc579115071764c7423A4f12eDde41f106Ed', GameFinder: GAME_FINDER_MAINNET, }; - static readonly sepoliaConfig: RollupDeployment = { chain1: CHAINS.SEPOLIA, chain2: CHAINS.OP_SEPOLIA, @@ -45,6 +44,13 @@ export class OPFaultRollup extends AbstractOPRollup { GameFinder: GAME_FINDER_SEPOLIA, }; + // https://docs.base.org/docs/base-contracts#l1-contract-addresses + static readonly baseMainnetConfig: RollupDeployment = { + chain1: CHAINS.MAINNET, + chain2: CHAINS.BASE, + OptimismPortal: '0x49048044D57e1C92A77f79988d21Fa8fAF74E97e', + GameFinder: GAME_FINDER_MAINNET, + }; // https://docs.base.org/docs/base-contracts/#ethereum-testnet-sepolia static readonly baseSepoliaConfig: RollupDeployment = { chain1: CHAINS.SEPOLIA, diff --git a/src/op/OPRollup.ts b/src/op/OPRollup.ts index a2f9717f..edf5fcef 100755 --- a/src/op/OPRollup.ts +++ b/src/op/OPRollup.ts @@ -10,12 +10,12 @@ export type OPConfig = { }; export class OPRollup extends AbstractOPRollup { - // https://docs.base.org/docs/base-contracts#base-mainnet - static readonly baseMainnetConfig: RollupDeployment = { - chain1: CHAINS.MAINNET, - chain2: CHAINS.BASE, - L2OutputOracle: '0x56315b90c40730925ec5485cf004d835058518A0', - }; + // 20241030: base changed to fault proofs + // static readonly baseMainnetConfig: RollupDeployment = { + // chain1: CHAINS.MAINNET, + // chain2: CHAINS.BASE, + // L2OutputOracle: '0x56315b90c40730925ec5485cf004d835058518A0', + // }; // https://docs.blast.io/building/contracts#mainnet static readonly blastMainnnetConfig: RollupDeployment = { diff --git a/src/polygon/ZKEVMProver.ts b/src/polygon/ZKEVMProver.ts index 559dc057..b02321f4 100755 --- a/src/polygon/ZKEVMProver.ts +++ b/src/polygon/ZKEVMProver.ts @@ -13,6 +13,7 @@ import { export class ZKEVMProver extends BlockProver { static readonly isContract = isContract; static readonly encodeProof = encodeProof; + static readonly latest = this._createLatest(); override async isContract(target: HexAddress): Promise { target = target.toLowerCase(); if (this.fast) { diff --git a/src/vm.ts b/src/vm.ts index 7444292f..b292d6ad 100755 --- a/src/vm.ts +++ b/src/vm.ts @@ -485,10 +485,6 @@ export function makeStorageKey(target: HexAddress, slot: bigint) { return `${target}${slot.toString(16)}`; } -export interface LatestProverFactory

{ - latest(provider: Provider, relative: BigNumberish): Promise

; -} - // TODO: totalAssembledBytes export abstract class AbstractProver { // general proof cache @@ -966,14 +962,17 @@ export abstract class AbstractProver { } } +export interface LatestProverFactory

{ + latest(provider: Provider, relative?: BigNumberish): Promise

; +} + export abstract class BlockProver extends AbstractProver { - // absolutely disgusting typescript - static async latest>( - this: new (...a: ConstructorParameters) => T, - provider: Provider, - relBlockTag: BigNumberish = 0 + protected static _createLatest

( + this: new (...a: ConstructorParameters) => P ) { - return new this(provider, await fetchBlockNumber(provider, relBlockTag)); + return async (provider: Provider, relative: BigNumberish = 0) => { + return new this(provider, await fetchBlockNumber(provider, relative)); + }; } readonly block: HexString; constructor(provider: Provider, block: BigNumberish) { diff --git a/src/zksync/ZKSyncProver.ts b/src/zksync/ZKSyncProver.ts index 521fe3fc..0d1c727a 100755 --- a/src/zksync/ZKSyncProver.ts +++ b/src/zksync/ZKSyncProver.ts @@ -43,7 +43,8 @@ export class ZKSyncProver extends AbstractProver { return batchIndex + Number(relative); //(typeof relative === 'string' ? 0 : Number(relative)); } static async latest(provider: Provider, relative: BigNumberish = 0) { - return new this(provider, await this.latestBatchIndex(provider, relative)); + const batchIndex = await this.latestBatchIndex(provider, relative); + return new this(provider, batchIndex); } constructor( provider: Provider, diff --git a/test/debug/play.ts b/test/debug/play.ts index 21578558..440a45bb 100755 --- a/test/debug/play.ts +++ b/test/debug/play.ts @@ -1,10 +1,21 @@ -import { GatewayRequestV1, GatewayRequest, EthProver, CHAINS, NitroRollup, OPRollup, TaikoRollup, ZKSyncRollup, fetchBlockNumber } from '../../src/index.js'; +import { randomBytes, SigningKey, ZeroHash } from 'ethers'; +import { GatewayRequestV1, GatewayRequest, EthProver, CHAINS, NitroRollup, OPRollup, TaikoRollup, ZKSyncRollup, fetchBlockNumber, TrustedRollup, ZKEVMProver, ZKSyncProver, LineaProver, OPFaultRollup } from '../../src/index.js'; import { createProvider, createProviderPair } from '../providers.js'; // this is just a worksheet //console.log(createProvider(1n)._getConnection()) +if (1) { + const provider = createProvider(CHAINS.MAINNET); + const key = new SigningKey(randomBytes(32)); + const rollup1 = new TrustedRollup(provider, EthProver, key); + const rollup2 = new TrustedRollup(provider, ZKSyncProver, key); + const rollup3 = new TrustedRollup(provider, LineaProver, key); + const rollup4 = new TrustedRollup(provider, ZKEVMProver, key); + throw 1; +} + if (1) { const provider = createProvider(CHAINS.OP_SEPOLIA); console.log(await fetchBlockNumber(provider, 'finalized')); @@ -31,8 +42,8 @@ if (0) { if (0) { - const config = OPRollup.baseMainnetConfig; - const rollup = new OPRollup(createProviderPair(config), config); + const config = OPFaultRollup.baseMainnetConfig; + const rollup = new OPFaultRollup(createProviderPair(config), config); //const commit = await rollup.fetchLatestCommit(); const commit = await rollup.fetchCommit(0n); console.log(commit); diff --git a/test/gateway/base.test.ts b/test/gateway/base.test.ts index 9b62280e..fdfe6c8f 100755 --- a/test/gateway/base.test.ts +++ b/test/gateway/base.test.ts @@ -1,7 +1,10 @@ -import { OPRollup } from '../../src/op/OPRollup.js'; -import { testOP } from './common.js'; +import { OPFaultRollup } from '../../src/op/OPFaultRollup.js'; +import { testOPFault } from './common.js'; -testOP(OPRollup.baseMainnetConfig, { +// 20241030: base changed to fault proofs +// https://base.mirror.xyz/eOsedW4tm8MU5OhdGK107A9wsn-aU7MAb8f3edgX5Tk +// https://twitter.com/base/status/1851672364439814529 +testOPFault(OPFaultRollup.baseMainnetConfig, { // https://basescan.org/address/0x0C49361E151BC79899A9DD31B8B0CCdE4F6fd2f6 slotDataContract: '0x0C49361E151BC79899A9DD31B8B0CCdE4F6fd2f6', }); diff --git a/test/gateway/common.ts b/test/gateway/common.ts index 8b5a7cda..19914834 100755 --- a/test/gateway/common.ts +++ b/test/gateway/common.ts @@ -20,7 +20,7 @@ import { ScrollRollup, } from '../../src/scroll/ScrollRollup.js'; import { EthSelfRollup } from '../../src/eth/EthSelfRollup.js'; -import { TrustedRollup } from '../../src/eth/TrustedRollup.js'; +import { TrustedRollup } from '../../src/TrustedRollup.js'; import { EthProver } from '../../src/eth/EthProver.js'; import { randomBytes, SigningKey } from 'ethers/crypto'; import { afterAll } from 'bun:test'; diff --git a/test/gateway/delayed-base.test.ts b/test/gateway/delayed-zora.test.ts similarity index 54% rename from test/gateway/delayed-base.test.ts rename to test/gateway/delayed-zora.test.ts index 3a2cce5d..d5a790a4 100755 --- a/test/gateway/delayed-base.test.ts +++ b/test/gateway/delayed-zora.test.ts @@ -1,9 +1,9 @@ import { OPRollup } from '../../src/op/OPRollup.js'; import { testOP } from './common.js'; -testOP(OPRollup.baseMainnetConfig, { - // https://basescan.org/address/0x0C49361E151BC79899A9DD31B8B0CCdE4F6fd2f6 - slotDataContract: '0x0C49361E151BC79899A9DD31B8B0CCdE4F6fd2f6', +testOP(OPRollup.zoraMainnetConfig, { + // https://explorer.zora.energy/address/0x73404681064a8e16c22C1411A02D47e6395f6582 + slotDataContract: '0x73404681064a8e16c22C1411A02D47e6395f6582', // delay by 1 hour // NOTE: to delay longer, Gateway.commitDepth needs to be bigger minAgeSec: 3600, diff --git a/test/rollup/base.ts b/test/rollup/base.ts index 214b4806..badf4f8b 100755 --- a/test/rollup/base.ts +++ b/test/rollup/base.ts @@ -1,11 +1,13 @@ -import { OPRollup } from '../../src/op/OPRollup.js'; +import { OPFaultRollup } from '../../src/op/OPFaultRollup.js'; import { createProviderPair } from '../providers.js'; -const config = OPRollup.baseMainnetConfig; -const rollup = new OPRollup(createProviderPair(config), config); +const config = OPFaultRollup.baseMainnetConfig; +const rollup = new OPFaultRollup(createProviderPair(config), config); console.log({ - L2OutputOracle: rollup.L2OutputOracle.target, + OptimismPortal: rollup.OptimismPortal.target, + GameFinder: rollup.GameFinder.target, + respectedGameType: await rollup.fetchRespectedGameType(), defaultWindow: rollup.defaultWindow, }); @@ -15,5 +17,10 @@ const v = commits.map((x) => Number(x.index)); console.log(v); console.log(v.slice(1).map((x, i) => v[i] - x)); +// (OLD: OPRollup) // [ 10444, 10443, 10442, 10441, 10440, 10439, 10438, 10437, 10436, 10435 ] // [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + +// (NEW: OPFaultRollup) +// [ 6, 2, 0 ] +// [ 4, 2 ] diff --git a/test/rollup/trusted.ts b/test/rollup/trusted.ts index ea01955a..86244c9b 100755 --- a/test/rollup/trusted.ts +++ b/test/rollup/trusted.ts @@ -1,6 +1,6 @@ import { randomBytes, SigningKey } from 'ethers/crypto'; import { CHAINS } from '../../src/chains.js'; -import { TrustedRollup } from '../../src/eth/TrustedRollup.js'; +import { TrustedRollup } from '../../src/TrustedRollup.js'; import { EthProver } from '../../src/index.js'; import { createProvider } from '../providers.js'; diff --git a/test/rollup/zora.ts b/test/rollup/zora.ts new file mode 100755 index 00000000..c2b50bec --- /dev/null +++ b/test/rollup/zora.ts @@ -0,0 +1,19 @@ +import { OPRollup } from '../../src/op/OPRollup.js'; +import { createProviderPair } from '../providers.js'; + +const config = OPRollup.zoraMainnetConfig; +const rollup = new OPRollup(createProviderPair(config), config); + +console.log({ + L2OutputOracle: rollup.L2OutputOracle.target, + defaultWindow: rollup.defaultWindow, +}); + +const commits = await rollup.fetchRecentCommits(10); + +const v = commits.map((x) => Number(x.index)); +console.log(v); +console.log(v.slice(1).map((x, i) => v[i] - x)); + +// [ 12367, 12366, 12365, 12364, 12363, 12362, 12361, 12360, 12359, 12358 ] +// [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ]