@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
119 lines • 5.22 kB
JavaScript
import { fromHex } from "@lodestar/utils";
import { Eth1DepositDataTracker } from "./eth1DepositDataTracker.js";
import { Eth1MergeBlockTracker } from "./eth1MergeBlockTracker.js";
import { Eth1Provider } from "./provider/eth1Provider.js";
export { Eth1Provider };
// This module encapsulates all consumer functionality to the execution node (formerly eth1). The execution client
// has to:
//
// - For genesis, the beacon node must follow the eth1 chain: get all deposit events + blocks within that range.
// Once the genesis conditions are met, start the POS chain with the resulting state. The logic is similar to the
// two points below, but the implementation is specialized for each scenario.
//
// - Follow the eth1 block chain to validate eth1Data votes. It needs all consecutive blocks within a specific range
// and at a distance from the head.
// ETH1_FOLLOW_DISTANCE uint64(2**11) (= 2,048) Eth1 blocks ~8 hours
// EPOCHS_PER_ETH1_VOTING_PERIOD uint64(2**6) (= 64) epochs ~6.8 hours
//
// - Fetch ALL deposit events from the deposit contract to build the deposit tree and validate future merkle proofs.
// Then it must follow deposit events at a distance roughly similar to the `ETH1_FOLLOW_DISTANCE` parameter above.
//
// - [New bellatrix]: After BELLATRIX_FORK_EPOCH, it must fetch the block with hash
// `state.eth1_data.block_hash` to compute `terminal_total_difficulty`. Note this may change with
// https://github.com/ethereum/consensus-specs/issues/2603.
//
// - [New bellatrix]: On block production post BELLATRIX_FORK_EPOCH, pre merge, the beacon node must find the merge block
// crossing the `terminal_total_difficulty` boundary and include it in the block. After the merge block production
// will just use `execution_engine.assemble_block` without fetching individual blocks.
//
// - [New bellatrix]: Fork-choice must validate the merge block ensuring it crossed the `terminal_total_difficulty`
// boundary, so it must fetch the POW block referenced in the merge block + its POW parent block.
//
// With the merge the beacon node has to follow the eth1 chain at two distances:
// 1. At `ETH1_FOLLOW_DISTANCE` for eth1Data to be re-org safe
// 2. At the head to get the first merge block, tolerating possible re-orgs
//
// Then both streams of blocks should not be merged since it's harder to guard against re-orgs from (2) to (1).
export function initializeEth1ForBlockProduction(opts, modules) {
if (opts.enabled) {
return new Eth1ForBlockProduction(opts, {
config: modules.config,
db: modules.db,
metrics: modules.metrics,
logger: modules.logger,
signal: modules.signal,
});
}
return new Eth1ForBlockProductionDisabled();
}
export class Eth1ForBlockProduction {
constructor(opts, modules) {
const eth1Provider = modules.eth1Provider ||
new Eth1Provider(modules.config, { ...opts, logger: modules.logger }, modules.signal, modules.metrics?.eth1HttpClient);
this.eth1DepositDataTracker = opts.disableEth1DepositDataTracker
? null
: new Eth1DepositDataTracker(opts, modules, eth1Provider);
this.eth1MergeBlockTracker = new Eth1MergeBlockTracker(modules, eth1Provider);
}
async getEth1DataAndDeposits(state) {
if (this.eth1DepositDataTracker === null) {
return { eth1Data: state.eth1Data, deposits: [] };
}
return this.eth1DepositDataTracker.getEth1DataAndDeposits(state);
}
async getTerminalPowBlock() {
const block = await this.eth1MergeBlockTracker.getTerminalPowBlock();
return block && fromHex(block.blockHash);
}
getPowBlock(powBlockHash) {
return this.eth1MergeBlockTracker.getPowBlock(powBlockHash);
}
getTDProgress() {
return this.eth1MergeBlockTracker.getTDProgress();
}
startPollingMergeBlock() {
this.eth1MergeBlockTracker.startPollingMergeBlock();
}
isPollingEth1Data() {
return this.eth1DepositDataTracker?.isPollingEth1Data() ?? false;
}
stopPollingEth1Data() {
this.eth1DepositDataTracker?.stopPollingEth1Data();
}
}
/**
* Disabled version of Eth1ForBlockProduction
* May produce invalid blocks by not adding new deposits and voting for the same eth1Data
*/
export class Eth1ForBlockProductionDisabled {
/**
* Returns same eth1Data as in state and no deposits
* May produce invalid blocks if deposits have to be added
*/
async getEth1DataAndDeposits(state) {
return { eth1Data: state.eth1Data, deposits: [] };
}
/**
* Will miss the oportunity to propose the merge block but will still produce valid blocks
*/
async getTerminalPowBlock() {
return null;
}
/** Will not be able to validate the merge block */
async getPowBlock(_powBlockHash) {
throw Error("eth1 must be enabled to verify merge block");
}
getTDProgress() {
return null;
}
isPollingEth1Data() {
return false;
}
startPollingMergeBlock() {
// Ignore
}
stopPollingEth1Data() {
// Ignore
}
}
//# sourceMappingURL=index.js.map