Skip to content

Commit

Permalink
feat: ✨ Added default fork config
Browse files Browse the repository at this point in the history
  • Loading branch information
timbrinded committed Nov 27, 2024
1 parent 9e53242 commit a580ae4
Show file tree
Hide file tree
Showing 40 changed files with 265 additions and 98 deletions.
3 changes: 1 addition & 2 deletions packages/cli/src/cmds/runTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ export async function executeTests(env: Environment, testRunArgs?: testRunArgs)
const testFileDir =
additionalArgs?.subDirectory !== undefined
? env.testFileDir.map((folder) =>
// @ts-expect-error - bug in tsc
path.join(folder, additionalArgs.subDirectory)
path.join(folder, additionalArgs.subDirectory || "error")
)
: env.testFileDir;

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/internal/cmdFunctions/initialisation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export function createConfig(options: {
testFileDir: [options.testDir],
foundation: {
type: options.foundation as any,
},
} as any,
},
],
};
Expand Down
45 changes: 40 additions & 5 deletions packages/cli/src/internal/commandParsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
import chalk from "chalk";
import path from "node:path";
import { standardRepos } from "../lib/repoDefinitions";
import invariant from "tiny-invariant";

export function parseZombieCmd(launchSpec: ZombieLaunchSpec) {
if (launchSpec) {
Expand Down Expand Up @@ -45,27 +46,61 @@ export async function parseRunCmd(launchSpec: DevLaunchSpec, additionalRepos?: R
? [...launchSpec.options]
: fetchDefaultArgs(path.basename(launchSpec.binPath), additionalRepos);

const overrideArg = (newArg: string) => {
const newArgKey = newArg.split("=")[0];
const existingIndex = args.findIndex((arg) => arg.startsWith(`${newArgKey}=`));

if (existingIndex !== -1) {
args[existingIndex] = newArg;
} else {
args.push(newArg);
}
};

if (launchSpec.ports) {
const ports = launchSpec.ports;
if (ports.p2pPort) {
args.push(`--port=${ports.p2pPort}`);
overrideArg(`--port=${ports.p2pPort}`);
}
if (ports.wsPort) {
args.push(`--ws-port=${ports.wsPort}`);
overrideArg(`--ws-port=${ports.wsPort}`);
}
if (ports.rpcPort) {
args.push(`--rpc-port=${ports.rpcPort}`);
overrideArg(`--rpc-port=${ports.rpcPort}`);
}
} else {
const freePort = (await getFreePort()).toString();
process.env.MOONWALL_RPC_PORT = freePort;

if (launchSpec.newRpcBehaviour) {
args.push(`--rpc-port=${freePort}`);
overrideArg(`--rpc-port=${freePort}`);
} else {
args.push(`--ws-port=${freePort}`);
overrideArg(`--ws-port=${freePort}`);
}
}

const forkOptions = launchSpec.defaultForkConfig;

console.log("forkOptions");
console.dir(forkOptions, { depth: null });
if (forkOptions) {
if (forkOptions.url) {
invariant(forkOptions.url.startsWith("http"), "Fork URL must start with http:// or https://");
overrideArg(`--fork-chain-from-rpc=${forkOptions.url}`);
}
if (forkOptions.blockNumber) {
overrideArg(`--block=${forkOptions.blockNumber}`);
}
if (forkOptions.stateOverridePath) {
overrideArg(`--fork-state-overrides=${forkOptions.stateOverridePath}`);
}
if (forkOptions.verbose) {
overrideArg("-llazy-loading=trace");
}
}

console.log("post args");
console.log(args);
return { cmd, args, launch };
}

Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/internal/localNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export async function launchNode(cmd: string, args: string[], name: string) {
checkAccess(cmd);
}

console.log("REMOVE ME")
console.log(cmd)
console.log(args)

const port = args.find((a) => a.includes("port"))?.split("=")[1];
debug(`\x1b[36mStarting ${name} node on port ${port}...\x1b[0m`);

Expand Down
61 changes: 27 additions & 34 deletions packages/cli/src/lib/globalContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
import { type ChildProcess, exec, execSync } from "node:child_process";
import { promisify } from "node:util";
import { withTimeout } from "../internal";
import invariant from "tiny-invariant";
const debugSetup = Debug("global:context");

export class MoonwallContext {
Expand All @@ -51,26 +52,23 @@ export class MoonwallContext {
zombieNetwork?: Network;
rtUpgradePath?: string;
ipcServer?: net.Server;
injectedOptions?: object

constructor(config: MoonwallConfig) {
constructor(config: MoonwallConfig, options?: object) {
const env = config.environments.find(({ name }) => name === process.env.MOON_TEST_ENV);

if (!env) {
throw new Error(`Environment ${process.env.MOON_TEST_ENV} not found in config`);
}
invariant(env, `Environment ${process.env.MOON_TEST_ENV} not found in config`);

this.providers = [];
this.nodes = [];
this.foundation = env.foundation.type;
this.injectedOptions = options
}

public async setupFoundation() {
const config = await importAsyncConfig();
const env = config.environments.find(({ name }) => name === process.env.MOON_TEST_ENV);

if (!env) {
throw new Error(`Environment ${process.env.MOON_TEST_ENV} not found in config`);
}
invariant(env, `Environment ${process.env.MOON_TEST_ENV} not found in config`);

const foundationHandlers: Record<
FoundationType,
Expand All @@ -79,9 +77,10 @@ export class MoonwallContext {
read_only: this.handleReadOnly,
chopsticks: this.handleChopsticks,
dev: this.handleDev,
zombie: this.handleZombie,
fork: this.handleReadOnly, // TODO: Implement fork
zombie: this.handleZombie
};
console.log("RUNNING FOUNDATION HANDLER")
console.dir(env.foundation,{depth: null})

const foundationHandler = foundationHandlers[env.foundation.type];
this.environment = {
Expand All @@ -93,9 +92,7 @@ export class MoonwallContext {
}

private async handleZombie(env: Environment) {
if (env.foundation.type !== "zombie") {
throw new Error(`Foundation type must be 'zombie'`);
}
invariant(env.foundation.type === "zombie", "Foundation type must be 'zombie'");

const { cmd: zombieConfig } = await parseZombieCmd(env.foundation.zombieSpec);
this.rtUpgradePath = env.foundation.rtUpgradePath;
Expand All @@ -114,9 +111,7 @@ export class MoonwallContext {
}

private async handleDev(env: Environment, config: MoonwallConfig) {
if (env.foundation.type !== "dev") {
throw new Error(`Foundation type must be 'dev'`);
}
invariant(env.foundation.type === "dev", "Foundation type must be 'dev'");

const { cmd, args, launch } = await parseRunCmd(
env.foundation.launchSpec[0],
Expand Down Expand Up @@ -434,6 +429,9 @@ export class MoonwallContext {

const nodes = ctx.environment.nodes;

console.log("remove me")
console.dir(nodes, {depth:null})

if (this.environment.foundationType === "zombie") {
return await this.startZombieNetwork();
}
Expand Down Expand Up @@ -625,12 +623,14 @@ export class MoonwallContext {
}
}

public static async getContext(config?: MoonwallConfig, force = false): Promise<MoonwallContext> {
//TODO: Type options
public static async getContext(config?: MoonwallConfig, options?: object, force = false): Promise<MoonwallContext> {
invariant(!(options && MoonwallContext.instance), "Attempting to open a new context with overrides when context already exists");

if (!MoonwallContext.instance?.configured || force) {
if (!config) {
throw new Error("❌ Config must be provided on Global Context instantiation");
}
MoonwallContext.instance = new MoonwallContext(config);
invariant(config, "Config must be provided on Global Context instantiation");

MoonwallContext.instance = new MoonwallContext(config, options);
await MoonwallContext.instance.setupFoundation();
debugSetup(`🟢 Moonwall context "${config.label}" created`);
}
Expand All @@ -640,9 +640,7 @@ export class MoonwallContext {
public static async destroy() {
const ctx = MoonwallContext.instance;

if (!ctx) {
throw new Error("❌ No context to destroy");
}
invariant(ctx, "No context to destroy");

try {
await ctx.disconnect();
Expand All @@ -652,16 +650,10 @@ export class MoonwallContext {

while (ctx.nodes.length > 0) {
const node = ctx.nodes.pop();

if (!node) {
throw new Error("❌ No nodes to destroy");
}
invariant(node, "No node to destroy");

const pid = node.pid;

if (!pid) {
throw new Error("❌ No pid to destroy");
}
invariant(pid, "No pid to destroy");

node.kill("SIGINT");
for (;;) {
Expand Down Expand Up @@ -698,9 +690,10 @@ export class MoonwallContext {
}
}

export const contextCreator = async () => {
// TODO: Type this properly!
export const contextCreator = async (options?: object) => {
const config = await importAsyncConfig();
const ctx = await MoonwallContext.getContext(config);
const ctx = await MoonwallContext.getContext(config, options);
await runNetworkOnly();
await ctx.connectEnvironment();
return ctx;
Expand Down
8 changes: 7 additions & 1 deletion packages/cli/src/lib/runnerContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export function describeSuite<T extends FoundationType>({
minRtVersion,
chainType,
notChainType,
options,
}: ITestSuiteType<T>): void {
if (
(minRtVersion && minRtVersion > RT_VERSION) ||
Expand All @@ -82,7 +83,13 @@ export function describeSuite<T extends FoundationType>({
let ctx: MoonwallContext | null = null;

beforeAll(async () => {

const env = getEnvironmentFromConfig();
if (env.foundation.type === "dev") {
// Pass options to contextCreator if they exist
ctx = await contextCreator(options);
}

ctx = await contextCreator();
if (env.foundation.type === "read_only") {
const settings = loadParams(env.foundation.launchSpec);
Expand Down Expand Up @@ -159,7 +166,6 @@ export function describeSuite<T extends FoundationType>({
chopsticks: chopsticksHandler,
zombie: zombieHandler,
read_only: readOnlyHandler,
fork: readOnlyHandler,
};

const handler = foundationHandlers[foundationMethods];
Expand Down
35 changes: 24 additions & 11 deletions packages/types/config_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,31 @@
"description": "A launch specification object for the \"dev\" foundation type.",
"properties": {
"binPath": {
"description": "The path to the binary file.",
"description": "The path to the binary to execute",
"type": "string"
},
"defaultForkConfig": {
"description": "Default Fork options for the node (overriden by per-test fork options)",
"properties": {
"blockNumber": {
"description": "The block number to fork from (optional)",
"type": "number"
},
"stateOverridePath": {
"description": "The state override path (optional)",
"type": "string"
},
"url": {
"description": "The URL to fork from",
"type": "string"
},
"verbose": {
"description": "Turns on trace logging for LazyLoading service (optional)",
"type": "boolean"
}
},
"type": "object"
},
"disableDefaultEthProviders": {
"description": "Determines if the default Ethereum provider connections should be disabled.\nWhen set to true, the framework will not automatically connect the Ethereum providers.\nDefault behavior (when unset or set to false) is to connect with Ethers, Viem & Web3 frameworks.\n\nNote: This also acts as a feature gate for context methods like createTxn and readPrecompile.",
"type": "boolean"
Expand All @@ -92,7 +114,7 @@
"type": "array"
},
"ports": {
"description": "An optional object with p2pPort, wsPort, and rpcPort.",
"description": "Port configuration",
"properties": {
"p2pPort": {
"description": "The port for peer-to-peer (P2P) communication.",
Expand Down Expand Up @@ -181,15 +203,6 @@
}
},
"type": "object"
},
{
"properties": {
"type": {
"const": "fork",
"type": "string"
}
},
"type": "object"
}
],
"description": "The foundation configuration for the environment. It can be of several types including \"dev\", \"chopsticks\", \"zombie\", \"read_only\", or \"fork\"."
Expand Down
Loading

0 comments on commit a580ae4

Please sign in to comment.