Skip to content

Commit

Permalink
Merge 8928377 into 6cf7c2c
Browse files Browse the repository at this point in the history
  • Loading branch information
dapplion committed Sep 19, 2021
2 parents 6cf7c2c + 8928377 commit 364b75c
Show file tree
Hide file tree
Showing 43 changed files with 1,062 additions and 278 deletions.
12 changes: 11 additions & 1 deletion packages/beacon-state-transition/src/merge/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {merge, ssz} from "@chainsafe/lodestar-types";
import {allForks, merge, ssz} from "@chainsafe/lodestar-types";

/**
* Execution enabled = merge is done.
Expand Down Expand Up @@ -32,3 +32,13 @@ export function isMergeComplete(state: merge.BeaconState): boolean {
ssz.merge.ExecutionPayloadHeader.defaultTreeBacked()
);
}

/** Type guard for merge.BeaconState */
export function isMergeStateType(state: allForks.BeaconState): state is merge.BeaconState {
return (state as merge.BeaconState).latestExecutionPayloadHeader !== undefined;
}

/** Type guard for merge.BeaconBlockBody */
export function isMergeBlockBodyType(blockBody: allForks.BeaconBlockBody): blockBody is merge.BeaconBlockBody {
return (blockBody as merge.BeaconBlockBody).executionPayload !== undefined;
}
16 changes: 2 additions & 14 deletions packages/cli/src/cmds/beacon/initBeaconState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,7 @@ import {TreeBacked} from "@chainsafe/ssz";
import {createIBeaconConfig, IBeaconConfig, IChainForkConfig} from "@chainsafe/lodestar-config";
import {fromHex, ILogger} from "@chainsafe/lodestar-utils";
import {computeEpochAtSlot, allForks} from "@chainsafe/lodestar-beacon-state-transition";
import {
IBeaconDb,
Eth1Provider,
IBeaconNodeOptions,
initStateFromAnchorState,
initStateFromEth1,
} from "@chainsafe/lodestar";
import {IBeaconDb, IBeaconNodeOptions, initStateFromAnchorState, initStateFromEth1} from "@chainsafe/lodestar";
// eslint-disable-next-line no-restricted-imports
import {getStateTypeFromBytes} from "@chainsafe/lodestar/lib/util/multifork";
import {downloadOrLoadFile} from "../../util";
Expand Down Expand Up @@ -127,13 +121,7 @@ export async function initBeaconState(
const config = createIBeaconConfig(chainForkConfig, anchorState.genesisValidatorsRoot);
return await initStateFromAnchorState(config, db, logger, anchorState);
} else {
return await initStateFromEth1(
chainForkConfig,
db,
logger,
new Eth1Provider(chainForkConfig, options.eth1, signal),
signal
);
return await initStateFromEth1({config: chainForkConfig, db, logger, opts: options.eth1, signal});
}
}
}
15 changes: 7 additions & 8 deletions packages/fork-choice/src/forkChoice/forkChoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class ForkChoice implements IForkChoice {
private readonly config: IChainForkConfig,
private readonly fcStore: IForkChoiceStore,
/** Nullable until merge time comes */
private transitionStore: ITransitionStore | null,
private readonly transitionStore: ITransitionStore,
/** The underlying representation of the block DAG. */
private readonly protoArray: ProtoArray,
/**
Expand All @@ -91,12 +91,6 @@ export class ForkChoice implements IForkChoice {
this.head = this.updateHead();
}

/** For merge transition. Initialize transition store when merge fork condition is met */
initializeTransitionStore(transitionStore: ITransitionStore): void {
if (this.transitionStore !== null) throw Error("transitionStore already initialized");
this.transitionStore = transitionStore;
}

/**
* Returns the block root of an ancestor of `blockRoot` at the given `slot`.
* (Note: `slot` refers to the block that is *returned*, not the one that is supplied.)
Expand Down Expand Up @@ -304,7 +298,12 @@ export class ForkChoice implements IForkChoice {
});
}

if (this.transitionStore && merge.isMergeBlock(state as merge.BeaconState, (block as merge.BeaconBlock).body)) {
if (
this.transitionStore.initialized &&
merge.isMergeStateType(state) &&
merge.isMergeBlockBodyType(block.body) &&
merge.isMergeBlock(state, block.body)
) {
const {powBlock, powBlockParent} = preCachedData || {};
if (!powBlock) throw Error("onBlock preCachedData must include powBlock");
if (!powBlockParent) throw Error("onBlock preCachedData must include powBlock");
Expand Down
3 changes: 0 additions & 3 deletions packages/fork-choice/src/forkChoice/interface.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import {Epoch, Slot, ValidatorIndex, phase0, allForks, Root, RootHex} from "@chainsafe/lodestar-types";
import {IProtoBlock} from "../protoArray/interface";
import {CheckpointWithHex} from "./store";
import {ITransitionStore} from "./transitionStore";

export type CheckpointHex = {
epoch: Epoch;
root: RootHex;
};

export interface IForkChoice {
/** For merge transition. Initialize transition store when merge fork condition is met */
initializeTransitionStore(transitionStore: ITransitionStore): void;
/**
* Returns the block root of an ancestor of `block_root` at the given `slot`. (Note: `slot` refers
* to the block that is *returned*, not the one that is supplied.)
Expand Down
7 changes: 7 additions & 0 deletions packages/fork-choice/src/forkChoice/transitionStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
export interface ITransitionStore {
/**
* Equivalent to spec check `TransitionStore not null`.
* Since the TransitionStore is used in fork-choice + block production it's simpler for it to be always not null,
* and handle the initialized state internally.
*/
initialized: boolean;
/**
* Cumulative total difficulty over the entire Ethereum POW network.
* Value may not be always available
*/
terminalTotalDifficulty: bigint;
}
4 changes: 2 additions & 2 deletions packages/fork-choice/test/unit/forkChoice/forkChoice.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ForkChoice, IForkChoiceStore, ProtoArray} from "../../../src";
import {ForkChoice, IForkChoiceStore, ITransitionStore, ProtoArray} from "../../../src";
import {config} from "@chainsafe/lodestar-config/default";
import {expect} from "chai";
import {fromHexString} from "@chainsafe/ssz";
Expand Down Expand Up @@ -44,7 +44,7 @@ describe("Forkchoice", function () {
bestJustifiedCheckpoint: {epoch: genesisEpoch, root: fromHexString(finalizedRoot), rootHex: finalizedRoot},
};

const transitionStore = null;
const transitionStore: ITransitionStore = {initialized: false, terminalTotalDifficulty: BigInt(0)};

it("getAllAncestorBlocks", function () {
protoArr.onBlock(block);
Expand Down
1 change: 1 addition & 0 deletions packages/lodestar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@ethersproject/abi": "^5.0.0",
"@types/datastore-level": "^3.0.0",
"bl": "^5.0.0",
"buffer-xor": "^2.0.2",
"cross-fetch": "^3.1.4",
"datastore-level": "^6.0.2",
"deepmerge": "^3.2.0",
Expand Down
2 changes: 0 additions & 2 deletions packages/lodestar/src/api/impl/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import {IBeaconChain} from "../../chain";
import {IBeaconDb} from "../../db";
import {IBeaconSync} from "../../sync";
import {INetwork} from "../../network";
import {IEth1ForBlockProduction} from "../../eth1";
import {IMetrics} from "../../metrics";

export type ApiModules = {
config: IChainForkConfig;
chain: IBeaconChain;
db: IBeaconDb;
eth1: IEth1ForBlockProduction;
logger: ILogger;
metrics: IMetrics | null;
network: INetwork;
Expand Down
17 changes: 2 additions & 15 deletions packages/lodestar/src/api/impl/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,7 @@ const SYNC_TOLERANCE_EPOCHS = 1;
* Server implementation for handling validator duties.
* See `@chainsafe/lodestar-validator/src/api` for the client implementation).
*/
export function getValidatorApi({
chain,
config,
eth1,
logger,
metrics,
network,
sync,
}: ApiModules): routes.validator.Api {
export function getValidatorApi({chain, config, logger, metrics, network, sync}: ApiModules): routes.validator.Api {
let genesisBlockRoot: Root | null = null;

/** Compute and cache the genesis block root */
Expand Down Expand Up @@ -149,12 +141,7 @@ export function getValidatorApi({
await waitForSlot(slot); // Must never request for a future slot > currentSlot

timer = metrics?.blockProductionTime.startTimer();
const block = await assembleBlock(
{config, chain, eth1, metrics},
slot,
randaoReveal,
toGraffitiBuffer(graffiti || "")
);
const block = await assembleBlock({chain, metrics}, slot, randaoReveal, toGraffitiBuffer(graffiti || ""));
metrics?.blockProductionSuccess.inc();
return {data: block, version: config.getForkName(block.slot)};
} finally {
Expand Down
2 changes: 1 addition & 1 deletion packages/lodestar/src/chain/blocks/stateTransition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export async function runStateTransition(

// current justified checkpoint should be prev epoch or current epoch if it's just updated
// it should always have epochBalances there bc it's a checkpoint state, ie got through processEpoch
let justifiedBalances: number[] = [];
let justifiedBalances: number[] | undefined = undefined;
if (postState.currentJustifiedCheckpoint.epoch > forkChoice.getJustifiedCheckpoint().epoch) {
const justifiedState = checkpointStateCache.get(toCheckpointHex(postState.currentJustifiedCheckpoint));
if (!justifiedState) {
Expand Down
15 changes: 13 additions & 2 deletions packages/lodestar/src/chain/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,16 @@ import {
import {ForkDigestContext, IForkDigestContext} from "../util/forkDigestContext";
import {LightClientIniter} from "./lightClient";
import {Archiver} from "./archiver";
import {IEth1ForBlockProduction} from "../eth1";
import {IExecutionEngine} from "../executionEngine";

export class BeaconChain implements IBeaconChain {
readonly genesisTime: Number64;
readonly genesisValidatorsRoot: Root;
readonly eth1: IEth1ForBlockProduction;
readonly executionEngine: IExecutionEngine;
// Expose config for convenience in modularized functions
readonly config: IBeaconConfig;

bls: IBlsVerifier;
forkChoice: IForkChoice;
Expand Down Expand Up @@ -75,7 +81,6 @@ export class BeaconChain implements IBeaconChain {
readonly seenContributionAndProof = new SeenContributionAndProof();

protected readonly blockProcessor: BlockProcessor;
protected readonly config: IBeaconConfig;
protected readonly db: IBeaconDb;
protected readonly logger: ILogger;
protected readonly metrics: IMetrics | null;
Expand All @@ -97,13 +102,17 @@ export class BeaconChain implements IBeaconChain {
metrics,
anchorState,
transitionStore,
eth1,
executionEngine,
}: {
config: IBeaconConfig;
db: IBeaconDb;
logger: ILogger;
metrics: IMetrics | null;
anchorState: TreeBacked<allForks.BeaconState>;
transitionStore: ITransitionStore | null;
transitionStore: ITransitionStore;
eth1: IEth1ForBlockProduction;
executionEngine: IExecutionEngine;
}
) {
this.opts = opts;
Expand All @@ -113,6 +122,8 @@ export class BeaconChain implements IBeaconChain {
this.metrics = metrics;
this.genesisTime = anchorState.genesisTime;
this.genesisValidatorsRoot = anchorState.genesisValidatorsRoot.valueOf() as Uint8Array;
this.eth1 = eth1;
this.executionEngine = executionEngine;

this.forkDigestContext = new ForkDigestContext(config, this.genesisValidatorsRoot);

Expand Down
102 changes: 76 additions & 26 deletions packages/lodestar/src/chain/factory/block/body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@
* @module chain/blockAssembly
*/

import {List} from "@chainsafe/ssz";
import {ForkName} from "@chainsafe/lodestar-params";
import {Bytes96, Bytes32, phase0, allForks, altair, Root, Slot} from "@chainsafe/lodestar-types";
import {IChainForkConfig} from "@chainsafe/lodestar-config";
import {CachedBeaconState} from "@chainsafe/lodestar-beacon-state-transition";

import {IEth1ForBlockProduction} from "../../../eth1";
import xor from "buffer-xor";
import {List, hash} from "@chainsafe/ssz";
import {Bytes96, Bytes32, phase0, allForks, altair, Root, Slot, BLSSignature, ssz} from "@chainsafe/lodestar-types";
import {
CachedBeaconState,
computeEpochAtSlot,
computeTimeAtSlot,
getCurrentEpoch,
getRandaoMix,
merge,
} from "@chainsafe/lodestar-beacon-state-transition";
import {IBeaconChain} from "../../interface";

export async function assembleBody(
{chain, config, eth1}: {chain: IBeaconChain; config: IChainForkConfig; eth1: IEth1ForBlockProduction},
chain: IBeaconChain,
currentState: CachedBeaconState<allForks.BeaconState>,
randaoReveal: Bytes96,
graffiti: Bytes32,
Expand All @@ -34,11 +38,11 @@ export async function assembleBody(
const [attesterSlashings, proposerSlashings] = chain.opPool.getSlashings(currentState);
const voluntaryExits = chain.opPool.getVoluntaryExits(currentState);
const attestations = chain.aggregatedAttestationPool.getAttestationsForBlock(currentState);
const {eth1Data, deposits} = await eth1.getEth1DataAndDeposits(
const {eth1Data, deposits} = await chain.eth1.getEth1DataAndDeposits(
currentState as CachedBeaconState<allForks.BeaconState>
);

const blockBodyPhase0: phase0.BeaconBlockBody = {
const blockBody: phase0.BeaconBlockBody = {
randaoReveal,
graffiti,
eth1Data,
Expand All @@ -49,25 +53,71 @@ export async function assembleBody(
voluntaryExits: voluntaryExits as List<phase0.SignedVoluntaryExit>,
};

const blockFork = config.getForkName(blockSlot);
switch (blockFork) {
case ForkName.phase0:
return blockBodyPhase0;
const blockEpoch = computeEpochAtSlot(blockSlot);

case ForkName.altair: {
const block: altair.BeaconBlockBody = {
...blockBodyPhase0,
syncAggregate: chain.syncContributionAndProofPool.getAggregate(
syncAggregateData.parentSlot,
syncAggregateData.parentBlockRoot
),
};
return block;
}
if (blockEpoch >= chain.config.ALTAIR_FORK_EPOCH) {
(blockBody as altair.BeaconBlockBody).syncAggregate = chain.syncContributionAndProofPool.getAggregate(
syncAggregateData.parentSlot,
syncAggregateData.parentBlockRoot
);
}

if (blockEpoch >= chain.config.MERGE_FORK_EPOCH) {
(blockBody as merge.BeaconBlockBody).executionPayload = await getExecutionPayload(
chain,
currentState as merge.BeaconState,
randaoReveal
);
}

return blockBody;
}

default:
throw new Error(`Block processing not implemented for fork ${blockFork}`);
/**
* Produce ExecutionPayload for pre-merge, merge, and post-merge.
*
* Expects `eth1MergeBlockFinder` to be actively searching for blocks well in advance to being called.
*/
async function getExecutionPayload(
chain: IBeaconChain,
state: merge.BeaconState,
randaoReveal: BLSSignature
): Promise<merge.ExecutionPayload> {
if (!merge.isMergeComplete(state)) {
const terminalPowBlockHash = chain.eth1.getMergeBlockHash();
if (terminalPowBlockHash === null) {
// Pre-merge, empty payload
ssz.merge.ExecutionPayload.defaultValue();
} else {
// Signify merge via producing on top of the last PoW block
const parentHash = terminalPowBlockHash;
return produceExecutionPayload(chain, state, parentHash, randaoReveal);
}
}

// Post-merge, normal payload
const parentHash = state.latestExecutionPayloadHeader.blockHash;
return produceExecutionPayload(chain, state, parentHash, randaoReveal);
}

async function produceExecutionPayload(
chain: IBeaconChain,
state: merge.BeaconState,
parentHash: Root,
randaoReveal: BLSSignature
): Promise<merge.ExecutionPayload> {
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
const randaoMix = computeRandaoMix(state, randaoReveal);

// NOTE: This is a naive implementation that does not give sufficient time to the eth1 block to produce an optimal
// block. Probably in the future there will exist mechanisms to optimize block production, such as giving a heads
// up to the execution client, then calling assembleBlock. Stay up to spec updates and update accordingly.
return chain.executionEngine.assembleBlock(parentHash, timestamp, randaoMix);
}

function computeRandaoMix(state: merge.BeaconState, randaoReveal: BLSSignature): Bytes32 {
const epoch = getCurrentEpoch(state);
return xor(Buffer.from(getRandaoMix(state, epoch) as Uint8Array), Buffer.from(hash(randaoReveal as Uint8Array)));
}

/** process_sync_committee_contributions is implemented in syncCommitteeContribution.getSyncAggregate */
Loading

0 comments on commit 364b75c

Please sign in to comment.