Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add capella support to sim multi-fork tests #5489

Merged
merged 18 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test-sim.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ on:
workflow_dispatch:

env:
GETH_DOCKER_IMAGE: ethereum/client-go:v1.10.25
GETH_DOCKER_IMAGE: ethereum/client-go:v1.11.6
LIGHTHOUSE_DOCKER_IMAGE: sigp/lighthouse:latest-amd64-unstable-dev
NETHERMIND_DOCKER_IMAGE: nethermind/nethermind:1.14.5
NETHERMIND_DOCKER_IMAGE: nethermind/nethermind:1.18.0

jobs:
tests-sim:
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/test/sim/backup_eth_provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from "../utils/simulation/utils/index.js";
import {connectAllNodes, waitForSlot} from "../utils/simulation/utils/network.js";

const genesisSlotsDelay = 20;
const genesisDelaySeconds = 20 * SIM_TESTS_SECONDS_PER_SLOT;
const altairForkEpoch = 2;
const bellatrixForkEpoch = 4;
// Make sure bellatrix started before TTD reach
Expand All @@ -23,15 +23,15 @@ const syncWaitEpoch = 2;

const runTimeoutMs =
getEstimatedTimeInSecForRun({
genesisSlotDelay: genesisSlotsDelay,
genesisDelaySeconds,
secondsPerSlot: SIM_TESTS_SECONDS_PER_SLOT,
runTill: runTillEpoch + syncWaitEpoch,
// After adding Nethermind its took longer to complete
graceExtraTimeFraction: 0.3,
}) * 1000;

const ttd = getEstimatedTTD({
genesisDelay: genesisSlotsDelay,
genesisDelaySeconds,
bellatrixForkEpoch: bellatrixForkEpoch,
secondsPerSlot: SIM_TESTS_SECONDS_PER_SLOT,
cliqueSealingPeriod: CLIQUE_SEALING_PERIOD,
Expand All @@ -45,7 +45,7 @@ const env = await SimulationEnvironment.initWithDefaults(
chainConfig: {
ALTAIR_FORK_EPOCH: altairForkEpoch,
BELLATRIX_FORK_EPOCH: bellatrixForkEpoch,
GENESIS_DELAY: genesisSlotsDelay,
GENESIS_DELAY: genesisDelaySeconds,
TERMINAL_TOTAL_DIFFICULTY: ttd,
},
},
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/test/sim/deneb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {SimulationEnvironment} from "../utils/simulation/SimulationEnvironment.j
import {getEstimatedTimeInSecForRun, getEstimatedTTD, logFilesDir} from "../utils/simulation/utils/index.js";
import {connectAllNodes, connectNewNode, waitForNodeSync, waitForSlot} from "../utils/simulation/utils/network.js";

const genesisSlotsDelay = 20;
const genesisDelaySeconds = 20 * SIM_TESTS_SECONDS_PER_SLOT;
const altairForkEpoch = 2;
const bellatrixForkEpoch = 4;
// Make sure bellatrix started before TTD reach
Expand All @@ -20,15 +20,15 @@ const syncWaitEpoch = 2;

const runTimeoutMs =
getEstimatedTimeInSecForRun({
genesisSlotDelay: genesisSlotsDelay,
genesisDelaySeconds,
secondsPerSlot: SIM_TESTS_SECONDS_PER_SLOT,
runTill: runTillEpoch + syncWaitEpoch,
// After adding Nethermind its took longer to complete
graceExtraTimeFraction: 0.3,
}) * 1000;

const ttd = getEstimatedTTD({
genesisDelay: genesisSlotsDelay,
genesisDelaySeconds,
bellatrixForkEpoch: bellatrixForkEpoch,
secondsPerSlot: SIM_TESTS_SECONDS_PER_SLOT,
cliqueSealingPeriod: CLIQUE_SEALING_PERIOD,
Expand All @@ -42,7 +42,7 @@ const env = await SimulationEnvironment.initWithDefaults(
chainConfig: {
ALTAIR_FORK_EPOCH: altairForkEpoch,
BELLATRIX_FORK_EPOCH: bellatrixForkEpoch,
GENESIS_DELAY: genesisSlotsDelay,
GENESIS_DELAY: genesisDelaySeconds,
TERMINAL_TOTAL_DIFFICULTY: ttd,
},
},
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/test/sim/endpoints.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import {getEstimatedTimeInSecForRun, logFilesDir} from "../utils/simulation/util
import {waitForSlot} from "../utils/simulation/utils/network.js";
import {SIM_TESTS_SECONDS_PER_SLOT} from "../utils/simulation/constants.js";

const genesisSlotsDelay = 10;
const genesisDelaySeconds = 10 * SIM_TESTS_SECONDS_PER_SLOT;
const altairForkEpoch = 2;
const bellatrixForkEpoch = 4;
const validatorCount = 2;
const runTimeoutMs =
getEstimatedTimeInSecForRun({
genesisSlotDelay: genesisSlotsDelay,
genesisDelaySeconds,
secondsPerSlot: SIM_TESTS_SECONDS_PER_SLOT,
runTill: 2,
// After adding Nethermind its took longer to complete
Expand All @@ -30,7 +30,7 @@ const env = await SimulationEnvironment.initWithDefaults(
chainConfig: {
ALTAIR_FORK_EPOCH: altairForkEpoch,
BELLATRIX_FORK_EPOCH: bellatrixForkEpoch,
GENESIS_DELAY: genesisSlotsDelay,
GENESIS_DELAY: genesisDelaySeconds,
},
},
[
Expand Down
32 changes: 20 additions & 12 deletions packages/cli/test/sim/multi_fork.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/naming-convention */
import path from "node:path";
import {activePreset} from "@lodestar/params";
import {sleep, toHexString} from "@lodestar/utils";
import {ApiError} from "@lodestar/api";
import {CLIQUE_SEALING_PERIOD, SIM_TESTS_SECONDS_PER_SLOT} from "../utils/simulation/constants.js";
Expand All @@ -16,27 +15,29 @@ import {
} from "../utils/simulation/utils/network.js";
import {nodeAssertion} from "../utils/simulation/assertions/nodeAssertion.js";
import {mergeAssertion} from "../utils/simulation/assertions/mergeAssertion.js";
import {createForkAssertion} from "../utils/simulation/assertions/forkAssertion.js";

const genesisSlotsDelay = 20;
const genesisDelaySeconds = 20 * SIM_TESTS_SECONDS_PER_SLOT;
const altairForkEpoch = 2;
const bellatrixForkEpoch = 4;
const capellaForkEpoch = 6;
// Make sure bellatrix started before TTD reach
const additionalSlotsForTTD = activePreset.SLOTS_PER_EPOCH - 2;
const runTillEpoch = 6;
const additionalSlotsForTTD = 2;
const runTillEpoch = 8;
const syncWaitEpoch = 2;

const runTimeoutMs =
getEstimatedTimeInSecForRun({
genesisSlotDelay: genesisSlotsDelay,
genesisDelaySeconds,
secondsPerSlot: SIM_TESTS_SECONDS_PER_SLOT,
runTill: runTillEpoch + syncWaitEpoch,
// After adding Nethermind its took longer to complete
graceExtraTimeFraction: 0.3,
}) * 1000;

const ttd = getEstimatedTTD({
genesisDelay: genesisSlotsDelay,
bellatrixForkEpoch: bellatrixForkEpoch,
genesisDelaySeconds,
bellatrixForkEpoch,
secondsPerSlot: SIM_TESTS_SECONDS_PER_SLOT,
cliqueSealingPeriod: CLIQUE_SEALING_PERIOD,
additionalSlots: additionalSlotsForTTD,
Expand All @@ -49,7 +50,8 @@ const env = await SimulationEnvironment.initWithDefaults(
chainConfig: {
ALTAIR_FORK_EPOCH: altairForkEpoch,
BELLATRIX_FORK_EPOCH: bellatrixForkEpoch,
GENESIS_DELAY: genesisSlotsDelay,
CAPELLA_FORK_EPOCH: capellaForkEpoch,
GENESIS_DELAY: genesisDelaySeconds,
TERMINAL_TOTAL_DIFFICULTY: ttd,
},
},
Expand Down Expand Up @@ -79,10 +81,16 @@ env.tracker.register({
await env.start({runTimeoutMs});
await connectAllNodes(env.nodes);

// The `TTD` will be reach around `start of bellatrixForkEpoch + additionalSlotsForMerge` slot
// We wait for the end of that epoch with half more epoch to make sure merge transition is complete
await waitForSlot(env.clock.getLastSlotOfEpoch(bellatrixForkEpoch) + activePreset.SLOTS_PER_EPOCH / 2, env.nodes, {
silent: true,
let lastForkEpoch = 0;
// Go through every fork and check which one is active and register assertion for it
// This will make sure this test would identify if we add new fork or activate one of the existing ones
for (const fork of env.forkConfig.forksAscendingEpochOrder) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a nice comment above this code explaining the purpose of this and motivation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if (!Number.isInteger(fork.epoch)) continue;
lastForkEpoch = fork.epoch;
env.tracker.register(createForkAssertion(fork.name, fork.epoch));
}

await waitForSlot(env.clock.getLastSlotOfEpoch(lastForkEpoch + 1), env.nodes, {
env,
});

Expand Down
61 changes: 37 additions & 24 deletions packages/cli/test/utils/simulation/SimulationEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
CLNode,
ELClient,
ELGeneratorClientOptions,
ELGeneratorGenesisOptions,
ELNode,
ELStartMode,
IRunner,
Expand All @@ -42,7 +43,13 @@ import {
SimulationOptions,
} from "./interfaces.js";
import {SimulationTracker} from "./SimulationTracker.js";
import {getEstimatedTTD, makeUniqueArray, regsiterProcessHandler, replaceIpFromUrl} from "./utils/index.js";
import {
getEstimatedShanghaiTime,
getEstimatedTTD,
makeUniqueArray,
registerProcessHandler,
replaceIpFromUrl,
} from "./utils/index.js";
import {generateLighthouseBeaconNode} from "./cl_clients/lighthouse.js";
import {Runner} from "./runner/index.js";
import {createKeystores} from "./utils/keys.js";
Expand Down Expand Up @@ -75,7 +82,7 @@ export class SimulationEnvironment {
this.options = options;

this.clock = new EpochClock({
genesisTime: this.options.genesisTime,
genesisTime: this.options.eth1GenesisTime + this.forkConfig.GENESIS_DELAY,
secondsPerSlot: this.forkConfig.SECONDS_PER_SLOT,
slotsPerEpoch: activePreset.SLOTS_PER_EPOCH,
signal: this.options.controller.signal,
Expand All @@ -97,11 +104,11 @@ export class SimulationEnvironment {
clients: NodePairOptions[]
): Promise<SimulationEnvironment> {
const secondsPerSlot = chainConfig.SECONDS_PER_SLOT ?? SIM_TESTS_SECONDS_PER_SLOT;
const genesisTime = Math.floor(Date.now() / 1000) + chainConfig.GENESIS_DELAY * secondsPerSlot;
const genesisTime = Math.floor(Date.now() / 1000);
const ttd =
chainConfig.TERMINAL_TOTAL_DIFFICULTY ??
getEstimatedTTD({
genesisDelay: chainConfig.GENESIS_DELAY,
genesisDelaySeconds: chainConfig.GENESIS_DELAY,
bellatrixForkEpoch: chainConfig.BELLATRIX_FORK_EPOCH,
secondsPerSlot: secondsPerSlot,
cliqueSealingPeriod: CLIQUE_SEALING_PERIOD,
Expand All @@ -115,12 +122,14 @@ export class SimulationEnvironment {
TERMINAL_TOTAL_DIFFICULTY: ttd,
DEPOSIT_CHAIN_ID: SIM_ENV_CHAIN_ID,
DEPOSIT_NETWORK_ID: SIM_ENV_NETWORK_ID,
SECONDS_PER_ETH1_BLOCK: CLIQUE_SEALING_PERIOD,
ETH1_FOLLOW_DISTANCE: 1,
});

const env = new SimulationEnvironment(forkConfig, {
logsDir,
id,
genesisTime,
eth1GenesisTime: genesisTime,
controller: new AbortController(),
rootDir: path.join(tmp.dirSync({unsafeCleanup: true, tmpdir: "/tmp", template: "sim-XXXXXX"}).name, id),
});
Expand Down Expand Up @@ -157,7 +166,7 @@ export class SimulationEnvironment {
}, msToGenesis);

try {
regsiterProcessHandler(this);
registerProcessHandler(this);
if (!fs.existsSync(this.options.rootDir)) {
await mkdir(this.options.rootDir);
}
Expand Down Expand Up @@ -203,12 +212,12 @@ export class SimulationEnvironment {
process.removeAllListeners("SIGTERM");
process.removeAllListeners("SIGINT");
console.log(`Simulation environment "${this.options.id}" is stopping: ${message}`);
this.options.controller.abort();
await this.tracker.stop();
await Promise.all(this.nodes.map((node) => node.el.job.stop()));
await Promise.all(this.nodes.map((node) => node.cl.job.stop()));
await this.externalSigner.stop();
await this.runner.stop();
this.options.controller.abort();

if (this.tracker.getErrorCount() > 0) {
this.tracker.reporter.summary();
Expand Down Expand Up @@ -299,7 +308,7 @@ export class SimulationEnvironment {
paths: clPaths,
nodeIndex: options.nodeIndex,
keys: options?.keys ?? {type: "no-keys"},
genesisTime: this.options.genesisTime,
genesisTime: this.options.eth1GenesisTime + this.forkConfig.GENESIS_DELAY,
engineMock: options?.engineMock ?? false,
clientOptions: options?.clientOptions ?? {},
address: "127.0.0.1",
Expand Down Expand Up @@ -357,30 +366,34 @@ export class SimulationEnvironment {

const mode =
options?.mode ?? (this.forkConfig.BELLATRIX_FORK_EPOCH > 0 ? ELStartMode.PreMerge : ELStartMode.PostMerge);

await writeFile(
elPaths.genesisFilePath,
JSON.stringify(
getGethGenesisBlock(mode, {
ttd: options?.ttd ?? this.forkConfig.TERMINAL_TOTAL_DIFFICULTY,
cliqueSealingPeriod: options?.cliqueSealingPeriod ?? CLIQUE_SEALING_PERIOD,
clientOptions: [],
})
)
);
const genesisOptions: ELGeneratorGenesisOptions<E> = {
ttd: options?.ttd ?? this.forkConfig.TERMINAL_TOTAL_DIFFICULTY,
cliqueSealingPeriod: options?.cliqueSealingPeriod ?? CLIQUE_SEALING_PERIOD,
genesisTime: options?.genesisTime ?? this.options.eth1GenesisTime,
shanghaiTime:
options?.shanghaiTime ??
getEstimatedShanghaiTime({
genesisDelaySeconds: this.forkConfig.GENESIS_DELAY,
capellaForkEpoch: this.forkConfig.CAPELLA_FORK_EPOCH,
eth1GenesisTime: this.options.eth1GenesisTime,
secondsPerSlot: this.forkConfig.SECONDS_PER_SLOT,
additionalSlots: 0,
}),
clientOptions: options.clientOptions ?? [],
};

const opts: ELGeneratorClientOptions<E> = {
...genesisOptions,
id: elId,
paths: elPaths,
mode,
nodeIndex: options.nodeIndex,
mode: options?.mode ?? (this.forkConfig.BELLATRIX_FORK_EPOCH > 0 ? ELStartMode.PreMerge : ELStartMode.PostMerge),
ttd: options?.ttd ?? this.forkConfig.TERMINAL_TOTAL_DIFFICULTY,
cliqueSealingPeriod: options?.cliqueSealingPeriod ?? CLIQUE_SEALING_PERIOD,
address: this.runner.getNextIp(),
mining: options?.mining ?? false,
clientOptions: options.clientOptions ?? [],
};

await writeFile(elPaths.genesisFilePath, JSON.stringify(getGethGenesisBlock(mode, genesisOptions)));

switch (client) {
case ELClient.Mock: {
return generateMockNode(opts as ELGeneratorClientOptions<ELClient.Mock>, this.runner);
Expand Down Expand Up @@ -409,7 +422,7 @@ export class SimulationEnvironment {
}

const genesisState = nodeUtils.initDevState(this.forkConfig, this.keysCount, {
genesisTime: this.options.genesisTime,
genesisTime: this.options.eth1GenesisTime + this.forkConfig.GENESIS_DELAY,
eth1BlockHash: fromHexString(eth1Genesis.hash),
}).state;

Expand Down
37 changes: 37 additions & 0 deletions packages/cli/test/utils/simulation/assertions/forkAssertion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {ApiError} from "@lodestar/api";
import {ForkName} from "@lodestar/params";
import {Epoch} from "@lodestar/types";
import {toHexString} from "@lodestar/utils";
import {SimulationAssertion} from "../interfaces.js";

export function createForkAssertion(fork: ForkName, epoch: Epoch): SimulationAssertion<string, string> {
return {
id: `fork-${fork}`,
match: ({slot, clock}) => {
return slot === clock.getFirstSlotOfEpoch(epoch) ? {match: true, remove: true} : false;
},
assert: async ({nodes, slot, forkConfig}) => {
const errors: string[] = [];
for (const node of nodes) {
const res = await node.cl.api.debug.getStateV2("head");
ApiError.assert(res);
const expectedForkVersion = toHexString(forkConfig.getForkInfo(slot).version);
const currentForkVersion = toHexString(res.response.data.fork.currentVersion);

if (expectedForkVersion !== currentForkVersion) {
errors.push(
`Node is not on correct fork. ${JSON.stringify({
id: node.cl.id,
slot,
fork,
expectedForkVersion,
currentForkVersion,
})}`
);
}
}

return errors;
},
};
}
2 changes: 1 addition & 1 deletion packages/cli/test/utils/simulation/el_clients/geth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export const generateGethNode: ELClientGenerator<ELClient.Geth> = (opts, runner)
// Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail
"--verbosity",
"5",
...(mining ? ["--mine"] : []),
...(mining ? ["--mine", "--miner.etherbase", GENESIS_ACCOUNT] : []),
...(mode == ELStartMode.PreMerge ? ["--nodiscover"] : []),
...clientOptions,
],
Expand Down
Loading