@lodestar/prover
Version:
A Typescript implementation of the Ethereum Consensus light client
113 lines (101 loc) • 3.33 kB
text/typescript
import {Logger} from "@lodestar/utils";
import {ProofProvider} from "../proof_provider/proof_provider.js";
import {ELBlock, ELProof, HexString, JsonRpcRequest} from "../types.js";
import {bufferToHex} from "./conversion.js";
import {getELBlock, getELCode, getELProof} from "./execution.js";
import {ELRpcProvider} from "./rpc_provider.js";
import {isValidAccount, isValidBlock, isValidCodeHash, isValidStorageKeys} from "./validation.js";
type VerificationResult<T> = {data: T; valid: true} | {valid: false; data?: undefined};
export async function verifyAccount({
address,
proofProvider,
logger,
rpc,
block,
}: {
address: HexString;
rpc: ELRpcProvider;
proofProvider: ProofProvider;
logger: Logger;
block?: number | string;
}): Promise<VerificationResult<ELProof>> {
try {
const executionPayload = await proofProvider.getExecutionPayload(block ?? "latest");
const proof = await getELProof(rpc, [address, [], bufferToHex(executionPayload.blockHash)]);
const validAccount = await isValidAccount({
address: address,
stateRoot: executionPayload.stateRoot,
proof,
logger,
});
// If account is invalid don't check the storage
const validStorage = validAccount && (await isValidStorageKeys({storageKeys: [], proof, logger}));
if (validAccount && validStorage) {
return {data: proof, valid: true};
}
return {valid: false};
} catch (err) {
logger.error("Error while verifying account", {address}, err as Error);
return {valid: false};
}
}
export async function verifyCode({
address,
proofProvider,
logger,
rpc,
codeHash,
block,
}: {
address: HexString;
rpc: ELRpcProvider;
proofProvider: ProofProvider;
logger: Logger;
codeHash: HexString;
block?: number | string;
}): Promise<VerificationResult<string>> {
try {
const executionPayload = await proofProvider.getExecutionPayload(block ?? "latest");
const code = await getELCode(rpc, [address, bufferToHex(executionPayload.blockHash)]);
if (await isValidCodeHash({codeHash, codeResponse: code, logger})) {
return {data: code, valid: true};
}
return {valid: false};
} catch (err) {
logger.error("Error while verifying code", {address}, err as Error);
return {valid: false};
}
}
export async function verifyBlock({
payload,
proofProvider,
logger,
rpc,
}: {
payload: JsonRpcRequest<[block: string | number, hydrated: boolean]>;
rpc: ELRpcProvider;
proofProvider: ProofProvider;
logger: Logger;
}): Promise<VerificationResult<ELBlock>> {
try {
const blockNumber = payload.params[0];
const executionPayload = await proofProvider.getExecutionPayload(blockNumber);
const block = await getELBlock(rpc, [blockNumber, true]); // Always request hydrated blocks as we need access to `transactions` details
// If response is not valid from the EL we don't need to verify it
if (!block) return {data: block, valid: false};
if (
await isValidBlock({
logger,
block,
executionPayload,
config: proofProvider.config,
})
) {
return {data: block, valid: true};
}
return {valid: false};
} catch (err) {
logger.error("Error while verifying block", {block: payload.params[0]}, err as Error);
return {valid: false};
}
}