Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ packages/beacon-node/mainnet_pubkeys.csv
packages/api/oapi-schemas
test-logs/

# Kurtosis test files (keep locally, don't commit)
packages/cli/test/utils/crucible/kurtosis/test/runner-test.ts

Comment on lines +45 to +47
Copy link
Contributor

Choose a reason for hiding this comment

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

We would remove this line when this PR get ready.

# Autogenerated docs
packages/**/docs
packages/**/typedocs
Expand Down
30 changes: 3 additions & 27 deletions packages/cli/test/sim/deneb.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import path from "node:path";
import {createBlobsAssertion} from "../utils/crucible/assertions/blobsAssertion.js";
import {BeaconClient, ExecutionClient, ValidatorClient} from "../utils/crucible/interfaces.js";
import {Simulation} from "../utils/crucible/simulation.js";
import {Simulation} from "../utils/crucible/kurtosis/simulation/simulation-kurtosis.js";
import {defineSimTestConfig, logFilesDir} from "../utils/crucible/utils/index.js";
import {connectAllNodes, waitForSlot} from "../utils/crucible/utils/network.js";
import {assertCheckpointSync, assertRangeSync} from "../utils/crucible/utils/syncing.js";
Expand All @@ -19,36 +18,13 @@ const {estimatedTimeoutMs, forkConfig} = defineSimTestConfig({
additionalSlotsForTTD: 0,
});

const env = await Simulation.initWithDefaults(
const env = await Simulation.initWithKurtosisConfig(
{
id: "deneb",
logsDir: path.join(logFilesDir, "deneb"),
forkConfig,
},
[
{
id: "node-1",
beacon: BeaconClient.Lodestar,
validator: {
type: ValidatorClient.Lodestar,
options: {},
},
execution: ExecutionClient.Geth,
keysCount: 32,
mining: true,
},
{
id: "node-2",
beacon: BeaconClient.Lodestar,
validator: {
type: ValidatorClient.Lodestar,
options: {},
},
execution: ExecutionClient.Geth,
keysCount: 32,
remote: true,
},
]
"deneb.yml"
Copy link
Contributor

Choose a reason for hiding this comment

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

I would suggest to keep deneb.test.ts and deneb.yml in the same directory. So as the other test files. Would be much easier to navigate and check the tests.

);

await env.start({runTimeoutMs: estimatedTimeoutMs});
Expand Down
88 changes: 11 additions & 77 deletions packages/cli/test/sim/multiFork.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import {createForkAssertion} from "../utils/crucible/assertions/forkAssertion.js
import {mergeAssertion} from "../utils/crucible/assertions/mergeAssertion.js";
import {nodeAssertion} from "../utils/crucible/assertions/nodeAssertion.js";
import {createWithdrawalAssertions} from "../utils/crucible/assertions/withdrawalsAssertion.js";
import {BeaconClient, ExecutionClient, Match, ValidatorClient} from "../utils/crucible/interfaces.js";
import {Simulation} from "../utils/crucible/simulation.js";
import {Match} from "../utils/crucible/interfaces.js";
import {Simulation} from "../utils/crucible/kurtosis/simulation/simulation-kurtosis.js";
import {defineSimTestConfig, logFilesDir} from "../utils/crucible/utils/index.js";
import {connectAllNodes, waitForSlot} from "../utils/crucible/utils/network.js";
import {assertCheckpointSync, assertRangeSync, assertUnknownBlockSync} from "../utils/crucible/utils/syncing.js";

const altairForkEpoch = 2;
const bellatrixForkEpoch = 4;
const capellaForkEpoch = 6;
const denebForkEpoch = 8;
const runTillEpoch = 10;
const altairForkEpoch = 0;
const bellatrixForkEpoch = 0;
const capellaForkEpoch = 0;
const denebForkEpoch = 0;
const runTillEpoch = 4;
const syncWaitEpoch = 2;

const {estimatedTimeoutMs, forkConfig} = defineSimTestConfig({
Expand All @@ -27,81 +27,15 @@ const {estimatedTimeoutMs, forkConfig} = defineSimTestConfig({
initialNodes: 5,
});

const env = await Simulation.initWithDefaults(

// Load configuration and create simulation (services not started yet)
const env = await Simulation.initWithKurtosisConfig(
{
id: "multi-fork",
logsDir: path.join(logFilesDir, "multi-fork"),
forkConfig,
},
[
{
id: "node-1",
beacon: BeaconClient.Lodestar,
validator: {
type: ValidatorClient.Lodestar,
options: {
// this will cause race in beacon but since builder is not attached will
// return with engine full block and publish via publishBlockV2
clientOptions: {
"builder.selection": "default",
},
},
},
execution: ExecutionClient.Geth,
keysCount: 32,
mining: true,
},
{
id: "node-2",
beacon: BeaconClient.Lodestar,
validator: {
type: ValidatorClient.Lodestar,
options: {
// this will make the beacon respond with blinded version of the local block as no
// builder is attached to beacon, and publish via publishBlindedBlockV2
clientOptions: {
"builder.selection": "default",
blindedLocal: true,
},
},
},
execution: ExecutionClient.Geth,
keysCount: 32,
remote: true,
},
{
id: "node-3",
beacon: BeaconClient.Lodestar,
validator: {
type: ValidatorClient.Lodestar,
options: {
// this builder selection will make it respond with full block
clientOptions: {
"builder.selection": "executiononly",
},
},
},
execution: ExecutionClient.Geth,
keysCount: 32,
},
{
id: "node-4",
beacon: BeaconClient.Lodestar,
validator: {
type: ValidatorClient.Lodestar,
options: {
// this builder selection will make it respond with blinded version
// of local block and subsequent publishing via publishBlindedBlockV2
clientOptions: {
"builder.selection": "default",
},
},
},
execution: ExecutionClient.Geth,
keysCount: 32,
},
{id: "node-5", beacon: BeaconClient.Lighthouse, execution: ExecutionClient.Geth, keysCount: 32},
]
"multi-fork.yml" // Kurtosis network configuration
);

env.tracker.register({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {EL_GENESIS_ACCOUNT} from "../constants.js";
import {EL_GENESIS_ACCOUNT, EL_GENESIS_SECRET_KEY} from "../constants.js";
import {Assertion, AssertionResult, Match, NodePair} from "../interfaces.js";

const transactionAmount = BigInt(2441406250);
Expand All @@ -22,13 +22,19 @@ export function createAccountBalanceAssertion({
return Match.None;
},
async capture({node}) {
await node.execution.provider?.eth.sendTransaction({
const account = await node.execution.provider?.accountProvider?.privateKeyToAccount(`0x${EL_GENESIS_SECRET_KEY}`);
if (!account) throw new Error("Can not deserialize the genesis account from key");

const tx = await account.signTransaction({
to: address,
from: EL_GENESIS_ACCOUNT,
gas: "0x76c0",
gasPrice: "0x9184e72a000",
value: transactionAmount,
});
if (!tx) throw new Error("Can not send tx from el genesis account");

await node.execution.provider?.eth.sendSignedTransaction(tx.rawTransaction);

// Capture the value transferred to account
return transactionAmount;
Expand All @@ -55,4 +61,4 @@ export function createAccountBalanceAssertion({
return errors;
},
};
}
}
75 changes: 30 additions & 45 deletions packages/cli/test/utils/crucible/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import {ChildProcess} from "node:child_process";
//import {ChildProcess} from "node:child_process";
import {SecretKey} from "@chainsafe/blst";
import {ApiClient} from "@lodestar/api";
import {ApiClient as KeyManagerApi} from "@lodestar/api/keymanager";
import {ChainForkConfig} from "@lodestar/config";
import {LogLevel, Logger} from "@lodestar/logger";
import {ForkName} from "@lodestar/params";
import {Epoch, SignedBeaconBlock, Slot} from "@lodestar/types";
import {ServiceContext} from "kurtosis-sdk";
import {Web3} from "web3";
import {BeaconArgs} from "../../../src/cmds/beacon/options.js";
import {IValidatorCliArgs} from "../../../src/cmds/validator/options.js";
import {GlobalArgs} from "../../../src/options/index.js";
import {EpochClock} from "./epochClock.js";
import { KurtosisNetworkConfig, KurtosisServicesMap } from "./kurtosis/runner/kurtosisTypes.js";

export type NodeId = string;

export type SimulationInitOptions = {
id: string;
logsDir: string;
forkConfig: ChainForkConfig;
trustedSetup?: boolean;
};

export type SimulationOptions = {
Expand All @@ -26,6 +29,7 @@ export type SimulationOptions = {
rootDir: string;
controller: AbortController;
genesisTime: number;
trustedSetup?: boolean;
logLevel?: LogLevel;
};

Expand All @@ -45,11 +49,6 @@ export enum ExecutionClient {
Nethermind = "execution-nethermind",
}

export enum ExecutionStartMode {
PreMerge = "pre-merge",
PostMerge = "post-merge",
}

export type BeaconClientsOptions = {
[BeaconClient.Lodestar]: Partial<BeaconArgs & GlobalArgs>;
[BeaconClient.Lighthouse]: Record<string, unknown>;
Expand Down Expand Up @@ -136,7 +135,7 @@ export interface ExecutionGenesisOptions<E extends ExecutionClient = ExecutionCl
export interface ExecutionGeneratorOptions<E extends ExecutionClient = ExecutionClient>
extends ExecutionGenesisOptions<E>,
GeneratorOptions {
mode: ExecutionStartMode;
//mode: ExecutionStartMode; //✅ REMOVED - ExecutionStartMode not needed anymore
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes remove that line completely.

mining: boolean;
paths: ExecutionPaths;
clientOptions: ExecutionClientsOptions[E];
Expand Down Expand Up @@ -164,52 +163,37 @@ export type LighthouseAPI = Omit<ApiClient, "lodestar"> & {
};
};

// NEW - Kurtosis-specific BeaconNode
export interface BeaconNode<C extends BeaconClient = BeaconClient> {
readonly client: C;
readonly id: string;
/**
* Beacon Node Rest API URL accessible form the host machine if the process is running in private network inside docker
*/
readonly restPublicUrl: string;
/**
* Beacon Node Rest API URL accessible within private network
*/
readonly restPrivateUrl: string;
readonly restPublicUrl: string; //🔄 From Kurtosis?
readonly restPrivateUrl: string; //🔄 From Kurtosis?
readonly api: C extends BeaconClient.Lodestar ? LodestarAPI : LighthouseAPI;
readonly job: Job;
readonly serviceContext: ServiceContext; // ✅ NEW - Kurtosis-native
}

// NEW - Kurtosis-specific ValidatorNode
export interface ValidatorNode<C extends ValidatorClient = ValidatorClient> {
readonly client: C;
readonly id: string;
readonly keyManager: KeyManagerApi;
readonly keys: ValidatorClientKeys;
readonly job: Job;
readonly serviceContext: ServiceContext; // ✅ NEW - Kurtosis-native
}

// NEW - Kurtosis-specific executionNode
export interface ExecutionNode<E extends ExecutionClient = ExecutionClient> {
readonly client: E;
readonly id: string;
readonly ttd: bigint;
/**
* Engine URL accessible form the host machine if the process is running in private network inside docker
*/
readonly engineRpcPublicUrl: string;
/**
* Engine URL accessible within private network inside docker
*/
readonly engineRpcPrivateUrl: string;
/**
* RPC URL accessible form the host machine if the process is running in private network inside docker
*/
readonly ethRpcPublicUrl: string;
/**
* RPC URL accessible within private network inside docker
*/
readonly ethRpcPrivateUrl: string;
readonly jwtSecretHex: string;
readonly engineRpcPublicUrl: string; //🔄 From Kurtosis?
readonly engineRpcPrivateUrl: string; //🔄 From Kurtosis?
readonly ethRpcPublicUrl: string; //🔄 From Kurtosis?
readonly ethRpcPrivateUrl: string; //🔄 From Kurtosis?
readonly jwtSecretHex?: string;
readonly provider: E extends ExecutionClient.Mock ? null : Web3;
readonly job: Job;
readonly serviceContext: ServiceContext; // ✅ NEW - Kurtosis-native
}

export interface NodePair {
Expand Down Expand Up @@ -287,17 +271,19 @@ export type RunnerOptions = {
};
};

//✅ New Kurtosis Runner
export interface IRunner {
create: (jobOptions: JobOptions[]) => Job;
on(event: RunnerEvent, cb: (id: string) => void | Promise<void>): void;
start(): Promise<void>;
stop(): Promise<void>;
getNextIp(): string;
}
// Takes a structured config and instantiates the network
create: (config: KurtosisNetworkConfig) => Promise<KurtosisServicesMap>; // ✅ NEW - Kurtosis-native

// Starts the environment (e.g., enclave)
start: (enclaveName: string) => Promise<void>; // ✅ NEW - Kurtosis-native

export interface RunnerEnv<T extends RunnerType> {
type: T;
create: (jobOption: Omit<JobOptions<T>, "children">) => Job;
// Stops or tears down the environment
stop: () => Promise<void>;

// Attach listeners for events, such as service start, stop, crash, etc.
on(event: RunnerEvent, cb: (id: string) => void | Promise<void>): void;
}

export type RunnerEvent = "starting" | "started" | "stopping" | "stop";
Expand Down Expand Up @@ -383,7 +369,6 @@ export interface AssertionError {
message: string;
data?: Record<string, unknown>;
}
export type ChildProcessWithJobOptions = {jobOptions: JobOptions; childProcess: ChildProcess};

export type Eth1GenesisBlock = {
config: {
Expand Down
Loading