Skip to content
77 changes: 34 additions & 43 deletions packages/prover/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {NetworkName} from "@lodestar/config/networks";
import {Logger, LogLevel} from "@lodestar/utils";
import {ProofProvider} from "./proof_provider/proof_provider.js";
import {JsonRpcRequest, JsonRpcRequestOrBatch, JsonRpcResponse, JsonRpcResponseOrBatch} from "./types.js";
import {ELRpc} from "./utils/rpc.js";
import {ELRpcProvider} from "./utils/rpc_provider.js";

export type {NetworkName} from "@lodestar/config/networks";
export enum LCTransport {
Expand All @@ -30,50 +30,14 @@ export type ELRequestHandler<Params = unknown[], Response = unknown> = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ELRequestHandlerAny = ELRequestHandler<any, any>;

// Modern providers uses this structure e.g. Web3 4.x
export interface EIP1193Provider {
request: (payload: JsonRpcRequestOrBatch) => Promise<JsonRpcResponseOrBatch>;
}

export interface Web3jsProvider {
request: (payload: JsonRpcRequest) => Promise<JsonRpcResponse>;
}

// Some providers uses `request` instead of the `send`. e.g. Ganache
export interface RequestProvider {
request(
payload: JsonRpcRequestOrBatch,
callback: (err: Error | undefined, response: JsonRpcResponseOrBatch) => void
): void;
}

// The legacy Web3 1.x use this structure
export interface SendProvider {
send(payload: JsonRpcRequest, callback: (err?: Error | null, response?: JsonRpcResponse) => void): void;
}

// Ethers provider uses this structure
export interface EthersProvider {
// Ethers provider does not have a public interface for batch requests
send(method: string, params: Array<unknown>): Promise<JsonRpcResponse>;
}

// Some legacy providers use this very old structure
export interface SendAsyncProvider {
sendAsync(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch>;
}

export type Web3Provider =
| SendProvider
| EthersProvider
| SendAsyncProvider
| RequestProvider
| EIP1193Provider
| Web3jsProvider;
/**
* @deprecated Kept for backward compatibility. Use `AnyWeb3Provider` type instead.
*/
export type Web3Provider = object;

export type ELVerifiedRequestHandlerOpts<Params = unknown[]> = {
payload: JsonRpcRequest<Params>;
rpc: ELRpc;
rpc: ELRpcProvider;
proofProvider: ProofProvider;
logger: Logger;
};
Expand All @@ -96,4 +60,31 @@ export type RootProviderOptions = {
unverifiedWhitelist?: string[];
};

export type VerifiedExecutionInitOptions = LogOptions & ConsensusNodeOptions & NetworkOrConfig & RootProviderOptions;
export type ProviderTypeOptions<T extends boolean | undefined> = {
/**
* If user specify custom provider types we will register those at the start in given order.
* So if you provider [custom1, custom2] and we already have [web3js, ethers] then final order
* of providers will be [custom1, custom2, web3js, ethers]
*/
providerTypes?: Web3ProviderType<AnyWeb3Provider>[];
/**
* To keep the backward compatible behavior if this option is not set we consider `true` as default.
* In coming breaking release we may set this option default to `false`.
*/
mutateProvider?: T;
};

export type VerifiedExecutionInitOptions<T extends boolean | undefined> = LogOptions &
ConsensusNodeOptions &
NetworkOrConfig &
RootProviderOptions &
ProviderTypeOptions<T>;

export type AnyWeb3Provider = object;

export interface Web3ProviderType<T extends AnyWeb3Provider> {
name: string;
matched: (provider: AnyWeb3Provider) => provider is T;
handler(provider: T): ELRpcProvider["handler"];
mutateProvider(provider: T, newHandler: ELRpcProvider["handler"]): void;
}
32 changes: 32 additions & 0 deletions packages/prover/src/provider_types/eip1193_provider_type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {Web3ProviderType} from "../interfaces.js";
import {JsonRpcRequestOrBatch, JsonRpcResponseOrBatch} from "../types.js";

// Modern providers uses this structure e.g. Web3 4.x
export interface EIP1193Provider {
request: (payload: JsonRpcRequestOrBatch) => Promise<JsonRpcResponseOrBatch>;
}
export default {
name: "eip1193",
matched(provider): provider is EIP1193Provider {
return (
"request" in provider &&
typeof provider.request === "function" &&
provider.request.constructor.name === "AsyncFunction"
);
},
handler(provider) {
const request = provider.request.bind(provider);

return async (payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> => {
const response = await request(payload);
return response;
};
},
mutateProvider(provider, newHandler) {
Object.assign(provider, {
request: async function newRequest(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
return newHandler(payload);
},
});
},
} as Web3ProviderType<EIP1193Provider>;
44 changes: 44 additions & 0 deletions packages/prover/src/provider_types/ethers_provider_type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {Web3ProviderType} from "../interfaces.js";
import {JsonRpcRequestOrBatch, JsonRpcResponse, JsonRpcResponseOrBatch} from "../types.js";
import {isBatchRequest} from "../utils/json_rpc.js";
import web3JsProviderType from "./web3_js_provider_type.js";

export interface EthersProvider {
// Ethers provider does not have a public interface for batch requests
send(method: string, params: Array<unknown>): Promise<JsonRpcResponse>;
}
export default {
name: "ethers",
matched(provider): provider is EthersProvider {
return (
!web3JsProviderType.matched(provider) &&
"send" in provider &&
typeof provider.send === "function" &&
provider.send.length > 1 &&
provider.send.constructor.name === "AsyncFunction"
);
},
handler(provider) {
const send = provider.send.bind(provider);

return async (payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> => {
// Because ethers provider public interface does not support batch requests
// so we need to handle it manually
if (isBatchRequest(payload)) {
const responses = [];
for (const request of payload) {
responses.push(await send(request.method, request.params));
}
return responses;
}
return send(payload.method, payload.params);
};
},
mutateProvider(provider, newHandler) {
Object.assign(provider, {
send: function newSend(method: string, params: Array<unknown>): Promise<JsonRpcResponseOrBatch | undefined> {
return newHandler({jsonrpc: "2.0", id: 0, method, params});
},
});
},
} as Web3ProviderType<EthersProvider>;
123 changes: 123 additions & 0 deletions packages/prover/src/provider_types/legacy_provider_type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {AnyWeb3Provider, Web3ProviderType} from "../interfaces.js";
import {JsonRpcRequest, JsonRpcRequestOrBatch, JsonRpcResponse, JsonRpcResponseOrBatch} from "../types.js";
import web3JsProviderType from "./web3_js_provider_type.js";

// Some providers uses `request` instead of the `send`. e.g. Ganache
interface RequestProvider {
request(
payload: JsonRpcRequestOrBatch,
callback: (err: Error | undefined, response: JsonRpcResponseOrBatch) => void
): void;
}
// The legacy Web3 1.x use this structure
interface SendProvider {
send(payload: JsonRpcRequest, callback: (err?: Error | null, response?: JsonRpcResponse) => void): void;
}
// Some legacy providers use this very old structure
interface SendAsyncProvider {
sendAsync(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch>;
}

type LegacyProvider = RequestProvider | SendProvider | SendAsyncProvider;

export default {
name: "legacy",
matched(provider): provider is LegacyProvider {
return isRequestProvider(provider) || isSendProvider(provider) || isSendAsyncProvider(provider);
},
handler(provider) {
if (isRequestProvider(provider)) {
const request = provider.request.bind(provider);
return function newHandler(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
return new Promise((resolve, reject) => {
request(payload, (err, response) => {
if (err) {
reject(err);
} else {
resolve(response);
}
});
});
};
}
if (isSendProvider(provider)) {
const send = provider.send.bind(provider);
return function newHandler(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
return new Promise((resolve, reject) => {
// web3 providers supports batch requests but don't have valid types
send(payload as JsonRpcRequest, (err, response) => {
if (err) {
reject(err);
} else {
resolve(response);
}
});
});
};
}

// sendAsync provider
const sendAsync = provider.sendAsync.bind(provider);
return async function newHandler(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
const response = await sendAsync(payload);
return response;
};
},
mutateProvider(provider, newHandler) {
if (isRequestProvider(provider)) {
const newRequest = function newRequest(
payload: JsonRpcRequestOrBatch,
callback: (err?: Error | null, response?: JsonRpcResponseOrBatch) => void
): void {
newHandler(payload)
.then((response) => callback(undefined, response))
.catch((err) => callback(err, undefined));
};

Object.assign(provider, {request: newRequest});
}

if (isSendProvider(provider)) {
const newSend = function newSend(
payload: JsonRpcRequestOrBatch,
callback: (err?: Error | null, response?: JsonRpcResponseOrBatch) => void
): void {
newHandler(payload)
.then((response) => callback(undefined, response))
.catch((err) => callback(err, undefined));
};

Object.assign(provider, {send: newSend});
}

// sendAsync provider
Object.assign(provider, {sendAsync: newHandler});
},
} as Web3ProviderType<LegacyProvider>;

function isSendProvider(provider: AnyWeb3Provider): provider is SendProvider {
return (
!web3JsProviderType.matched(provider) &&
"send" in provider &&
typeof provider.send === "function" &&
provider.send.length > 1 &&
provider.send.constructor.name !== "AsyncFunction"
);
}

function isRequestProvider(provider: AnyWeb3Provider): provider is RequestProvider {
return (
!web3JsProviderType.matched(provider) &&
"request" in provider &&
typeof provider.request === "function" &&
provider.request.length > 1
);
}

function isSendAsyncProvider(provider: AnyWeb3Provider): provider is SendAsyncProvider {
return (
"sendAsync" in provider &&
typeof provider.sendAsync === "function" &&
provider.sendAsync.constructor.name === "AsyncFunction"
);
}
35 changes: 35 additions & 0 deletions packages/prover/src/provider_types/web3_js_provider_type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {AnyWeb3Provider, Web3ProviderType} from "../interfaces.js";
import {JsonRpcRequest, JsonRpcRequestOrBatch, JsonRpcResponse, JsonRpcResponseOrBatch} from "../types.js";
import {isBatchRequest} from "../utils/json_rpc.js";

export interface Web3jsProvider {
request: (payload: JsonRpcRequest) => Promise<JsonRpcResponse>;
}

export default {
name: "web3js",
matched(provider): provider is Web3jsProvider {
return (
"isWeb3Provider" in provider.constructor &&
(provider.constructor as {isWeb3Provider: (provider: AnyWeb3Provider) => boolean}).isWeb3Provider(provider)
);
},
handler(provider) {
const request = provider.request.bind(provider);

return async (payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> => {
if (isBatchRequest(payload)) {
return Promise.all(payload.map((p) => request(p)));
}

return request(payload);
};
},
mutateProvider(provider, newHandler) {
Object.assign(provider, {
request: async function newRequest(payload: JsonRpcRequestOrBatch): Promise<JsonRpcResponseOrBatch | undefined> {
return newHandler(payload);
},
});
},
} as Web3ProviderType<Web3jsProvider>;
1 change: 1 addition & 0 deletions packages/prover/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export type ELStorageProof = Pick<ELProof, "storageHash" | "storageProof">;

/* eslint-disable @typescript-eslint/naming-convention */
export type ELApi = {
eth_getBalance: (address: string, block?: number | string) => string;
eth_createAccessList: (transaction: ELTransaction, block?: ELBlockNumberOrTag) => ELAccessListResponse;
eth_call: (transaction: ELTransaction, block?: ELBlockNumberOrTag) => HexString;
eth_estimateGas: (transaction: ELTransaction, block?: ELBlockNumberOrTag) => HexString;
Expand Down
Loading