Skip to content

Commit

Permalink
Merge pull request #843 from ChainSafe/tuyen/enr-eth2
Browse files Browse the repository at this point in the history
Add ENRForkID to ENR eth2
  • Loading branch information
twoeths authored Apr 28, 2020
2 parents d112ae3 + f7978c3 commit 9b94085
Show file tree
Hide file tree
Showing 19 changed files with 321 additions and 22 deletions.
19 changes: 19 additions & 0 deletions packages/lodestar-beacon-state-transition/src/epoch/fork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {IBeaconConfig} from "@chainsafe/lodestar-config";
import {BeaconState} from "@chainsafe/lodestar-types";
import {getCurrentEpoch} from "..";
import {intToBytes} from "@chainsafe/lodestar-utils";

export function processForkChanged(config: IBeaconConfig, state: BeaconState): void {
const currentEpoch = getCurrentEpoch(config, state);
const nextEpoch = currentEpoch + 1;
const currentForkVersion = state.fork.currentVersion;
const nextFork = config.params.ALL_FORKS && config.params.ALL_FORKS.find(
(fork) => config.types.Version.equals(currentForkVersion, intToBytes(fork.previousVersion, 4)));
if (nextFork && nextFork.epoch === nextEpoch) {
state.fork = {
previousVersion: state.fork.currentVersion,
currentVersion: intToBytes(nextFork.currentVersion, 4),
epoch: nextFork.epoch,
};
}
}
4 changes: 4 additions & 0 deletions packages/lodestar-beacon-state-transition/src/epoch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {processFinalUpdates} from "./finalUpdates";
import {processJustificationAndFinalization} from "./justification";
import {processRegistryUpdates} from "./registryUpdates";
import {processSlashings} from "./slashings";
import {processForkChanged} from "./fork";

export * from "./balanceUpdates";
export * from "./finalUpdates";
Expand Down Expand Up @@ -41,6 +42,9 @@ export function processEpoch(config: IBeaconConfig, state: BeaconState): BeaconS
// Final Updates
processFinalUpdates(config, state);

// check and process planned hard fork
processForkChanged(config, state);

// TODO Later Phase
// afterProcessFinalUpdates

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {config} from "@chainsafe/lodestar-config/lib/presets/mainnet";
import {BeaconState} from "@chainsafe/lodestar-types";
import {generateState} from "../../../utils/state";
import {processForkChanged} from "../../../../src/epoch/fork";
import {expect} from "chai";
import {bytesToInt} from "@chainsafe/lodestar-utils";

describe("processForkChanged", () => {
let state: BeaconState;

beforeEach(() => {
state = generateState();
state.fork = {
currentVersion: Buffer.from([1, 0, 0, 0]),
epoch: 100,
previousVersion: Buffer.alloc(4),
};
});

afterEach(() => {
config.params.ALL_FORKS = undefined;
});

it("should not update fork if no matched next fork", () => {
config.params.ALL_FORKS = undefined;
const preFork = state.fork;
processForkChanged(config, state);
expect(config.types.Fork.equals(preFork, state.fork)).to.be.true;
});

it("should not update fork if found matched next fork but epoch not matched", () => {
config.params.ALL_FORKS = [
{
previousVersion: bytesToInt(Buffer.from([1, 0, 0, 0])),
currentVersion: bytesToInt(Buffer.from([2, 0, 0, 0])),
epoch: 200,
},
];
const preFork = state.fork;
processForkChanged(config, state);
expect(config.types.Fork.equals(preFork, state.fork)).to.be.true;
});

it("should update fork if found matched next fork and matched epoch", () => {
config.params.ALL_FORKS = [
{
previousVersion: bytesToInt(Buffer.from([1, 0, 0, 0])),
currentVersion: bytesToInt(Buffer.from([2, 0, 0, 0])),
epoch: 200,
},
];
const preFork = state.fork;
state.slot = 200 * config.params.SLOTS_PER_EPOCH - 1;
processForkChanged(config, state);
expect(config.types.Fork.equals(preFork, state.fork)).to.be.false;
expect(config.types.Version.equals(preFork.currentVersion, state.fork.previousVersion));
expect(state.fork.epoch).to.be.equal(200);
});
});
10 changes: 9 additions & 1 deletion packages/lodestar-cli/src/commands/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ import {getTomlConfig} from "../lodestar/util/file";
import {createNodeJsLibp2p, loadPeerIdFromJsonFile} from "@chainsafe/lodestar/lib/network/nodejs";
import {ENR} from "@chainsafe/discv5";
import {initBLS} from "@chainsafe/bls";
import fs from "fs";
import {load} from "js-yaml";

interface IBeaconCommandOptions {
[key: string]: string;
peerId: string;
forkFile?: string;
configFile?: string;
preset?: string;
loggingLevel?: string;
Expand Down Expand Up @@ -52,7 +55,7 @@ export class BeaconNodeCommand implements ICliCommand {
generateCommanderOptions(command, BeaconNodeOptions);
}

public async action(cmdOptions: IBeaconCommandOptions, logger: ILogger): Promise<void> {
public async action(cmdOptions: IBeaconCommandOptions, logger: ILogger): Promise<BeaconNode> {
let nodeOptions: Partial<IBeaconNodeOptions> = {};
//find better place for this once this cli is refactored
await initBLS();
Expand Down Expand Up @@ -81,7 +84,12 @@ export class BeaconNodeCommand implements ICliCommand {
const libp2p = await createNodeJsLibp2p(peerId, libp2pOpt);
const config = cmdOptions.preset === "minimal" ? minimalConfig : mainnetConfig;
// nodejs will create EthersEth1Notifier by default
if (cmdOptions.forkFile) {
// @ts-ignore
config.params.ALL_FORKS = load(fs.readFileSync(cmdOptions.forkFile));
}
this.node = new BeaconNode(nodeOptions, {config, logger, libp2p});
await this.node.start();
return this.node;
}
}
22 changes: 20 additions & 2 deletions packages/lodestar-cli/test/e2e/commands/beacon.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import {config} from "@chainsafe/lodestar-config/lib/presets/minimal";
import rimraf from "rimraf";
import fs from "fs";
import {assert} from "chai";
import {BeaconNodeCommand, DepositCommand} from "../../../src/commands";
import {ILogger, WinstonLogger} from "@chainsafe/lodestar-utils/lib/logger";
import {PrivateEth1Network} from "@chainsafe/lodestar/lib/eth1/dev";
import {JsonRpcProvider} from "ethers/providers";
import {createPeerId} from "@chainsafe/lodestar/lib/network";
import {savePeerId} from "@chainsafe/lodestar/lib/network/nodejs";
import {bytesToInt, intToBytes} from "@chainsafe/lodestar-utils";
import {dump} from "js-yaml";

describe("beacon cli", function() {
this.timeout(0);
Expand All @@ -16,6 +19,7 @@ describe("beacon cli", function() {
//same folder of default db
const tmpDir = ".tmp";
const peerIdPath = `${tmpDir}/peer-id.json`;
const forkFile = `${tmpDir}/forks.yml`;

before(async () => {
if (fs.existsSync(tmpDir)) {
Expand All @@ -24,6 +28,16 @@ describe("beacon cli", function() {
fs.mkdirSync(`${tmpDir}/lodestar-db`, {recursive: true});
const peerId = await createPeerId();
await savePeerId(peerIdPath, peerId);
const ALL_FORKS = [
{
currentVersion: 2,
epoch: 100,
// GENESIS_FORK_VERSION is <Buffer 00 00 00 01> but previousVersion = 16777216 not 1 due to bytesToInt
previousVersion: bytesToInt(config.params.GENESIS_FORK_VERSION)
},
];
const yml = dump(ALL_FORKS);
fs.writeFileSync(forkFile, yml);
});

after(() => {
Expand Down Expand Up @@ -66,11 +80,15 @@ describe("beacon cli", function() {
eth1RpcUrl: eth1Network.rpcUrl(),
networkId: "999",
depositContractBlockNum: "0", // not really but it's ok
depositContract: contractAddress
depositContract: contractAddress,
forkFile: forkFile,
};
const cmd = new BeaconNodeCommand();
await cmd.action(cmdOptions, logger);
const node = await cmd.action(cmdOptions, logger);
logger.verbose("cmd.action started node successfully");
const enrForkID = await node.chain.getENRForkID();
assert(config.types.Version.equals(enrForkID.nextForkVersion, intToBytes(2, 4)));
assert(enrForkID.nextForkEpoch === 100);
await new Promise((resolve) => setTimeout(resolve, 10 * config.params.SECONDS_PER_SLOT * 1000));
await cmd.node.stop();
logger.verbose("cmd.stop stopped node successfully");
Expand Down
13 changes: 13 additions & 0 deletions packages/lodestar-params/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,17 @@ export interface IBeaconParams {
MAX_ATTESTATIONS: number;
MAX_DEPOSITS: number;
MAX_VOLUNTARY_EXITS: number;

// Old and future forks
ALL_FORKS: IFork[];
}

interface IFork {
// 4 bytes
previousVersion: number;
// 4 bytes
currentVersion: number;
// Fork epoch number
epoch: number;
}

8 changes: 8 additions & 0 deletions packages/lodestar-types/src/ssz/generators/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export const ForkData = (ssz: IBeaconSSZTypes): ContainerType => new ContainerTy
},
});

export const ENRForkID = (ssz: IBeaconSSZTypes): ContainerType => new ContainerType({
fields: {
forkDigest: ssz.ForkDigest,
nextForkVersion: ssz.Version,
nextForkEpoch: ssz.Epoch,
},
});

export const Checkpoint = (ssz: IBeaconSSZTypes): ContainerType => new ContainerType({
fields: {
epoch: ssz.Epoch,
Expand Down
2 changes: 2 additions & 0 deletions packages/lodestar-types/src/ssz/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface IBeaconSSZTypes {
// misc
Fork: ContainerType<t.Fork>;
ForkData: ContainerType<t.ForkData>;
ENRForkID: ContainerType<t.ENRForkID>;
Checkpoint: ContainerType<t.Checkpoint>;
Validator: ContainerType<t.Validator>;
AttestationData: ContainerType<t.AttestationData>;
Expand Down Expand Up @@ -110,6 +111,7 @@ export const typeNames: (keyof IBeaconSSZTypes)[] = [
// misc
"Fork",
"ForkData",
"ENRForkID",
"Checkpoint",
"Validator",
"AttestationData",
Expand Down
10 changes: 10 additions & 0 deletions packages/lodestar-types/src/types/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
CommitteeIndex,
Bytes32,
Domain,
ForkDigest,
} from "./primitive";

export interface Fork {
Expand All @@ -36,6 +37,15 @@ export interface ForkData {
genesisValidatorsRoot: Root;
}

export interface ENRForkID {
// Current fork digest
forkDigest: ForkDigest;
// next planned fork versin
nextForkVersion: Version;
// next fork epoch
nextForkEpoch: Epoch;
}

export interface Checkpoint {
epoch: Epoch;
root: Root;
Expand Down
7 changes: 4 additions & 3 deletions packages/lodestar-utils/test/unit/bytes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import {assert, expect} from "chai";
import {describe, it} from "mocha";
import {intToBytes, bytesToInt} from "../../src";

describe("intToBytes", () => {
describe("intToBytes", () => {
const zeroedArray = (length: number): number[] => Array.from({length}, () => 0);
const testCases: { input: [bigint | number, number]; output: Buffer }[] = [
{input: [255, 1], output: Buffer.from([255])},
{input: [255, 1], output: Buffer.from([255])},
{input: [1, 4], output: Buffer.from([1, 0, 0, 0])},
{input: [255n, 1], output: Buffer.from([255])},
{input: [65535, 2], output: Buffer.from([255, 255])},
{input: [65535n, 2], output: Buffer.from([255, 255])},
Expand All @@ -31,7 +32,7 @@ describe("intToBytes", () => {
}
});

describe.only("bytesToInt", () => {
describe("bytesToInt", () => {
const testCases: { input: Buffer; output: number}[] = [
{input: Buffer.from([3]), output: 3},
{input: Buffer.from([20, 0]), output: 20},
Expand Down
16 changes: 14 additions & 2 deletions packages/lodestar/src/chain/blocks/process.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import {IBlockProcessJob} from "../chain";
import {BeaconState, Root, SignedBeaconBlock} from "@chainsafe/lodestar-types";
import {stateTransition} from "@chainsafe/lodestar-beacon-state-transition";
import {stateTransition, computeEpochAtSlot} from "@chainsafe/lodestar-beacon-state-transition";
import {toHexString} from "@chainsafe/ssz";
import {IBeaconConfig} from "@chainsafe/lodestar-config";
import {IBeaconDb} from "../../db/api";
import {ILogger} from "@chainsafe/lodestar-utils/lib/logger";
import {ILMDGHOST} from "../forkChoice";
import {BlockPool} from "./pool";
import {ChainEventEmitter} from "..";

export function processBlock(
config: IBeaconConfig, db: IBeaconDb, logger: ILogger, forkChoice: ILMDGHOST, pool: BlockPool
config: IBeaconConfig,
db: IBeaconDb,
logger: ILogger,
forkChoice: ILMDGHOST,
pool: BlockPool,
eventBus: ChainEventEmitter,
): (source: AsyncIterable<IBlockProcessJob>) => AsyncGenerator<{block: SignedBeaconBlock; postState: BeaconState}> {
return (source) => {
return (async function*() {
Expand All @@ -32,6 +38,12 @@ export function processBlock(
const newChainHeadRoot = await updateForkChoice(config, db, forkChoice, job.signedBlock, newState);
if(newChainHeadRoot) {
logger.important(`Fork choice changed head to 0x${toHexString(newChainHeadRoot)}`);
if(!config.types.Fork.equals(preState.fork, newState.fork)) {
const epoch = computeEpochAtSlot(config, newState.slot);
const currentVersion = newState.fork.currentVersion;
logger.important(`Fork version changed to ${currentVersion} at slot ${newState.slot} and epoch ${epoch}`);
eventBus.emit("forkDigestChanged");
}
await updateDepositMerkleTree(config, db, newState);
}
pool.onProcessedBlock(job.signedBlock);
Expand Down
2 changes: 1 addition & 1 deletion packages/lodestar/src/chain/blocks/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class BlockProcessor implements IService {
return abortable(source, abortSignal, {returnOnAbort: true});
},
validateBlock(this.config, this.logger, this.db, this.forkChoice),
processBlock(this.config, this.db, this.logger, this.forkChoice, this.pendingBlocks),
processBlock(this.config, this.db, this.logger, this.forkChoice, this.pendingBlocks, this.eventBus),
postProcess(
this.config,
this.db,
Expand Down
Loading

0 comments on commit 9b94085

Please sign in to comment.