Skip to content

Commit

Permalink
feat(ccip-gateway): Improve Reliability of the CCIP Gateway
Browse files Browse the repository at this point in the history
  • Loading branch information
alainncls committed Jan 7, 2025
1 parent 491d7c3 commit dfdc9d1
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 41 deletions.
4 changes: 3 additions & 1 deletion packages/linea-ccip-gateway/.env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
PORT=8081

L1_PROVIDER_URL=http://127.0.0.1:8545
L1_PROVIDER_URL=https://ethereum-sepolia-rpc.publicnode.com
L1_PROVIDER_URL_FALLBACK=https://sepolia.gateway.tenderly.co
L2_PROVIDER_URL=https://rpc.sepolia.linea.build/
L2_PROVIDER_URL_FALLBACK=https://linea-sepolia-rpc.publicnode.com
L1_ROLLUP_ADDRESS=0xB218f8A4Bc926cF1cA7b3423c154a0D627Bdb7E5
L2_CHAIN_ID=59141
9 changes: 4 additions & 5 deletions packages/linea-ccip-gateway/src/L2ProofService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
AddressLike,
Contract,
ethers,
JsonRpcProvider,
FallbackProvider,
} from 'ethers';
import { EVMProofHelper, IProofService, StateProof } from './evm-gateway';
import { logDebug, logError } from './utils';
Expand All @@ -23,12 +23,11 @@ export class L2ProofService implements IProofService<L2ProvableBlock> {
private readonly helper: EVMProofHelper;

constructor(
providerL1: JsonRpcProvider,
providerL2: JsonRpcProvider,
providerL1: FallbackProvider,
providerL2: FallbackProvider,
rollupAddress: string,
shomeiNode?: JsonRpcProvider,
) {
this.helper = new EVMProofHelper(providerL2, shomeiNode);
this.helper = new EVMProofHelper(providerL2);
const currentL2BlockNumberIface = new ethers.Interface([
currentL2BlockNumberSig,
]);
Expand Down
58 changes: 37 additions & 21 deletions packages/linea-ccip-gateway/src/evm-gateway/EVMProofHelper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { AddressLike, ethers, JsonRpcProvider, toBeHex } from 'ethers';
import {
AddressLike,
FallbackProvider,
JsonRpcProvider,
toBeHex,
} from 'ethers';
import { logDebug, logInfo } from '../utils';

interface ProofStruct {
Expand Down Expand Up @@ -26,13 +31,10 @@ export interface StateProof {
*
*/
export class EVMProofHelper {
private readonly providerL2: JsonRpcProvider;
private readonly shomeiNode: JsonRpcProvider;
private readonly providerL2: FallbackProvider;

constructor(providerL2: JsonRpcProvider, shomeiNode?: JsonRpcProvider) {
constructor(providerL2: FallbackProvider) {
this.providerL2 = providerL2;
// shomeiNode optional since an rpc infura nodes can support both eth_getStorageAt and linea_getProof
this.shomeiNode = shomeiNode ? shomeiNode : providerL2;
}

/**
Expand Down Expand Up @@ -63,29 +65,43 @@ export class EVMProofHelper {
address: AddressLike,
slots: bigint[],
): Promise<StateProof> {
const args = [
const args: (AddressLike | string[])[] = [
address,
slots.map(slot => toBeHex(slot, 32)),
'0x' + blockNo.toString(16),
];

logInfo('Calling linea_getProof with args', args);

// We have to reinitilize the provider L2 because of an issue when multiple
// We have to reinitialize the provider L2 because of an issue when multiple
// requests are sent at the same time, the provider becomes not aware of
// the linea_getProof method
const providerUrl = this.shomeiNode._getConnection().url;
const providerChainId = this.shomeiNode._network.chainId;
const providerL2 = new ethers.JsonRpcProvider(
providerUrl,
providerChainId,
{
staticNetwork: true,
},
);
logDebug('Calling linea_getProof with L2 provider', providerUrl);
const proofs: StateProof = await providerL2.send('linea_getProof', args);
logDebug('Proof result', proofs);
return proofs;

const providerConfigs = this.providerL2.providerConfigs;

for (const config of providerConfigs) {
const provider = config.provider;

if (provider instanceof JsonRpcProvider) {
try {
logDebug(
`Trying provider with URL: ${provider._getConnection().url}`,
);
const proofs: StateProof = await provider.send(
'linea_getProof',
args,
);

logDebug('Proof result from provider:', proofs);
return proofs;
} catch (error) {
logInfo(`Provider failed: ${provider._getConnection().url}`, error);
}
} else {
logInfo('Skipped non-JsonRpcProvider provider in fallback');
}
}

throw new Error('All providers failed to fetch linea_getProof');
}
}
9 changes: 4 additions & 5 deletions packages/linea-ccip-gateway/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { EVMGateway } from './evm-gateway';
import { JsonRpcProvider } from 'ethers';
import { FallbackProvider } from 'ethers';
import { L2ProofService, L2ProvableBlock } from './L2ProofService';

export type L1Gateway = EVMGateway<L2ProvableBlock>;

export function makeL2Gateway(
providerL1: JsonRpcProvider,
providerL2: JsonRpcProvider,
providerL1: FallbackProvider,
providerL2: FallbackProvider,
rollupAddress: string,
shomeiNode?: JsonRpcProvider,
): L1Gateway {
return new EVMGateway(
new L2ProofService(providerL1, providerL2, rollupAddress, shomeiNode),
new L2ProofService(providerL1, providerL2, rollupAddress),
);
}

Expand Down
19 changes: 14 additions & 5 deletions packages/linea-ccip-gateway/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { Request, Response } from 'express';
import { EVMGateway } from './evm-gateway';
import { ethers } from 'ethers';
import { FallbackProvider, JsonRpcProvider } from 'ethers';
import { L2ProofService } from './L2ProofService';
import 'dotenv/config';
import { Server } from '@chainlink/ccip-read-server';
import { logError } from './utils';

const l1ProviderUrl = process.env.L1_PROVIDER_URL;
const l1ProviderUrlFallback = process.env.L1_PROVIDER_URL_FALLBACK;
const l2ProviderUrl = process.env.L2_PROVIDER_URL;
const l2ProviderUrlFallback = process.env.L2_PROVIDER_URL_FALLBACK;
const l2ChainId = parseInt(process.env.L2_CHAIN_ID ?? '59141');
const rollupAddress =
process.env.L1_ROLLUP_ADDRESS ?? '0xB218f8A4Bc926cF1cA7b3423c154a0D627Bdb7E5';
const port = process.env.PORT || 3000;
const nodeEnv = process.env.NODE_ENV || 'test';

try {
const providerL1 = new ethers.JsonRpcProvider(l1ProviderUrl);
const providerL2 = new ethers.JsonRpcProvider(l2ProviderUrl, l2ChainId, {
staticNetwork: true,
});
const providerL1 = new FallbackProvider([
new JsonRpcProvider(l1ProviderUrl),
new JsonRpcProvider(l1ProviderUrlFallback),
]);
const providerL2 = new FallbackProvider(
[
new JsonRpcProvider(l2ProviderUrl),
new JsonRpcProvider(l2ProviderUrlFallback),
],
l2ChainId,
);

const gateway = new EVMGateway(
new L2ProofService(providerL1, providerL2, rollupAddress),
Expand Down
4 changes: 2 additions & 2 deletions packages/linea-ccip-gateway/test/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const L1_RPC_URL = 'https://gateway.tenderly.co/public/sepolia';
export const L2_RPC_URL = 'https://rpc.sepolia.linea.build/';
export const L1_RPC_URL = 'https://ethereum-sepolia-rpc.publicnode.com';
export const L2_RPC_URL = 'https://rpc.sepolia.linea.build';
export const L2_TEST_CONTRACT_ADDRESS =
'0x09a434561f4b40067F71444d7042fd110013F879';
export const ROLLUP_SEPOLIA_ADDRESS =
Expand Down
5 changes: 3 additions & 2 deletions packages/linea-ccip-gateway/test/testVerifier.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
BrowserProvider,
Contract,
ethers,
FallbackProvider,
FetchRequest,
JsonRpcProvider,
Signer,
Expand Down Expand Up @@ -75,8 +76,8 @@ describe('L1Verifier', () => {
const rollup = await Rollup.deploy(currentL2BlockNumber, stateRootHash);

const gateway = makeL2Gateway(
(l1Provider as unknown) as JsonRpcProvider,
l2Provider,
new FallbackProvider([(l1Provider as unknown) as JsonRpcProvider]),
new FallbackProvider([l2Provider]),
await rollup.getAddress(),
);
const server = new Server();
Expand Down

0 comments on commit dfdc9d1

Please sign in to comment.