@lodestar/prover
Version:
A Typescript implementation of the Ethereum Consensus light client
96 lines • 4.27 kB
JavaScript
import { Block } from "@ethereumjs/block";
import { RLP } from "@ethereumjs/rlp";
import { Trie } from "@ethereumjs/trie";
import { Account, KECCAK256_NULL_S } from "@ethereumjs/util";
import { keccak256 } from "ethereum-cryptography/keccak.js";
import { blockDataFromELBlock, bufferToHex, hexToBuffer, padLeft } from "./conversion.js";
import { getChainCommon } from "./execution.js";
const emptyAccountSerialize = new Account().serialize();
const storageKeyLength = 32;
export function isBlockNumber(block) {
if (typeof block === "number") {
return true;
}
// If block is hex and less than 32 byte long it is a block number, else it's a block hash
return hexToBuffer(block).byteLength < 32;
}
export async function isValidAccount({ address, stateRoot, proof, logger, }) {
const trie = await Trie.create();
const key = keccak256(hexToBuffer(address));
try {
const expectedAccountRLP = await trie.verifyProof(Buffer.from(stateRoot), Buffer.from(key), proof.accountProof.map(hexToBuffer));
// Shresth Agrawal (2022) Patronum source code. https://github.com/lightclients/patronum
const account = Account.fromAccountData({
nonce: BigInt(proof.nonce),
balance: BigInt(proof.balance),
storageRoot: proof.storageHash,
codeHash: proof.codeHash,
});
return account.serialize().equals(expectedAccountRLP ? expectedAccountRLP : emptyAccountSerialize);
}
catch (err) {
logger.error("Error verifying account proof", undefined, err);
return false;
}
}
export async function isValidStorageKeys({ storageKeys, proof, logger, }) {
const trie = await Trie.create();
for (let i = 0; i < storageKeys.length; i++) {
const sp = proof.storageProof[i];
const key = keccak256(padLeft(hexToBuffer(storageKeys[i]), storageKeyLength));
try {
const expectedStorageRLP = await trie.verifyProof(hexToBuffer(proof.storageHash), Buffer.from(key), sp.proof.map(hexToBuffer));
// buffer.equals is not compatible with Uint8Array for browser
// so we need to convert the output of RLP.encode to Buffer first
const isStorageValid = (!expectedStorageRLP && sp.value === "0x0") ||
(!!expectedStorageRLP && expectedStorageRLP.equals(Buffer.from(RLP.encode(sp.value))));
if (!isStorageValid)
return false;
}
catch (err) {
logger.error("Error verifying storage keys", undefined, err);
return false;
}
}
return true;
}
export async function isValidBlock({ executionPayload, block, logger, config, }) {
if (bufferToHex(executionPayload.blockHash) !== block.hash) {
logger.error("Block hash does not match", {
rpcBlockHash: block.hash,
beaconExecutionBlockHash: bufferToHex(executionPayload.blockHash),
});
return false;
}
if (bufferToHex(executionPayload.parentHash) !== block.parentHash) {
logger.error("Block parent hash does not match", {
rpcBlockHash: block.parentHash,
beaconExecutionBlockHash: bufferToHex(executionPayload.parentHash),
});
return false;
}
const common = getChainCommon(config.PRESET_BASE);
common.setHardforkByBlockNumber(executionPayload.blockNumber, undefined, executionPayload.timestamp);
const blockObject = Block.fromBlockData(blockDataFromELBlock(block), { common });
if (!(await blockObject.validateTransactionsTrie())) {
logger.error("Block transactions could not be verified.", {
blockHash: bufferToHex(blockObject.hash()),
blockNumber: blockObject.header.number,
});
return false;
}
return true;
}
export async function isValidCodeHash({ codeHash, codeResponse, }) {
// if there is no code hash for that address
if (codeResponse === "0x" && codeHash === `0x${KECCAK256_NULL_S}`)
return true;
return bufferToHex(keccak256(hexToBuffer(codeResponse))) === codeHash;
}
export function isNullish(val) {
return val === null || val === undefined;
}
export function isPresent(val) {
return !isNullish(val);
}
//# sourceMappingURL=validation.js.map