# EPF6 - Week 10 ## Overview - Attended core dev call and shared updates - Focused on refining the design and integration of `initWithKurtosisConfig` in Simulation - Progressed on phasing out the Docker-based Runner and aligning the new `KurtosisSDKRunner` with existing simulation/trackers - Worked through questions on how the advanced runner should expose services to the assertion framework - Iterated on updated `interface.ts` - Main focus: designing how to replace Docker-based NodePair creation with Kurtosis-based equivalents ## Main points #### Simulation.ts changes - Drafted a new workflow after `initWithKurtosisConfig`() - Proposed call chain: - `createNodePairsFromKurtosis()` → iterates over participants in the config - `createNodePairFromKurtosis()` → builds one NodePair at a time - `createBeacon/Execution/ValidatorNodeFromKurtosis()` → instantiate role-specific clients - **createNodePairsFromKurtosis()** will act as Kurtosis version of [createNodePair()](https://github.com/ChainSafe/lodestar/blob/01f7ef8da440ea43e25f933b7b977e3c96787180/packages/cli/test/utils/crucible/simulation.ts#L226). Proposed function signature: ```typescript private async createNodePairsFromKurtosis( services: KurtosisServicesMap, config: KurtosisNetworkConfig ): Promise<NodePair[]> ``` Inside will call *createNodePairFromKurtosis()* - **createNodePairFromKurtosis()** proposed signature: ```typescript private async createNodePairFromKurtosis( nodeId: string, services: KurtosisServicesMap, config: KurtosisNetworkConfig, nodeIndex: number ): Promise<NodePair> ``` Emulating the current node creation, it will return role-specific clients: ```typescript return { id: nodeId, beacon: await this.createBeaconNodeFromKurtosis(beaconService, config, nodeIndex, forkConfig), execution: await this.createExecutionNodeFromKurtosis(executionService, config, nodeIndex, forkConfig), validator: validatorService ? await this.createValidatorNodeFromKurtosis(validatorService, config, nodeIndex, forkConfig) : undefined, }; ``` - **createBeacon/Execution/ValidatorNodeFromKurtosis()** proposed signature, they emulate [createBeaconNode()](https://github.com/ChainSafe/lodestar/blob/01f7ef8da440ea43e25f933b7b977e3c96787180/packages/cli/test/utils/crucible/clients/beacon/index.ts#L10), [createValidatorNode()](https://github.com/ChainSafe/lodestar/blob/01f7ef8da440ea43e25f933b7b977e3c96787180/packages/cli/test/utils/crucible/clients/validator/index.ts#L10) and [createExecutionNode()](https://github.com/ChainSafe/lodestar/blob/01f7ef8da440ea43e25f933b7b977e3c96787180/packages/cli/test/utils/crucible/clients/execution/index.ts#L18): ```typescript private async createBeaconNodeFromKurtosis( beaconService: NodeService, config: KurtosisNetworkConfig, nodeIndex: number, forkConfig: ChainForkConfig ): Promise<BeaconNode> ``` - The goal is to mirror the old logic but replaces Docker with Kurtosis service contexts and URLs #### NodePair creation - As assertions expect the same `NodePair` structure as before, work is now on replicating the NodePair object shape using Kurtosis’ `NodeService` and `KurtosisServicesMap`. The current NodePair is It returns an object that represents a complete Ethereum node setup with beacon, execution, and optionally validator clients - The hypotesis is that `ServiceContext` replaces [Job](https://github.com/ChainSafe/lodestar/blob/01f7ef8da440ea43e25f933b7b977e3c96787180/packages/cli/test/utils/crucible/interfaces.ts#L271) and `JobOption` for lifecycle and health checks. Beacon/Validator/Execution Node **refactoring proposal**: - **BeaconNode -> BeaconNodeKurtosis** From: ```typescript 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 api: C extends BeaconClient.Lodestar ? LodestarAPI : LighthouseAPI; readonly job: Job; // ❌ Removed - Docker } ``` To: ```typescript export interface BeaconNodeKurtosis<C extends BeaconClient = BeaconClient> { readonly client: C; readonly id: string; readonly restPublicUrl: string; //Kurtosis-generated? readonly restPrivateUrl: string; //Kurtosis-generated? readonly api: C extends BeaconClient.Lodestar ? LodestarAPI : LighthouseAPI; } readonly serviceContext: ServiceContext; // ✅ Kurtosis ``` - **ValidatorNode -> ValidatorNodeKurtosis** From: ```typescript export interface ValidatorNode<C extends ValidatorClient = ValidatorClient> { readonly client: C; readonly id: string; readonly keyManager: KeyManagerApi; readonly keys: ValidatorClientKeys; readonly job: Job; // ❌ Removed - Docker } ``` To: ```typescript export interface ValidatorNodeKurtosis<C extends ValidatorClient = ValidatorClient> { readonly client: C; readonly id: string; readonly keyManager: KeyManagerApi; readonly keys: ValidatorClientKeys; readonly serviceContext: ServiceContext; // ✅ Kurtosis } ``` - **Execution Node** From: ```typescript 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; //MODIFY - Use Kurtosis public URLs /** * Engine URL accessible within private network inside docker */ readonly engineRpcPrivateUrl: string; //REMOVE - Docker-specific (??) /** * RPC URL accessible form the host machine if the process is running in private network inside docker */ readonly ethRpcPublicUrl: string; //MODIFY - Use Kurtosis public URLs /** * RPC URL accessible within private network inside docker */ readonly ethRpcPrivateUrl: string; //MODIFY - Use Kurtosis private URLs readonly jwtSecretHex: string; readonly provider: E extends ExecutionClient.Mock ? null : Web3; readonly job: Job; // ❌ Removed - Docker } ``` To: ```typescript export interface ExecutionNode<E extends ExecutionClient = ExecutionClient> { readonly client: E; readonly id: string; readonly ttd: bigint; readonly engineRpcPublicUrl: string; readonly engineRpcPrivateUrl: string; readonly ethRpcPublicUrl: string; readonly ethRpcPrivateUrl: string; readonly jwtSecretHex: string; readonly provider: E extends ExecutionClient.Mock ? null : Web3; readonly serviceContext: ServiceContext; // ✅ Kurtosis } ``` Example of Kurtosis services output: ``` Service: cl-1-lodestar-geth 🔹 Basic Properties: ID: cl-1-lodestar-geth Beacon API URL: http://localhost:53604 Roles: {"beacon":true,"validator":false,"execution":false} Metadata: {} 🔹 ServiceContext Details: Container Details: Image: chainsafe/lodestar:latest Status: 1 Entrypoint: node ./packages/cli/bin/lodestar Command: beacon --logLevel=info --port=9000 --discoveryPort=9000 --dataDir=/data/lodestar/beacon-data --chain.persistInvalidSszObjects=true --eth1.depositContractDeployBlock=0 --network.connectToDiscv5Bootnodes=true --discv5=true --eth1=true --eth1.providerUrls=http://172.16.0.11:8545 --execution.urls=http://172.16.0.11:8551 --rest=true --rest.address=0.0.0.0 --rest.namespace=* --rest.port=4000 --nat=true --jwt-secret=/jwt/jwtsecret --enr.ip=172.16.0.13 --enr.tcp=9000 --enr.udp=9000 --metrics --metrics.address=0.0.0.0 --metrics.port=8008 --paramsFile=/network-configs/config.yaml --genesisStateFile=/network-configs/genesis.ssz Environment Variables: NODE_OPTIONS=--max-old-space-size NODE_VERSION=22.16.0 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin YARN_VERSION=1.22.22 Arguments: Network: --port=9000 Metrics: --metrics --metrics.address=0.0.0.0 --metrics.port=8008 Other: --logLevel=info --discoveryPort=9000 --dataDir=/data/lodestar/beacon-data --chain.persistInvalidSszObjects=true --eth1.depositContractDeployBlock=0 --network.connectToDiscv5Bootnodes=true --discv5=true --eth1=true --eth1.providerUrls=http://172.16.0.11:8545 --execution.urls=http://172.16.0.11:8551 --rest=true --rest.address=0.0.0.0 --rest.namespace=* --rest.port=4000 --nat=true --jwt-secret=/jwt/jwtsecret --enr.ip=172.16.0.13 --enr.tcp=9000 --enr.udp=9000 --paramsFile=/network-configs/config.yaml --genesisStateFile=/network-configs/genesis.ssz Public Ports: http:53604, metrics:53605, tcp-discovery:53606, udp-discovery:54078 Private Ports: http:4000, metrics:8008, tcp-discovery:9000, udp-discovery:9000 🔹 Legacy Properties: Beacon API: http://localhost:53604 EL RPC: undefined Engine API: undefined Metrics: undefined Validator API: undefined ``` #### General refactors and open questions - Does Kurtosis services expose enough metadata to attempt **NodePair** reconstruction? ## Learnings & Outcomes - Migration affects only infrastructure; assertions are infrastructure-agnostic - First draft tested a role-aware mapping (beacon, execution, validator), it requires a nomenclatural standardization to ensure semantic consistency with NodePair convention - Still exploratory: `NodePair` recreation is feasible in draft form, but final viability depends on service mapping conventions and assertion compatibility ## Week 11 TODOs - Collect feedback from the team on the current logic and approach before proceeding further - Investigate how to fully replace `Job` with `ServiceContext`, or decide if a compatibility layer is needed ### Useful resources checked