moonwall 1.0.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +681 -0
- package/README.md +54 -0
- package/config_schema.json +811 -0
- package/dist/api/constants/accounts.d.ts +36 -0
- package/dist/api/constants/accounts.d.ts.map +1 -0
- package/dist/api/constants/accounts.js +67 -0
- package/dist/api/constants/chain.d.ts +134 -0
- package/dist/api/constants/chain.d.ts.map +1 -0
- package/dist/api/constants/chain.js +149 -0
- package/dist/api/constants/index.d.ts +4 -0
- package/dist/api/constants/index.d.ts.map +1 -0
- package/dist/api/constants/index.js +3 -0
- package/dist/api/constants/smartContract.d.ts +29 -0
- package/dist/api/constants/smartContract.d.ts.map +1 -0
- package/dist/api/constants/smartContract.js +118 -0
- package/dist/api/testing/blocks.d.ts +59 -0
- package/dist/api/testing/blocks.d.ts.map +1 -0
- package/dist/api/testing/blocks.js +147 -0
- package/dist/api/testing/contracts.d.ts +5 -0
- package/dist/api/testing/contracts.d.ts.map +1 -0
- package/dist/api/testing/contracts.js +32 -0
- package/dist/api/testing/ethers.d.ts +3 -0
- package/dist/api/testing/ethers.d.ts.map +1 -0
- package/dist/api/testing/ethers.js +38 -0
- package/dist/api/testing/events.d.ts +12 -0
- package/dist/api/testing/events.d.ts.map +1 -0
- package/dist/api/testing/events.js +23 -0
- package/dist/api/testing/extrinsics.d.ts +5 -0
- package/dist/api/testing/extrinsics.d.ts.map +1 -0
- package/dist/api/testing/extrinsics.js +10 -0
- package/dist/api/testing/index.d.ts +9 -0
- package/dist/api/testing/index.d.ts.map +1 -0
- package/dist/api/testing/index.js +8 -0
- package/dist/api/testing/jumping.d.ts +8 -0
- package/dist/api/testing/jumping.d.ts.map +1 -0
- package/dist/api/testing/jumping.js +78 -0
- package/dist/api/testing/providers.d.ts +18 -0
- package/dist/api/testing/providers.d.ts.map +1 -0
- package/dist/api/testing/providers.js +34 -0
- package/dist/api/testing/viem.d.ts +139 -0
- package/dist/api/testing/viem.d.ts.map +1 -0
- package/dist/api/testing/viem.js +247 -0
- package/dist/api/types/config.d.ts +609 -0
- package/dist/api/types/config.d.ts.map +1 -0
- package/dist/api/types/config.js +1 -0
- package/dist/api/types/context.d.ts +125 -0
- package/dist/api/types/context.d.ts.map +1 -0
- package/dist/api/types/context.js +1 -0
- package/dist/api/types/contracts.d.ts +66 -0
- package/dist/api/types/contracts.d.ts.map +1 -0
- package/dist/api/types/contracts.js +1 -0
- package/dist/api/types/eth.d.ts +3 -0
- package/dist/api/types/eth.d.ts.map +1 -0
- package/dist/api/types/eth.js +1 -0
- package/dist/api/types/foundations.d.ts +11 -0
- package/dist/api/types/foundations.d.ts.map +1 -0
- package/dist/api/types/foundations.js +1 -0
- package/dist/api/types/helpers.d.ts +7 -0
- package/dist/api/types/helpers.d.ts.map +1 -0
- package/dist/api/types/helpers.js +1 -0
- package/dist/api/types/index.d.ts +8 -0
- package/dist/api/types/index.d.ts.map +1 -0
- package/dist/api/types/index.js +7 -0
- package/dist/api/types/runner.d.ts +490 -0
- package/dist/api/types/runner.d.ts.map +1 -0
- package/dist/api/types/runner.js +1 -0
- package/dist/cli/cmds/components/LogViewer.d.ts +17 -0
- package/dist/cli/cmds/components/LogViewer.d.ts.map +1 -0
- package/dist/cli/cmds/components/LogViewer.js +171 -0
- package/dist/cli/cmds/entrypoint.d.ts +6 -0
- package/dist/cli/cmds/entrypoint.d.ts.map +1 -0
- package/dist/cli/cmds/entrypoint.js +192 -0
- package/dist/cli/cmds/interactiveCmds/chopsticksIntCmds.d.ts +2 -0
- package/dist/cli/cmds/interactiveCmds/chopsticksIntCmds.d.ts.map +1 -0
- package/dist/cli/cmds/interactiveCmds/chopsticksIntCmds.js +117 -0
- package/dist/cli/cmds/interactiveCmds/devIntCmds.d.ts +2 -0
- package/dist/cli/cmds/interactiveCmds/devIntCmds.d.ts.map +1 -0
- package/dist/cli/cmds/interactiveCmds/devIntCmds.js +103 -0
- package/dist/cli/cmds/interactiveCmds/index.d.ts +4 -0
- package/dist/cli/cmds/interactiveCmds/index.d.ts.map +1 -0
- package/dist/cli/cmds/interactiveCmds/index.js +3 -0
- package/dist/cli/cmds/interactiveCmds/zombieIntCmds.d.ts +2 -0
- package/dist/cli/cmds/interactiveCmds/zombieIntCmds.d.ts.map +1 -0
- package/dist/cli/cmds/interactiveCmds/zombieIntCmds.js +32 -0
- package/dist/cli/cmds/main.d.ts +2 -0
- package/dist/cli/cmds/main.d.ts.map +1 -0
- package/dist/cli/cmds/main.js +336 -0
- package/dist/cli/cmds/runNetwork.d.ts +3 -0
- package/dist/cli/cmds/runNetwork.d.ts.map +1 -0
- package/dist/cli/cmds/runNetwork.js +292 -0
- package/dist/cli/cmds/runTests.d.ts +12 -0
- package/dist/cli/cmds/runTests.d.ts.map +1 -0
- package/dist/cli/cmds/runTests.js +257 -0
- package/dist/cli/internal/cmdFunctions/downloader.d.ts +4 -0
- package/dist/cli/internal/cmdFunctions/downloader.d.ts.map +1 -0
- package/dist/cli/internal/cmdFunctions/downloader.js +49 -0
- package/dist/cli/internal/cmdFunctions/fetchArtifact.d.ts +10 -0
- package/dist/cli/internal/cmdFunctions/fetchArtifact.d.ts.map +1 -0
- package/dist/cli/internal/cmdFunctions/fetchArtifact.js +145 -0
- package/dist/cli/internal/cmdFunctions/index.d.ts +5 -0
- package/dist/cli/internal/cmdFunctions/index.d.ts.map +1 -0
- package/dist/cli/internal/cmdFunctions/index.js +4 -0
- package/dist/cli/internal/cmdFunctions/initialisation.d.ts +20 -0
- package/dist/cli/internal/cmdFunctions/initialisation.d.ts.map +1 -0
- package/dist/cli/internal/cmdFunctions/initialisation.js +150 -0
- package/dist/cli/internal/cmdFunctions/tempLogs.d.ts +3 -0
- package/dist/cli/internal/cmdFunctions/tempLogs.d.ts.map +1 -0
- package/dist/cli/internal/cmdFunctions/tempLogs.js +37 -0
- package/dist/cli/internal/commandParsers.d.ts +59 -0
- package/dist/cli/internal/commandParsers.d.ts.map +1 -0
- package/dist/cli/internal/commandParsers.js +305 -0
- package/dist/cli/internal/deriveTestIds.d.ts +8 -0
- package/dist/cli/internal/deriveTestIds.d.ts.map +1 -0
- package/dist/cli/internal/deriveTestIds.js +123 -0
- package/dist/cli/internal/effect/index.d.ts +6 -0
- package/dist/cli/internal/effect/index.d.ts.map +1 -0
- package/dist/cli/internal/effect/index.js +5 -0
- package/dist/cli/internal/fileCheckers.d.ts +11 -0
- package/dist/cli/internal/fileCheckers.d.ts.map +1 -0
- package/dist/cli/internal/fileCheckers.js +165 -0
- package/dist/cli/internal/foundations/index.d.ts +4 -0
- package/dist/cli/internal/foundations/index.d.ts.map +1 -0
- package/dist/cli/internal/foundations/index.js +4 -0
- package/dist/cli/internal/index.d.ts +12 -0
- package/dist/cli/internal/index.d.ts.map +1 -0
- package/dist/cli/internal/index.js +11 -0
- package/dist/cli/internal/launcherCommon.d.ts +4 -0
- package/dist/cli/internal/launcherCommon.d.ts.map +1 -0
- package/dist/cli/internal/launcherCommon.js +130 -0
- package/dist/cli/internal/localNode.d.ts +16 -0
- package/dist/cli/internal/localNode.d.ts.map +1 -0
- package/dist/cli/internal/localNode.js +362 -0
- package/dist/cli/internal/logging.d.ts +2 -0
- package/dist/cli/internal/logging.d.ts.map +1 -0
- package/dist/cli/internal/logging.js +26 -0
- package/dist/cli/internal/node.d.ts +33 -0
- package/dist/cli/internal/node.d.ts.map +1 -0
- package/dist/cli/internal/node.js +228 -0
- package/dist/cli/internal/processHelpers.d.ts +17 -0
- package/dist/cli/internal/processHelpers.d.ts.map +1 -0
- package/dist/cli/internal/processHelpers.js +56 -0
- package/dist/cli/internal/providerFactories.d.ts +48 -0
- package/dist/cli/internal/providerFactories.d.ts.map +1 -0
- package/dist/cli/internal/providerFactories.js +442 -0
- package/dist/cli/internal/testIdParser.d.ts +34 -0
- package/dist/cli/internal/testIdParser.d.ts.map +1 -0
- package/dist/cli/internal/testIdParser.js +167 -0
- package/dist/cli/lib/binariesHelpers.d.ts +15 -0
- package/dist/cli/lib/binariesHelpers.d.ts.map +1 -0
- package/dist/cli/lib/binariesHelpers.js +99 -0
- package/dist/cli/lib/configReader.d.ts +8 -0
- package/dist/cli/lib/configReader.d.ts.map +1 -0
- package/dist/cli/lib/configReader.js +115 -0
- package/dist/cli/lib/contractFunctions.d.ts +2 -0
- package/dist/cli/lib/contractFunctions.d.ts.map +1 -0
- package/dist/cli/lib/contractFunctions.js +2 -0
- package/dist/cli/lib/globalContext.d.ts +46 -0
- package/dist/cli/lib/globalContext.d.ts.map +1 -0
- package/dist/cli/lib/globalContext.js +710 -0
- package/dist/cli/lib/governanceProcedures.d.ts +27 -0
- package/dist/cli/lib/governanceProcedures.d.ts.map +1 -0
- package/dist/cli/lib/governanceProcedures.js +458 -0
- package/dist/cli/lib/handlers/index.d.ts +5 -0
- package/dist/cli/lib/handlers/index.d.ts.map +1 -0
- package/dist/cli/lib/handlers/index.js +5 -0
- package/dist/cli/lib/repoDefinitions/index.d.ts +6 -0
- package/dist/cli/lib/repoDefinitions/index.d.ts.map +1 -0
- package/dist/cli/lib/repoDefinitions/index.js +21 -0
- package/dist/cli/lib/repoDefinitions/moonbeam.d.ts +4 -0
- package/dist/cli/lib/repoDefinitions/moonbeam.d.ts.map +1 -0
- package/dist/cli/lib/repoDefinitions/moonbeam.js +30 -0
- package/dist/cli/lib/repoDefinitions/polkadot.d.ts +4 -0
- package/dist/cli/lib/repoDefinitions/polkadot.d.ts.map +1 -0
- package/dist/cli/lib/repoDefinitions/polkadot.js +11 -0
- package/dist/cli/lib/repoDefinitions/tanssi.d.ts +4 -0
- package/dist/cli/lib/repoDefinitions/tanssi.d.ts.map +1 -0
- package/dist/cli/lib/repoDefinitions/tanssi.js +14 -0
- package/dist/cli/lib/rpcFunctions.d.ts +2 -0
- package/dist/cli/lib/rpcFunctions.d.ts.map +1 -0
- package/dist/cli/lib/rpcFunctions.js +26 -0
- package/dist/cli/lib/runnerContext.d.ts +32 -0
- package/dist/cli/lib/runnerContext.d.ts.map +1 -0
- package/dist/cli/lib/runnerContext.js +156 -0
- package/dist/cli/lib/shardManager.d.ts +40 -0
- package/dist/cli/lib/shardManager.d.ts.map +1 -0
- package/dist/cli/lib/shardManager.js +80 -0
- package/dist/cli/lib/upgradeProcedures.d.ts +5 -0
- package/dist/cli/lib/upgradeProcedures.d.ts.map +1 -0
- package/dist/cli/lib/upgradeProcedures.js +221 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3 -0
- package/dist/contracts/contractInteraction.d.ts +17 -0
- package/dist/contracts/contractInteraction.d.ts.map +1 -0
- package/dist/contracts/contractInteraction.js +170 -0
- package/dist/contracts/index.d.ts +2 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/foundations/chopsticks/handler.d.ts +3 -0
- package/dist/foundations/chopsticks/handler.d.ts.map +1 -0
- package/dist/foundations/chopsticks/handler.js +93 -0
- package/dist/foundations/chopsticks/helpers.d.ts +27 -0
- package/dist/foundations/chopsticks/helpers.d.ts.map +1 -0
- package/dist/foundations/chopsticks/helpers.js +133 -0
- package/dist/foundations/dev/handler.d.ts +3 -0
- package/dist/foundations/dev/handler.d.ts.map +1 -0
- package/dist/foundations/dev/handler.js +136 -0
- package/dist/foundations/dev/helpers.d.ts +27 -0
- package/dist/foundations/dev/helpers.d.ts.map +1 -0
- package/dist/foundations/dev/helpers.js +161 -0
- package/dist/foundations/read-only/handler.d.ts +3 -0
- package/dist/foundations/read-only/handler.d.ts.map +1 -0
- package/dist/foundations/read-only/handler.js +32 -0
- package/dist/foundations/zombie/handler.d.ts +3 -0
- package/dist/foundations/zombie/handler.d.ts.map +1 -0
- package/dist/foundations/zombie/handler.js +92 -0
- package/dist/foundations/zombie/helpers.d.ts +16 -0
- package/dist/foundations/zombie/helpers.d.ts.map +1 -0
- package/dist/foundations/zombie/helpers.js +97 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/internal/common.d.ts +3 -0
- package/dist/internal/common.d.ts.map +1 -0
- package/dist/internal/common.js +41 -0
- package/dist/internal/index.d.ts +4 -0
- package/dist/internal/index.d.ts.map +1 -0
- package/dist/internal/index.js +3 -0
- package/dist/internal/logger.d.ts +24 -0
- package/dist/internal/logger.d.ts.map +1 -0
- package/dist/internal/logger.js +66 -0
- package/dist/internal/logging.d.ts +7 -0
- package/dist/internal/logging.d.ts.map +1 -0
- package/dist/internal/logging.js +36 -0
- package/dist/moondebug.d.ts +3 -0
- package/dist/moondebug.d.ts.map +1 -0
- package/dist/moondebug.js +2 -0
- package/dist/services/cache/FileLock.d.ts +11 -0
- package/dist/services/cache/FileLock.d.ts.map +1 -0
- package/dist/services/cache/FileLock.js +68 -0
- package/dist/services/cache/StartupCacheService.d.ts +23 -0
- package/dist/services/cache/StartupCacheService.d.ts.map +1 -0
- package/dist/services/cache/StartupCacheService.js +159 -0
- package/dist/services/cache/index.d.ts +3 -0
- package/dist/services/cache/index.d.ts.map +1 -0
- package/dist/services/cache/index.js +2 -0
- package/dist/services/chopsticks/ChopsticksMultiChain.d.ts +158 -0
- package/dist/services/chopsticks/ChopsticksMultiChain.d.ts.map +1 -0
- package/dist/services/chopsticks/ChopsticksMultiChain.js +282 -0
- package/dist/services/chopsticks/ChopsticksService.d.ts +313 -0
- package/dist/services/chopsticks/ChopsticksService.d.ts.map +1 -0
- package/dist/services/chopsticks/ChopsticksService.js +77 -0
- package/dist/services/chopsticks/chopsticksConfigParser.d.ts +40 -0
- package/dist/services/chopsticks/chopsticksConfigParser.d.ts.map +1 -0
- package/dist/services/chopsticks/chopsticksConfigParser.js +201 -0
- package/dist/services/chopsticks/index.d.ts +5 -0
- package/dist/services/chopsticks/index.d.ts.map +1 -0
- package/dist/services/chopsticks/index.js +4 -0
- package/dist/services/chopsticks/launchChopsticksEffect.d.ts +225 -0
- package/dist/services/chopsticks/launchChopsticksEffect.d.ts.map +1 -0
- package/dist/services/chopsticks/launchChopsticksEffect.js +623 -0
- package/dist/services/config/configAccessors.d.ts +41 -0
- package/dist/services/config/configAccessors.d.ts.map +1 -0
- package/dist/services/config/configAccessors.js +149 -0
- package/dist/services/config/index.d.ts +2 -0
- package/dist/services/config/index.d.ts.map +1 -0
- package/dist/services/config/index.js +1 -0
- package/dist/services/errors.d.ts +72 -0
- package/dist/services/errors.d.ts.map +1 -0
- package/dist/services/errors.js +31 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +6 -0
- package/dist/services/network/NodeReadinessService.d.ts +35 -0
- package/dist/services/network/NodeReadinessService.d.ts.map +1 -0
- package/dist/services/network/NodeReadinessService.js +120 -0
- package/dist/services/network/PortDiscoveryService.d.ts +22 -0
- package/dist/services/network/PortDiscoveryService.d.ts.map +1 -0
- package/dist/services/network/PortDiscoveryService.js +77 -0
- package/dist/services/network/RpcPortDiscoveryService.d.ts +25 -0
- package/dist/services/network/RpcPortDiscoveryService.d.ts.map +1 -0
- package/dist/services/network/RpcPortDiscoveryService.js +136 -0
- package/dist/services/network/index.d.ts +4 -0
- package/dist/services/network/index.d.ts.map +1 -0
- package/dist/services/network/index.js +3 -0
- package/dist/services/process/ProcessManagerService.d.ts +49 -0
- package/dist/services/process/ProcessManagerService.d.ts.map +1 -0
- package/dist/services/process/ProcessManagerService.js +162 -0
- package/dist/services/process/index.d.ts +3 -0
- package/dist/services/process/index.d.ts.map +1 -0
- package/dist/services/process/index.js +2 -0
- package/dist/services/process/launchNodeEffect.d.ts +40 -0
- package/dist/services/process/launchNodeEffect.d.ts.map +1 -0
- package/dist/services/process/launchNodeEffect.js +86 -0
- package/dist/util/functions/index.d.ts +3 -0
- package/dist/util/functions/index.d.ts.map +1 -0
- package/dist/util/functions/index.js +4 -0
- package/dist/util/index.d.ts +4 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +2 -0
- package/package.json +157 -0
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
import { createLogger } from "../../util/index.js";
|
|
2
|
+
import zombie from "@zombienet/orchestrator";
|
|
3
|
+
import Docker from "dockerode";
|
|
4
|
+
import { ChildProcess, exec, execSync } from "node:child_process";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import net from "node:net";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import readline from "node:readline";
|
|
9
|
+
import { setTimeout as timer } from "node:timers/promises";
|
|
10
|
+
import { promisify } from "node:util";
|
|
11
|
+
import invariant from "tiny-invariant";
|
|
12
|
+
import { launchNodeLegacy, withTimeout } from "../internal/index.js";
|
|
13
|
+
import { LaunchCommandParser, parseChopsticksRunCmd, parseZombieCmd, } from "../internal/commandParsers.js";
|
|
14
|
+
import { checkZombieBins, getZombieConfig, } from "../internal/foundations/index.js";
|
|
15
|
+
import { launchNode } from "../internal/node.js";
|
|
16
|
+
import { launchChopsticksFromSpec } from "../internal/effect/index.js";
|
|
17
|
+
import { ProviderFactory, ProviderInterfaceFactory, vitestAutoUrl, } from "../internal/providerFactories.js";
|
|
18
|
+
import { getEnvironmentFromConfig, importAsyncConfig, isEthereumDevConfig, isEthereumZombieConfig, isOptionSet, } from "./configReader.js";
|
|
19
|
+
const logger = createLogger({ name: "context" });
|
|
20
|
+
const debugSetup = logger.debug.bind(logger);
|
|
21
|
+
export class MoonwallContext {
|
|
22
|
+
static instance;
|
|
23
|
+
configured = false;
|
|
24
|
+
environment;
|
|
25
|
+
providers;
|
|
26
|
+
nodes;
|
|
27
|
+
foundation;
|
|
28
|
+
zombieNetwork;
|
|
29
|
+
rtUpgradePath;
|
|
30
|
+
ipcServer;
|
|
31
|
+
injectedOptions;
|
|
32
|
+
nodeCleanupHandlers = [];
|
|
33
|
+
constructor(config, options) {
|
|
34
|
+
const env = config.environments.find(({ name }) => name === process.env.MOON_TEST_ENV);
|
|
35
|
+
invariant(env, `Environment ${process.env.MOON_TEST_ENV} not found in config`);
|
|
36
|
+
this.providers = [];
|
|
37
|
+
this.nodes = [];
|
|
38
|
+
this.foundation = env.foundation.type;
|
|
39
|
+
this.injectedOptions = options;
|
|
40
|
+
}
|
|
41
|
+
async setupFoundation() {
|
|
42
|
+
const config = await importAsyncConfig();
|
|
43
|
+
const env = config.environments.find(({ name }) => name === process.env.MOON_TEST_ENV);
|
|
44
|
+
invariant(env, `Environment ${process.env.MOON_TEST_ENV} not found in config`);
|
|
45
|
+
const foundationHandlers = {
|
|
46
|
+
read_only: this.handleReadOnly,
|
|
47
|
+
chopsticks: this.handleChopsticks,
|
|
48
|
+
dev: this.handleDev,
|
|
49
|
+
zombie: this.handleZombie,
|
|
50
|
+
};
|
|
51
|
+
const foundationHandler = foundationHandlers[env.foundation.type];
|
|
52
|
+
this.environment = {
|
|
53
|
+
providers: [],
|
|
54
|
+
nodes: [],
|
|
55
|
+
...(await foundationHandler.call(this, env, config)),
|
|
56
|
+
};
|
|
57
|
+
this.configured = true;
|
|
58
|
+
}
|
|
59
|
+
async handleZombie(env) {
|
|
60
|
+
invariant(env.foundation.type === "zombie", "Foundation type must be 'zombie'");
|
|
61
|
+
const { cmd: zombieConfig } = await parseZombieCmd(env.foundation.zombieSpec);
|
|
62
|
+
this.rtUpgradePath = env.foundation.rtUpgradePath;
|
|
63
|
+
return {
|
|
64
|
+
name: env.name,
|
|
65
|
+
foundationType: "zombie",
|
|
66
|
+
nodes: [
|
|
67
|
+
{
|
|
68
|
+
name: env.foundation.zombieSpec.name,
|
|
69
|
+
cmd: zombieConfig,
|
|
70
|
+
args: [],
|
|
71
|
+
launch: true,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
async handleDev(env, config) {
|
|
77
|
+
invariant(env.foundation.type === "dev", "Foundation type must be 'dev'");
|
|
78
|
+
// Always use async port allocation for better collision avoidance
|
|
79
|
+
const { cmd, args, launch } = await LaunchCommandParser.create({
|
|
80
|
+
launchSpec: env.foundation.launchSpec[0],
|
|
81
|
+
additionalRepos: config.additionalRepos,
|
|
82
|
+
launchOverrides: this.injectedOptions,
|
|
83
|
+
verbose: false,
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
name: env.name,
|
|
87
|
+
foundationType: "dev",
|
|
88
|
+
nodes: [
|
|
89
|
+
{
|
|
90
|
+
name: env.foundation.launchSpec[0].name,
|
|
91
|
+
cmd,
|
|
92
|
+
args,
|
|
93
|
+
launch,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
// Providers will be prepared in connectEnvironment after MOONWALL_RPC_PORT is set
|
|
97
|
+
providers: [],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async handleReadOnly(env) {
|
|
101
|
+
invariant(env.foundation.type === "read_only", "Foundation type must be 'read_only'");
|
|
102
|
+
invariant(env.connections, `${env.name} env config is missing connections specification, required by foundation READ_ONLY`);
|
|
103
|
+
return {
|
|
104
|
+
name: env.name,
|
|
105
|
+
foundationType: "read_only",
|
|
106
|
+
providers: ProviderFactory.prepare(env.connections),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
async handleChopsticks(env) {
|
|
110
|
+
invariant(env.foundation.type === "chopsticks", "Foundation type must be 'chopsticks'");
|
|
111
|
+
invariant(env.connections && env.connections.length > 0, `${env.name} env config is missing connections specification, required by foundation CHOPSTICKS`);
|
|
112
|
+
this.rtUpgradePath = env.foundation.rtUpgradePath;
|
|
113
|
+
return {
|
|
114
|
+
name: env.name,
|
|
115
|
+
foundationType: "chopsticks",
|
|
116
|
+
nodes: [parseChopsticksRunCmd(env.foundation.launchSpec)],
|
|
117
|
+
providers: [...ProviderFactory.prepare(env.connections)],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
async startZombieNetwork() {
|
|
121
|
+
const env = getEnvironmentFromConfig();
|
|
122
|
+
invariant(env.foundation.type === "zombie", "Foundation type must be 'zombie', something has gone very wrong.");
|
|
123
|
+
console.log("🧟 Spawning zombie nodes ...");
|
|
124
|
+
const nodes = this.environment.nodes;
|
|
125
|
+
const zombieConfig = getZombieConfig(nodes[0].cmd);
|
|
126
|
+
await checkZombieBins(zombieConfig);
|
|
127
|
+
const network = await zombie.start("", zombieConfig, { logType: "silent" });
|
|
128
|
+
const ipcLogPath = path.join(network.tmpDir, "ipc-server.log");
|
|
129
|
+
const ipcLogger = fs.createWriteStream(ipcLogPath, { flags: "a" });
|
|
130
|
+
const logIpc = (message) => {
|
|
131
|
+
const timestamp = new Date().toISOString();
|
|
132
|
+
ipcLogger.write(`${timestamp} - ${message}\n`);
|
|
133
|
+
};
|
|
134
|
+
process.env.MOON_RELAY_WSS = network.relay[0].wsUri;
|
|
135
|
+
if (Object.entries(network.paras).length > 0) {
|
|
136
|
+
process.env.MOON_PARA_WSS = Object.values(network.paras)[0].nodes[0].wsUri;
|
|
137
|
+
}
|
|
138
|
+
const nodeNames = Object.keys(network.nodesByName);
|
|
139
|
+
process.env.MOON_ZOMBIE_DIR = `${network.tmpDir}`;
|
|
140
|
+
process.env.MOON_ZOMBIE_NODES = nodeNames.join("|");
|
|
141
|
+
const onProcessExit = () => {
|
|
142
|
+
try {
|
|
143
|
+
invariant(this.zombieNetwork, "Zombie network not found to kill");
|
|
144
|
+
const processIds = Object.values(this.zombieNetwork.client.processMap)
|
|
145
|
+
.filter((item) => item.pid)
|
|
146
|
+
.map((process) => process.pid);
|
|
147
|
+
exec(`kill ${processIds.join(" ")}`, (error) => {
|
|
148
|
+
if (error) {
|
|
149
|
+
console.error(`Error killing process: ${error.message}`);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch (_error) {
|
|
154
|
+
// console.log(err.message);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
// Refactored IPC Server Implementation Starts Here
|
|
158
|
+
const socketPath = `${network.tmpDir}/node-ipc.sock`;
|
|
159
|
+
// Remove existing socket file if it exists to prevent EADDRINUSE errors
|
|
160
|
+
if (fs.existsSync(socketPath)) {
|
|
161
|
+
fs.unlinkSync(socketPath);
|
|
162
|
+
logIpc(`Removed existing socket at ${socketPath}`);
|
|
163
|
+
}
|
|
164
|
+
const server = net.createServer((client) => {
|
|
165
|
+
logIpc("📨 IPC server created");
|
|
166
|
+
logIpc(`Socket path: ${socketPath}`);
|
|
167
|
+
// Client message handling
|
|
168
|
+
client.on("data", async (data) => {
|
|
169
|
+
const writeToClient = (message) => {
|
|
170
|
+
if (client.writable) {
|
|
171
|
+
client.write(JSON.stringify(message));
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
logIpc("Client disconnected, cannot send response.");
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
try {
|
|
178
|
+
const message = JSON.parse(data.toString());
|
|
179
|
+
invariant(message.nodeName, "nodeName not provided in message");
|
|
180
|
+
const zombieClient = network.client;
|
|
181
|
+
switch (message.cmd) {
|
|
182
|
+
case "networkmap": {
|
|
183
|
+
const result = Object.keys(network.nodesByName);
|
|
184
|
+
writeToClient({
|
|
185
|
+
status: "success",
|
|
186
|
+
result: network.nodesByName,
|
|
187
|
+
message: result.join("|"),
|
|
188
|
+
});
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case "restart": {
|
|
192
|
+
logIpc(`📨 Restart command received for node: ${message.nodeName}`);
|
|
193
|
+
try {
|
|
194
|
+
await this.disconnect();
|
|
195
|
+
logIpc("✅ Disconnected all providers.");
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
logIpc(`❌ Error during disconnect: ${err}`);
|
|
199
|
+
throw err;
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
logIpc(`📨 Restarting node: ${message.nodeName}`);
|
|
203
|
+
// Timeout is in seconds 🤦
|
|
204
|
+
await zombieClient.restartNode(message.nodeName, 5);
|
|
205
|
+
logIpc(`✅ Restarted node: ${message.nodeName}`);
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
logIpc(`❌ Error during node restart: ${err}`);
|
|
209
|
+
throw err;
|
|
210
|
+
}
|
|
211
|
+
await timer(5000); // TODO: Replace when zombienet has an appropriate fn
|
|
212
|
+
try {
|
|
213
|
+
logIpc("🔄 Reconnecting environment...");
|
|
214
|
+
await this.connectEnvironment();
|
|
215
|
+
logIpc("✅ Reconnected environment.");
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
logIpc(`❌ Error during environment reconnection: ${err}`);
|
|
219
|
+
throw err;
|
|
220
|
+
}
|
|
221
|
+
writeToClient({
|
|
222
|
+
status: "success",
|
|
223
|
+
result: true,
|
|
224
|
+
message: `${message.nodeName} restarted`,
|
|
225
|
+
});
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case "resume": {
|
|
229
|
+
const node = network.getNodeByName(message.nodeName);
|
|
230
|
+
await this.disconnect();
|
|
231
|
+
const result = await node.resume();
|
|
232
|
+
await zombieClient.wait_node_ready(message.nodeName);
|
|
233
|
+
await this.connectEnvironment(true);
|
|
234
|
+
writeToClient({
|
|
235
|
+
status: "success",
|
|
236
|
+
result,
|
|
237
|
+
message: `${message.nodeName} resumed with result ${result}`,
|
|
238
|
+
});
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
case "pause": {
|
|
242
|
+
const node = network.getNodeByName(message.nodeName);
|
|
243
|
+
await this.disconnect();
|
|
244
|
+
const result = await node.pause();
|
|
245
|
+
await timer(1000); // TODO: Replace when zombienet has an appropriate fn
|
|
246
|
+
writeToClient({
|
|
247
|
+
status: "success",
|
|
248
|
+
result,
|
|
249
|
+
message: `${message.nodeName} paused with result ${result}`,
|
|
250
|
+
});
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
case "kill": {
|
|
254
|
+
// await this.disconnect();
|
|
255
|
+
const pid = network.client.processMap[message.nodeName].pid;
|
|
256
|
+
delete network.client.processMap[message.nodeName];
|
|
257
|
+
execSync(`kill ${pid}`, { stdio: "ignore" });
|
|
258
|
+
// await this.connectEnvironment(true);
|
|
259
|
+
writeToClient({
|
|
260
|
+
status: "success",
|
|
261
|
+
result: true,
|
|
262
|
+
message: `${message.nodeName}, pid ${pid} killed`,
|
|
263
|
+
});
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
case "isup": {
|
|
267
|
+
const node = network.getNodeByName(message.nodeName);
|
|
268
|
+
const result = await node.isUp();
|
|
269
|
+
writeToClient({
|
|
270
|
+
status: "success",
|
|
271
|
+
result,
|
|
272
|
+
message: `${message.nodeName} isUp result is ${result}`,
|
|
273
|
+
});
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
default:
|
|
277
|
+
invariant(false, `Invalid command received: ${message.cmd}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
logIpc("📨 Error processing message from client");
|
|
282
|
+
logIpc(e.message);
|
|
283
|
+
writeToClient({
|
|
284
|
+
status: "failure",
|
|
285
|
+
result: false,
|
|
286
|
+
message: e.message,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
// Handle client errors
|
|
291
|
+
client.on("error", (err) => {
|
|
292
|
+
logIpc(`📨 IPC client error:${err}`);
|
|
293
|
+
});
|
|
294
|
+
// Handle client disconnection
|
|
295
|
+
client.on("close", () => {
|
|
296
|
+
logIpc("📨 IPC client disconnected");
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
// Handle server errors to prevent crashes
|
|
300
|
+
server.on("error", (err) => {
|
|
301
|
+
console.error("IPC Server error:", err);
|
|
302
|
+
});
|
|
303
|
+
server.listen(socketPath, () => {
|
|
304
|
+
logIpc(`📨 IPC Server attempting to listen on ${socketPath}`);
|
|
305
|
+
try {
|
|
306
|
+
fs.chmodSync(socketPath, 0o600);
|
|
307
|
+
logIpc("📨 Successfully set socket permissions");
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
console.error("📨 Error setting socket permissions:", err);
|
|
311
|
+
}
|
|
312
|
+
logIpc(`📨 IPC Server listening on ${socketPath}`);
|
|
313
|
+
});
|
|
314
|
+
this.ipcServer = server;
|
|
315
|
+
process.env.MOON_IPC_SOCKET = socketPath;
|
|
316
|
+
process.once("exit", onProcessExit);
|
|
317
|
+
process.once("SIGINT", onProcessExit);
|
|
318
|
+
this.zombieNetwork = network;
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
async startNetwork() {
|
|
322
|
+
const ctx = await MoonwallContext.getContext();
|
|
323
|
+
if (process.env.MOON_RECYCLE === "true") {
|
|
324
|
+
debugSetup("🔄 MOON_RECYCLE=true, skipping node launch");
|
|
325
|
+
return ctx;
|
|
326
|
+
}
|
|
327
|
+
if (this.nodes.length > 0) {
|
|
328
|
+
debugSetup(`♻️ Reusing existing ${this.nodes.length} node(s) - skipping launch`);
|
|
329
|
+
return ctx;
|
|
330
|
+
}
|
|
331
|
+
debugSetup("🚀 No existing nodes found, launching new node...");
|
|
332
|
+
const nodes = ctx.environment.nodes;
|
|
333
|
+
if (this.environment.foundationType === "zombie") {
|
|
334
|
+
return await this.startZombieNetwork();
|
|
335
|
+
}
|
|
336
|
+
const env = getEnvironmentFromConfig();
|
|
337
|
+
const launchSpec = "launchSpec" in env.foundation && Array.isArray(env.foundation.launchSpec)
|
|
338
|
+
? env.foundation.launchSpec[0]
|
|
339
|
+
: undefined;
|
|
340
|
+
// Use programmatic chopsticks API for non-legacy chopsticks foundation
|
|
341
|
+
if (this.environment.foundationType === "chopsticks" &&
|
|
342
|
+
env.foundation.type === "chopsticks" &&
|
|
343
|
+
!env.foundation.launchSpec[0].legacy) {
|
|
344
|
+
debugSetup("🍴 Launching chopsticks via programmatic API...");
|
|
345
|
+
const maxStartupTimeout = 120000; // 2 minutes for chopsticks (needs to connect to remote endpoint)
|
|
346
|
+
await withTimeout(Promise.all(env.foundation.launchSpec.map(async (spec, index) => {
|
|
347
|
+
try {
|
|
348
|
+
const result = await launchChopsticksFromSpec(spec, {
|
|
349
|
+
timeout: maxStartupTimeout - 10000, // Leave 10s buffer for cleanup on timeout
|
|
350
|
+
chopsticksLogLevel: "silent", // Use moonwall logger instead of chopsticks' native logs
|
|
351
|
+
});
|
|
352
|
+
// Store cleanup handler for disconnect
|
|
353
|
+
this.nodeCleanupHandlers.push(result.cleanup);
|
|
354
|
+
// Set environment variables for test connections
|
|
355
|
+
// First chopsticks instance sets MOONWALL_RPC_PORT
|
|
356
|
+
if (index === 0) {
|
|
357
|
+
process.env.MOONWALL_RPC_PORT = result.port.toString();
|
|
358
|
+
process.env.WSS_URL = `ws://127.0.0.1:${result.port}`;
|
|
359
|
+
debugSetup(`Set MOONWALL_RPC_PORT=${result.port}, WSS_URL=${process.env.WSS_URL}`);
|
|
360
|
+
}
|
|
361
|
+
debugSetup(`✅ Chopsticks '${spec.configPath}' started at ${result.addr}`);
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
throw new Error(`Failed to start chopsticks '${spec.configPath}': ${error.message}`);
|
|
365
|
+
}
|
|
366
|
+
})), maxStartupTimeout);
|
|
367
|
+
debugSetup("✅ All chopsticks instances started successfully.");
|
|
368
|
+
return ctx;
|
|
369
|
+
}
|
|
370
|
+
const maxStartupTimeout = launchSpec && "useDocker" in launchSpec && launchSpec.useDocker ? 300000 : 30000; // 5 minutes for Docker, 30s otherwise
|
|
371
|
+
await withTimeout(Promise.all(nodes.map(async ({ cmd, args, name, launch }) => {
|
|
372
|
+
if (launch) {
|
|
373
|
+
try {
|
|
374
|
+
const options = {
|
|
375
|
+
command: cmd,
|
|
376
|
+
args,
|
|
377
|
+
name: name || "node",
|
|
378
|
+
launchSpec,
|
|
379
|
+
};
|
|
380
|
+
const isLegacy = env.foundation.type === "dev"
|
|
381
|
+
? env.foundation.launchSpec[0].legacy
|
|
382
|
+
: env.foundation.type === "chopsticks"
|
|
383
|
+
? env.foundation.launchSpec[0].legacy
|
|
384
|
+
: env.foundation.type === "zombie"
|
|
385
|
+
? env.foundation.zombieSpec.legacy
|
|
386
|
+
: false;
|
|
387
|
+
const result = isLegacy
|
|
388
|
+
? await launchNodeLegacy(options)
|
|
389
|
+
: await launchNode(options);
|
|
390
|
+
this.nodes.push(result.runningNode);
|
|
391
|
+
if (result.runningNode instanceof ChildProcess) {
|
|
392
|
+
debugSetup(`✅ Node '${name || "unnamed"}' started with PID ${result.runningNode.pid}`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
throw new Error(`Failed to start node '${name || "unnamed"}': ${error.message}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
})), maxStartupTimeout);
|
|
400
|
+
debugSetup("✅ All network nodes started successfully.");
|
|
401
|
+
return ctx;
|
|
402
|
+
}
|
|
403
|
+
async connectEnvironment(silent = false) {
|
|
404
|
+
const env = getEnvironmentFromConfig();
|
|
405
|
+
// Prepare providers at connection time to ensure MOONWALL_RPC_PORT is set
|
|
406
|
+
if (this.environment.foundationType === "zombie") {
|
|
407
|
+
this.environment.providers = env.connections
|
|
408
|
+
? ProviderFactory.prepare(env.connections)
|
|
409
|
+
: isEthereumZombieConfig()
|
|
410
|
+
? ProviderFactory.prepareDefaultZombie()
|
|
411
|
+
: ProviderFactory.prepareNoEthDefaultZombie();
|
|
412
|
+
}
|
|
413
|
+
if (this.environment.foundationType === "dev") {
|
|
414
|
+
debugSetup(`Dev foundation - env.connections: ${env.connections ? "YES" : "NO"}, isEthereumDevConfig: ${isEthereumDevConfig()}`);
|
|
415
|
+
this.environment.providers = env.connections
|
|
416
|
+
? ProviderFactory.prepare(env.connections)
|
|
417
|
+
: isEthereumDevConfig()
|
|
418
|
+
? ProviderFactory.prepareDefaultDev()
|
|
419
|
+
: ProviderFactory.prepare([
|
|
420
|
+
{
|
|
421
|
+
name: "node",
|
|
422
|
+
type: "polkadotJs",
|
|
423
|
+
endpoints: [vitestAutoUrl()],
|
|
424
|
+
},
|
|
425
|
+
]);
|
|
426
|
+
}
|
|
427
|
+
if (this.providers.length > 0) {
|
|
428
|
+
return MoonwallContext.getContext();
|
|
429
|
+
}
|
|
430
|
+
const maxRetries = 150;
|
|
431
|
+
const retryDelay = 100;
|
|
432
|
+
const connectTimeout = 10000; // 10 seconds per attempt
|
|
433
|
+
const connectWithRetry = async (provider) => {
|
|
434
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
435
|
+
try {
|
|
436
|
+
debugSetup(`Connecting ${provider.name} (type: ${provider.type}), attempt ${attempt}`);
|
|
437
|
+
debugSetup(`MOONWALL_RPC_PORT=${process.env.MOONWALL_RPC_PORT}`);
|
|
438
|
+
debugSetup(`🔄 Connecting provider ${provider.name}, attempt ${attempt}`);
|
|
439
|
+
const connectedProvider = await Promise.race([
|
|
440
|
+
ProviderInterfaceFactory.populate(provider.name, provider.type, provider.connect),
|
|
441
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Connection attempt timed out")), connectTimeout)),
|
|
442
|
+
]);
|
|
443
|
+
this.providers.push(connectedProvider);
|
|
444
|
+
debugSetup(`✅ Provider ${provider.name} connected on attempt ${attempt}`);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
console.error(`❌ Error connecting provider ${provider.name} on attempt ${attempt}: ${error.message}`);
|
|
449
|
+
if (attempt === maxRetries) {
|
|
450
|
+
throw new Error(`Failed to connect provider '${provider.name}' after ${maxRetries} attempts: ${error.message}`);
|
|
451
|
+
}
|
|
452
|
+
debugSetup(`⚠️ Retrying provider ${provider.name} connection, attempt ${attempt + 1}/${maxRetries}`);
|
|
453
|
+
await timer(retryDelay);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
try {
|
|
458
|
+
await Promise.all(this.environment.providers.map(connectWithRetry));
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
console.error(`Error connecting to environment: ${error.message}`);
|
|
462
|
+
console.error("Current providers:", this.providers.map((p) => p.name).join(", "));
|
|
463
|
+
console.error(`Total providers: ${this.environment.providers.map((p) => p.name).join(", ")}`);
|
|
464
|
+
throw error;
|
|
465
|
+
}
|
|
466
|
+
if (this.foundation === "zombie") {
|
|
467
|
+
await this.handleZombiePostConnection(silent, env);
|
|
468
|
+
}
|
|
469
|
+
return MoonwallContext.getContext();
|
|
470
|
+
}
|
|
471
|
+
async handleZombiePostConnection(silent, env) {
|
|
472
|
+
let readStreams = [];
|
|
473
|
+
if (!isOptionSet("disableLogEavesdropping")) {
|
|
474
|
+
!silent && console.log(`🦻 Eavesdropping on node logs at ${process.env.MOON_ZOMBIE_DIR}`);
|
|
475
|
+
const envVar = process.env.MOON_ZOMBIE_NODES;
|
|
476
|
+
invariant(envVar, "MOON_ZOMBIE_NODES not set, this is an error please raise.");
|
|
477
|
+
const zombieNodeLogs = envVar
|
|
478
|
+
.split("|")
|
|
479
|
+
.map((nodeName) => `${process.env.MOON_ZOMBIE_DIR}/${nodeName}.log`);
|
|
480
|
+
readStreams = zombieNodeLogs.map((logPath) => {
|
|
481
|
+
const readStream = fs.createReadStream(logPath, { encoding: "utf8" });
|
|
482
|
+
const lineReader = readline.createInterface({
|
|
483
|
+
input: readStream,
|
|
484
|
+
});
|
|
485
|
+
lineReader.on("line", (line) => {
|
|
486
|
+
if (line.includes("WARN") || line.includes("ERROR")) {
|
|
487
|
+
console.log(line);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
return readStream;
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
const polkadotJsProviders = this.providers
|
|
494
|
+
.filter(({ type }) => type === "polkadotJs")
|
|
495
|
+
.filter(({ name }) => env.foundation.type === "zombie" &&
|
|
496
|
+
(!env.foundation.zombieSpec.skipBlockCheck ||
|
|
497
|
+
!env.foundation.zombieSpec.skipBlockCheck.includes(name)));
|
|
498
|
+
await Promise.all(polkadotJsProviders.map(async (provider) => {
|
|
499
|
+
!silent && console.log(`⏲️ Waiting for chain ${provider.name} to produce blocks...`);
|
|
500
|
+
while ((await provider.api.rpc.chain.getBlock()).block.header.number.toNumber() === 0) {
|
|
501
|
+
await timer(500);
|
|
502
|
+
}
|
|
503
|
+
!silent && console.log(`✅ Chain ${provider.name} producing blocks, continuing`);
|
|
504
|
+
}));
|
|
505
|
+
if (!isOptionSet("disableLogEavesdropping")) {
|
|
506
|
+
for (const readStream of readStreams) {
|
|
507
|
+
readStream.close();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async disconnect(providerName) {
|
|
512
|
+
if (providerName) {
|
|
513
|
+
const prov = this.providers.find(({ name }) => name === providerName);
|
|
514
|
+
invariant(prov, `Provider ${providerName} not found`);
|
|
515
|
+
try {
|
|
516
|
+
await prov.disconnect();
|
|
517
|
+
debugSetup(`✅ Provider ${providerName} disconnected`);
|
|
518
|
+
}
|
|
519
|
+
catch (error) {
|
|
520
|
+
console.error(`❌ Error disconnecting provider ${providerName}: ${error.message}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
await Promise.all(this.providers.map(async (prov) => {
|
|
525
|
+
try {
|
|
526
|
+
await prov.disconnect();
|
|
527
|
+
debugSetup(`✅ Provider ${prov.name} disconnected`);
|
|
528
|
+
}
|
|
529
|
+
catch (error) {
|
|
530
|
+
console.error(`❌ Error disconnecting provider ${prov.name}: ${error.message}`);
|
|
531
|
+
}
|
|
532
|
+
}));
|
|
533
|
+
this.providers = [];
|
|
534
|
+
}
|
|
535
|
+
// Clean up nodes
|
|
536
|
+
if (this.nodes.length > 0) {
|
|
537
|
+
for (const node of this.nodes) {
|
|
538
|
+
if (node instanceof ChildProcess) {
|
|
539
|
+
// Use Effect-based cleanup if available (automatic resource management)
|
|
540
|
+
const moonwallNode = node;
|
|
541
|
+
if (moonwallNode.effectCleanup) {
|
|
542
|
+
try {
|
|
543
|
+
await moonwallNode.effectCleanup();
|
|
544
|
+
}
|
|
545
|
+
catch (_error) {
|
|
546
|
+
// Fallback to manual kill if Effect cleanup fails
|
|
547
|
+
try {
|
|
548
|
+
if (node.pid) {
|
|
549
|
+
process.kill(node.pid);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
catch (_killError) {
|
|
553
|
+
// Ignore errors when killing processes
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
// Legacy manual cleanup for nodes without Effect
|
|
559
|
+
try {
|
|
560
|
+
if (node.pid) {
|
|
561
|
+
process.kill(node.pid);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
catch (_error) {
|
|
565
|
+
// Ignore errors when killing processes
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (node instanceof Docker.Container) {
|
|
570
|
+
try {
|
|
571
|
+
await node.stop();
|
|
572
|
+
await node.remove();
|
|
573
|
+
}
|
|
574
|
+
catch (_error) {
|
|
575
|
+
// Ignore errors when stopping containers
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
this.nodes = [];
|
|
580
|
+
}
|
|
581
|
+
// Run any cleanup handlers (e.g. for Docker containers)
|
|
582
|
+
if (this.nodeCleanupHandlers.length > 0) {
|
|
583
|
+
await Promise.all(this.nodeCleanupHandlers.map((handler) => handler()));
|
|
584
|
+
this.nodeCleanupHandlers = [];
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
static async getContext(config, options, force = false) {
|
|
588
|
+
invariant(!(options && MoonwallContext.instance), "Attempting to open a new context with overrides when context already exists");
|
|
589
|
+
if (!MoonwallContext.instance?.configured || force) {
|
|
590
|
+
invariant(config, "Config must be provided on Global Context instantiation");
|
|
591
|
+
MoonwallContext.instance = new MoonwallContext(config, options);
|
|
592
|
+
await MoonwallContext.instance.setupFoundation();
|
|
593
|
+
debugSetup(`🟢 Moonwall context "${config.label}" created`);
|
|
594
|
+
}
|
|
595
|
+
return MoonwallContext.instance;
|
|
596
|
+
}
|
|
597
|
+
static async destroy(reason) {
|
|
598
|
+
const ctx = MoonwallContext.instance;
|
|
599
|
+
invariant(ctx, "No context to destroy");
|
|
600
|
+
try {
|
|
601
|
+
await ctx.disconnect();
|
|
602
|
+
}
|
|
603
|
+
catch {
|
|
604
|
+
console.log("🛑 All connections disconnected");
|
|
605
|
+
}
|
|
606
|
+
while (ctx.nodes.length > 0) {
|
|
607
|
+
const node = ctx.nodes.pop();
|
|
608
|
+
invariant(node, "No node to destroy");
|
|
609
|
+
if (node instanceof ChildProcess) {
|
|
610
|
+
const pid = node.pid;
|
|
611
|
+
invariant(pid, "No pid to destroy");
|
|
612
|
+
// Flag the process before killing it
|
|
613
|
+
const moonwallNode = node;
|
|
614
|
+
moonwallNode.isMoonwallTerminating = true;
|
|
615
|
+
moonwallNode.moonwallTerminationReason = reason || "shutdown";
|
|
616
|
+
node.kill("SIGINT");
|
|
617
|
+
for (;;) {
|
|
618
|
+
const isRunning = await isPidRunning(pid);
|
|
619
|
+
if (isRunning) {
|
|
620
|
+
await timer(10);
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (node instanceof Docker.Container) {
|
|
628
|
+
console.log("🛑 Stopping container");
|
|
629
|
+
// Try to append termination reason to Docker container log
|
|
630
|
+
const logLocation = process.env.MOON_LOG_LOCATION;
|
|
631
|
+
if (logLocation) {
|
|
632
|
+
const timestamp = new Date().toISOString();
|
|
633
|
+
const message = `${timestamp} [moonwall] container stopped. reason: ${reason || "shutdown"}\n`;
|
|
634
|
+
try {
|
|
635
|
+
fs.appendFileSync(logLocation, message);
|
|
636
|
+
}
|
|
637
|
+
catch (err) {
|
|
638
|
+
console.error(`Failed to append termination message to Docker log: ${err}`);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
await node.stop();
|
|
642
|
+
await node.remove();
|
|
643
|
+
console.log("🛑 Container stopped and removed");
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (ctx.zombieNetwork) {
|
|
647
|
+
console.log("🪓 Killing zombie nodes");
|
|
648
|
+
// Log termination reason for zombie network processes
|
|
649
|
+
const zombieProcesses = Object.values(ctx.zombieNetwork.client.processMap).filter((item) => item.pid);
|
|
650
|
+
for (const proc of zombieProcesses) {
|
|
651
|
+
if (proc.logPath) {
|
|
652
|
+
const timestamp = new Date().toISOString();
|
|
653
|
+
const message = `${timestamp} [moonwall] zombie network stopped. reason: ${reason || "shutdown"}\n`;
|
|
654
|
+
try {
|
|
655
|
+
fs.appendFileSync(proc.logPath, message);
|
|
656
|
+
}
|
|
657
|
+
catch (err) {
|
|
658
|
+
console.error(`Failed to append termination message to zombie log: ${err}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
await ctx.zombieNetwork.stop();
|
|
663
|
+
const processIds = zombieProcesses.map((process) => process.pid);
|
|
664
|
+
try {
|
|
665
|
+
execSync(`kill ${processIds.join(" ")}`, {});
|
|
666
|
+
}
|
|
667
|
+
catch (e) {
|
|
668
|
+
console.log(e.message);
|
|
669
|
+
console.log("continuing...");
|
|
670
|
+
}
|
|
671
|
+
await waitForPidsToDie(processIds);
|
|
672
|
+
ctx.ipcServer?.close(() => {
|
|
673
|
+
console.log("IPC Server closed.");
|
|
674
|
+
});
|
|
675
|
+
ctx.ipcServer?.removeAllListeners();
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
export const contextCreator = async (options) => {
|
|
680
|
+
const config = await importAsyncConfig();
|
|
681
|
+
const ctx = await MoonwallContext.getContext(config, options);
|
|
682
|
+
await runNetworkOnly();
|
|
683
|
+
await ctx.connectEnvironment();
|
|
684
|
+
return ctx;
|
|
685
|
+
};
|
|
686
|
+
export const runNetworkOnly = async () => {
|
|
687
|
+
const config = await importAsyncConfig();
|
|
688
|
+
const ctx = await MoonwallContext.getContext(config);
|
|
689
|
+
await ctx.startNetwork();
|
|
690
|
+
};
|
|
691
|
+
const execAsync = promisify(exec);
|
|
692
|
+
async function isPidRunning(pid) {
|
|
693
|
+
try {
|
|
694
|
+
const { stdout } = await execAsync(`ps -p ${pid} -o pid=`);
|
|
695
|
+
return stdout.trim() === pid.toString();
|
|
696
|
+
}
|
|
697
|
+
catch (_error) {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
async function waitForPidsToDie(pids) {
|
|
702
|
+
const checkPids = async () => {
|
|
703
|
+
const checks = pids.map(async (pid) => await isPidRunning(pid));
|
|
704
|
+
const results = await Promise.all(checks);
|
|
705
|
+
return results.every((running) => !running);
|
|
706
|
+
};
|
|
707
|
+
while (!(await checkPids())) {
|
|
708
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
709
|
+
}
|
|
710
|
+
}
|