@mysten-incubation/devstack 0.6.0 → 0.7.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/dist/plugins/walrus/client-services.mjs +13 -5
- package/dist/plugins/walrus/client-services.mjs.map +1 -1
- package/dist/plugins/walrus/codegen.d.mts +1 -0
- package/dist/plugins/walrus/codegen.mjs +1 -0
- package/dist/plugins/walrus/codegen.mjs.map +1 -1
- package/dist/plugins/walrus/errors.d.mts +1 -1
- package/dist/plugins/walrus/errors.mjs.map +1 -1
- package/dist/plugins/walrus/index.d.mts +10 -0
- package/dist/plugins/walrus/index.mjs +14 -3
- package/dist/plugins/walrus/index.mjs.map +1 -1
- package/dist/plugins/walrus/mode/known-deploy.d.mts +1 -0
- package/dist/plugins/walrus/mode/known-deploy.mjs +6 -2
- package/dist/plugins/walrus/mode/known-deploy.mjs.map +1 -1
- package/dist/plugins/walrus/mode/local-cluster.d.mts +6 -2
- package/dist/plugins/walrus/mode/local-cluster.mjs +10 -7
- package/dist/plugins/walrus/mode/local-cluster.mjs.map +1 -1
- package/dist/plugins/walrus/routable.mjs +27 -31
- package/dist/plugins/walrus/routable.mjs.map +1 -1
- package/dist/plugins/walrus/snapshot.mjs.map +1 -1
- package/images/walrus/Dockerfile +11 -7
- package/images/walrus/run-walrus-client-service.sh +41 -9
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@ import { walrusPluginError } from "./errors.mjs";
|
|
|
7
7
|
import { Effect, Scope } from "effect";
|
|
8
8
|
//#region src/plugins/walrus/client-services.ts
|
|
9
9
|
const DEFAULT_WALRUS_CLIENT_SERVICE_PORT = 31415;
|
|
10
|
+
const DEFAULT_WALRUS_UPLOAD_RELAY_SERVICE_PORT = 3e3;
|
|
10
11
|
const WALRUS_CLIENT_SERVICE_STOP_SIGNAL = "SIGINT";
|
|
11
12
|
const WALRUS_CLIENT_CONFIG_FILE = "client_config.yaml";
|
|
12
13
|
const WALRUS_CLIENT_WALLET_FILE = "sui_client.yaml";
|
|
@@ -23,6 +24,7 @@ const walrusClientServiceConfigHash = (parts) => [
|
|
|
23
24
|
`rpc=${parts.suiRpcUrlInNetwork}`
|
|
24
25
|
].join("|");
|
|
25
26
|
const SERVICE_READY_PROBE_INTERVAL_MS = 500;
|
|
27
|
+
const walrusClientServiceReadyPath = (role) => role === "upload-relay" ? "/v1/tip-config" : "/status";
|
|
26
28
|
const startWalrusClientServices = (runtime, spec) => Effect.gen(function* () {
|
|
27
29
|
const outerScope = yield* Effect.scope;
|
|
28
30
|
const serviceStopScope = yield* Scope.fork(outerScope, "parallel");
|
|
@@ -84,6 +86,7 @@ const startWalrusClientServices = (runtime, spec) => Effect.gen(function* () {
|
|
|
84
86
|
});
|
|
85
87
|
const handle = yield* Scope.provide(ensureService, serviceStopScope);
|
|
86
88
|
yield* setCurrentPluginPhase(`waiting for Walrus ${role} HTTP status on 127.0.0.1:${options.port}`);
|
|
89
|
+
const readyPath = walrusClientServiceReadyPath(role);
|
|
87
90
|
yield* waitForProbe({
|
|
88
91
|
label: `walrus.${role}`,
|
|
89
92
|
timeoutMs: readyTimeout,
|
|
@@ -91,10 +94,10 @@ const startWalrusClientServices = (runtime, spec) => Effect.gen(function* () {
|
|
|
91
94
|
probe: () => runtime.exec(handle, [
|
|
92
95
|
"sh",
|
|
93
96
|
"-c",
|
|
94
|
-
`curl -fsS --max-time 2 http://127.0.0.1:${options.port}
|
|
97
|
+
`curl -fsS --max-time 2 http://127.0.0.1:${options.port}${readyPath} >/dev/null`
|
|
95
98
|
]).pipe(Effect.map(exitCodeProbeResult))
|
|
96
99
|
}).pipe(Effect.mapError((cause) => {
|
|
97
|
-
if (cause instanceof ProbeTimeoutError) return walrusPluginError(role, `walrus ${role} never became ready within ${readyTimeout}ms (container=${containerName}, probe=http://127.0.0.1:${options.port}
|
|
100
|
+
if (cause instanceof ProbeTimeoutError) return walrusPluginError(role, `walrus ${role} never became ready within ${readyTimeout}ms (container=${containerName}, probe=http://127.0.0.1:${options.port}${readyPath}).`, { cause: cause.lastError ?? cause.lastNotReady ?? cause });
|
|
98
101
|
return walrusPluginError(role, `walrus ${role} ready-probe exec failed: ${cause.reason}: ${cause.detail}`, { cause });
|
|
99
102
|
}));
|
|
100
103
|
return {
|
|
@@ -103,13 +106,18 @@ const startWalrusClientServices = (runtime, spec) => Effect.gen(function* () {
|
|
|
103
106
|
containerPort: options.port
|
|
104
107
|
};
|
|
105
108
|
});
|
|
106
|
-
const [aggregator, publisher] = yield* Effect.all([
|
|
109
|
+
const [aggregator, publisher, uploadRelay] = yield* Effect.all([
|
|
110
|
+
spec.options.aggregator === null || spec.images.aggregator === null ? Effect.succeed(null) : startOne("aggregator", spec.images.aggregator, spec.options.aggregator),
|
|
111
|
+
spec.options.publisher === null || spec.images.publisher === null ? Effect.succeed(null) : startOne("publisher", spec.images.publisher, spec.options.publisher),
|
|
112
|
+
spec.options.uploadRelay === null || spec.images.uploadRelay === null ? Effect.succeed(null) : startOne("upload-relay", spec.images.uploadRelay, spec.options.uploadRelay)
|
|
113
|
+
], { concurrency: "unbounded" });
|
|
107
114
|
return {
|
|
108
115
|
aggregator,
|
|
109
|
-
publisher
|
|
116
|
+
publisher,
|
|
117
|
+
uploadRelay
|
|
110
118
|
};
|
|
111
119
|
});
|
|
112
120
|
//#endregion
|
|
113
|
-
export { DEFAULT_WALRUS_CLIENT_SERVICE_PORT, startWalrusClientServices };
|
|
121
|
+
export { DEFAULT_WALRUS_CLIENT_SERVICE_PORT, DEFAULT_WALRUS_UPLOAD_RELAY_SERVICE_PORT, startWalrusClientServices };
|
|
114
122
|
|
|
115
123
|
//# sourceMappingURL=client-services.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-services.mjs","names":[],"sources":["../../../src/plugins/walrus/client-services.ts"],"sourcesContent":["// Walrus local client-service lifecycle.\n//\n// These are the release-provided `walrus aggregator` and `walrus publisher`\n// subcommands, wrapped only enough to feed them the local deploy outputs and\n// make them routable through devstack.\n\nimport { Effect, Scope } from 'effect';\n\nimport type {\n\tContainerHandle,\n\tContainerRuntime,\n\tImageRef,\n} from '../../contracts/container-runtime.ts';\nimport { HOST_GATEWAY_EXTRA_HOSTS } from '../../substrate/runtime/host-gateway.ts';\nimport { ensureManagedContainer } from '../../substrate/runtime/managed-container.ts';\nimport {\n\tProbeTimeoutError,\n\texitCodeProbeResult,\n\twaitForProbe,\n} from '../../substrate/runtime/probes.ts';\nimport { setCurrentPluginPhase } from '../../substrate/runtime/current-plugin.ts';\nimport { walrusDeployMountPaths } from './deploy-paths.ts';\nimport { walrusPluginError, type WalrusPluginError } from './errors.ts';\n\nexport const DEFAULT_WALRUS_CLIENT_SERVICE_PORT = 31_415;\nexport const DEFAULT_WALRUS_CLIENT_SERVICE_READY_TIMEOUT_MS = 60_000;\nexport const DEFAULT_WALRUS_CLIENT_SERVICE_STOP_GRACE_SECONDS = 10;\nexport const WALRUS_CLIENT_SERVICE_STOP_SIGNAL = 'SIGINT';\nexport const WALRUS_CLIENT_CONFIG_FILE = 'client_config.yaml';\nexport const WALRUS_CLIENT_WALLET_FILE = 'sui_client.yaml';\nexport const WALRUS_CLIENT_KEYSTORE_FILE = 'sui_client.keystore';\n\nexport type WalrusClientServiceRole = 'aggregator' | 'publisher';\n\nexport interface WalrusClientServiceOptions {\n\treadonly port: number;\n}\n\nexport interface WalrusClientService {\n\treadonly role: WalrusClientServiceRole;\n\treadonly containerName: string;\n\treadonly containerPort: number;\n}\n\nexport interface WalrusClientServices {\n\treadonly aggregator: WalrusClientService | null;\n\treadonly publisher: WalrusClientService | null;\n}\n\nexport interface StartWalrusClientServicesSpec {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly walrusName: string;\n\treadonly images: {\n\t\treadonly aggregator: ImageRef | null;\n\t\treadonly publisher: ImageRef | null;\n\t};\n\treadonly options: {\n\t\treadonly aggregator: WalrusClientServiceOptions | null;\n\t\treadonly publisher: WalrusClientServiceOptions | null;\n\t};\n\treadonly walrusNetworkName: string;\n\treadonly suiNetworkName: string;\n\treadonly deployHostMountPath: string;\n\treadonly stackRoot: string;\n\treadonly deployConfigHash: string;\n\treadonly suiRpcUrlInNetwork: string;\n\treadonly stopGraceSeconds?: number;\n\treadonly readyTimeoutMs?: number;\n}\n\nexport const walrusClientServiceContainerName = (parts: {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly walrusName: string;\n\treadonly role: WalrusClientServiceRole;\n}): string => `devstack-${parts.app}-${parts.stack}-walrus-${parts.walrusName}-${parts.role}`;\n\nexport const walrusClientServiceConfigHash = (parts: {\n\treadonly role: WalrusClientServiceRole;\n\treadonly deployConfigHash: string;\n\treadonly deploySourceHostPath: string;\n\treadonly deployMountTarget: string;\n\treadonly containerPort: number;\n\treadonly walrusNetworkName: string;\n\treadonly suiNetworkName: string;\n\treadonly suiRpcUrlInNetwork: string;\n}): string =>\n\t[\n\t\t'walrus-client-service-v2',\n\t\t`role=${parts.role}`,\n\t\tparts.deployConfigHash,\n\t\t`client=${WALRUS_CLIENT_CONFIG_FILE},${WALRUS_CLIENT_WALLET_FILE},${WALRUS_CLIENT_KEYSTORE_FILE}`,\n\t\t`mount=${parts.deploySourceHostPath}->${parts.deployMountTarget}`,\n\t\t`port=${parts.containerPort}`,\n\t\t`net=${parts.walrusNetworkName},${parts.suiNetworkName}`,\n\t\t`rpc=${parts.suiRpcUrlInNetwork}`,\n\t].join('|');\n\nconst SERVICE_READY_PROBE_INTERVAL_MS = 500;\n\nexport const startWalrusClientServices = (\n\truntime: ContainerRuntime,\n\tspec: StartWalrusClientServicesSpec,\n): Effect.Effect<WalrusClientServices, WalrusPluginError, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tconst outerScope = yield* Effect.scope;\n\t\tconst serviceStopScope = yield* Scope.fork(outerScope, 'parallel');\n\t\tconst deployMount = walrusDeployMountPaths({\n\t\t\tstackRoot: spec.stackRoot,\n\t\t\tdeployOutputDirHostPath: spec.deployHostMountPath,\n\t\t\tmountTarget: '/opt/walrus/runtime',\n\t\t});\n\t\tconst readyTimeout = spec.readyTimeoutMs ?? DEFAULT_WALRUS_CLIENT_SERVICE_READY_TIMEOUT_MS;\n\t\tconst stopGraceSeconds =\n\t\t\tspec.stopGraceSeconds ?? DEFAULT_WALRUS_CLIENT_SERVICE_STOP_GRACE_SECONDS;\n\n\t\tconst startOne = (\n\t\t\trole: WalrusClientServiceRole,\n\t\t\timage: ImageRef,\n\t\t\toptions: WalrusClientServiceOptions,\n\t\t): Effect.Effect<WalrusClientService, WalrusPluginError, Scope.Scope> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst containerName = walrusClientServiceContainerName({\n\t\t\t\t\tapp: spec.app,\n\t\t\t\t\tstack: spec.stack,\n\t\t\t\t\twalrusName: spec.walrusName,\n\t\t\t\t\trole,\n\t\t\t\t});\n\n\t\t\t\tyield* setCurrentPluginPhase(`creating Walrus ${role} container ${containerName}`);\n\t\t\t\tconst ensureService: Effect.Effect<ContainerHandle, WalrusPluginError, Scope.Scope> =\n\t\t\t\t\tensureManagedContainer<WalrusPluginError>({\n\t\t\t\t\t\truntime,\n\t\t\t\t\t\tidentity: { app: spec.app, stack: spec.stack },\n\t\t\t\t\t\tplugin: 'walrus',\n\t\t\t\t\t\trole,\n\t\t\t\t\t\tspec: {\n\t\t\t\t\t\t\tname: containerName,\n\t\t\t\t\t\t\timage,\n\t\t\t\t\t\t\trecreate: 'on-config-change',\n\t\t\t\t\t\t\tconfigHash: walrusClientServiceConfigHash({\n\t\t\t\t\t\t\t\trole,\n\t\t\t\t\t\t\t\tdeployConfigHash: spec.deployConfigHash,\n\t\t\t\t\t\t\t\tdeploySourceHostPath: deployMount.sourceHostPath,\n\t\t\t\t\t\t\t\tdeployMountTarget: deployMount.mountTarget,\n\t\t\t\t\t\t\t\tcontainerPort: options.port,\n\t\t\t\t\t\t\t\twalrusNetworkName: spec.walrusNetworkName,\n\t\t\t\t\t\t\t\tsuiNetworkName: spec.suiNetworkName,\n\t\t\t\t\t\t\t\tsuiRpcUrlInNetwork: spec.suiRpcUrlInNetwork,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tenv: {\n\t\t\t\t\t\t\t\tDEPLOY_OUTPUT_DIR: deployMount.outputDirInContainer,\n\t\t\t\t\t\t\t\tSUI_RPC_URL: spec.suiRpcUrlInNetwork,\n\t\t\t\t\t\t\t\tWALRUS_CLIENT_SERVICE_BIND_ADDRESS: `0.0.0.0:${options.port}`,\n\t\t\t\t\t\t\t\tWALRUS_CONTAINER_PORT: String(options.port),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tcommand: [role],\n\t\t\t\t\t\t\tnetworkAttach: [spec.walrusNetworkName, spec.suiNetworkName],\n\t\t\t\t\t\t\textraHosts: HOST_GATEWAY_EXTRA_HOSTS,\n\t\t\t\t\t\t\tmounts: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsource: deployMount.sourceHostPath,\n\t\t\t\t\t\t\t\t\ttarget: deployMount.mountTarget,\n\t\t\t\t\t\t\t\t\treadonly: true,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tstopGraceSeconds,\n\t\t\t\t\t\t\tstopSignal: WALRUS_CLIENT_SERVICE_STOP_SIGNAL,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmapError: (cause) =>\n\t\t\t\t\t\t\twalrusPluginError(\n\t\t\t\t\t\t\t\trole,\n\t\t\t\t\t\t\t\t`walrus ${role} ensureContainer failed: ${cause.reason}: ${cause.detail}`,\n\t\t\t\t\t\t\t\t{ cause },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t});\n\t\t\t\tconst handle = yield* Scope.provide(ensureService, serviceStopScope);\n\n\t\t\t\tyield* setCurrentPluginPhase(\n\t\t\t\t\t`waiting for Walrus ${role} HTTP status on 127.0.0.1:${options.port}`,\n\t\t\t\t);\n\t\t\t\tyield* waitForProbe({\n\t\t\t\t\tlabel: `walrus.${role}`,\n\t\t\t\t\ttimeoutMs: readyTimeout,\n\t\t\t\t\tintervalMs: SERVICE_READY_PROBE_INTERVAL_MS,\n\t\t\t\t\tprobe: () =>\n\t\t\t\t\t\truntime\n\t\t\t\t\t\t\t.exec(handle, [\n\t\t\t\t\t\t\t\t'sh',\n\t\t\t\t\t\t\t\t'-c',\n\t\t\t\t\t\t\t\t`curl -fsS --max-time 2 http://127.0.0.1:${options.port}/status >/dev/null`,\n\t\t\t\t\t\t\t])\n\t\t\t\t\t\t\t.pipe(Effect.map(exitCodeProbeResult)),\n\t\t\t\t}).pipe(\n\t\t\t\t\tEffect.mapError((cause) => {\n\t\t\t\t\t\tif (cause instanceof ProbeTimeoutError) {\n\t\t\t\t\t\t\treturn walrusPluginError(\n\t\t\t\t\t\t\t\trole,\n\t\t\t\t\t\t\t\t`walrus ${role} never became ready within ${readyTimeout}ms ` +\n\t\t\t\t\t\t\t\t\t`(container=${containerName}, probe=http://127.0.0.1:${options.port}/status).`,\n\t\t\t\t\t\t\t\t{ cause: cause.lastError ?? cause.lastNotReady ?? cause },\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn walrusPluginError(\n\t\t\t\t\t\t\trole,\n\t\t\t\t\t\t\t`walrus ${role} ready-probe exec failed: ${cause.reason}: ${cause.detail}`,\n\t\t\t\t\t\t\t{ cause },\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\treturn { role, containerName, containerPort: options.port };\n\t\t\t});\n\n\t\tconst [aggregator, publisher] = yield* Effect.all(\n\t\t\t[\n\t\t\t\tspec.options.aggregator === null || spec.images.aggregator === null\n\t\t\t\t\t? Effect.succeed(null)\n\t\t\t\t\t: startOne('aggregator', spec.images.aggregator, spec.options.aggregator),\n\t\t\t\tspec.options.publisher === null || spec.images.publisher === null\n\t\t\t\t\t? Effect.succeed(null)\n\t\t\t\t\t: startOne('publisher', spec.images.publisher, spec.options.publisher),\n\t\t\t],\n\t\t\t{ concurrency: 'unbounded' },\n\t\t);\n\n\t\treturn { aggregator, publisher };\n\t});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client-services.mjs","names":[],"sources":["../../../src/plugins/walrus/client-services.ts"],"sourcesContent":["// Walrus local client-service lifecycle.\n//\n// These are the release-provided `walrus aggregator` / `walrus publisher`\n// subcommands plus the standalone `walrus-upload-relay` binary, wrapped only\n// enough to feed them the local deploy outputs and make them routable through\n// devstack.\n\nimport { Effect, Scope } from 'effect';\n\nimport type {\n\tContainerHandle,\n\tContainerRuntime,\n\tImageRef,\n} from '../../contracts/container-runtime.ts';\nimport { HOST_GATEWAY_EXTRA_HOSTS } from '../../substrate/runtime/host-gateway.ts';\nimport { ensureManagedContainer } from '../../substrate/runtime/managed-container.ts';\nimport {\n\tProbeTimeoutError,\n\texitCodeProbeResult,\n\twaitForProbe,\n} from '../../substrate/runtime/probes.ts';\nimport { setCurrentPluginPhase } from '../../substrate/runtime/current-plugin.ts';\nimport { walrusDeployMountPaths } from './deploy-paths.ts';\nimport { walrusPluginError, type WalrusPluginError } from './errors.ts';\n\nexport const DEFAULT_WALRUS_CLIENT_SERVICE_PORT = 31_415;\nexport const DEFAULT_WALRUS_UPLOAD_RELAY_SERVICE_PORT = 3_000;\nexport const DEFAULT_WALRUS_CLIENT_SERVICE_READY_TIMEOUT_MS = 60_000;\nexport const DEFAULT_WALRUS_CLIENT_SERVICE_STOP_GRACE_SECONDS = 10;\nexport const WALRUS_CLIENT_SERVICE_STOP_SIGNAL = 'SIGINT';\nexport const WALRUS_CLIENT_CONFIG_FILE = 'client_config.yaml';\nexport const WALRUS_CLIENT_WALLET_FILE = 'sui_client.yaml';\nexport const WALRUS_CLIENT_KEYSTORE_FILE = 'sui_client.keystore';\n\nexport type WalrusClientServiceRole = 'aggregator' | 'publisher' | 'upload-relay';\n\nexport interface WalrusClientServiceOptions {\n\treadonly port: number;\n}\n\nexport interface WalrusClientService {\n\treadonly role: WalrusClientServiceRole;\n\treadonly containerName: string;\n\treadonly containerPort: number;\n}\n\nexport interface WalrusClientServices {\n\treadonly aggregator: WalrusClientService | null;\n\treadonly publisher: WalrusClientService | null;\n\treadonly uploadRelay: WalrusClientService | null;\n}\n\nexport interface StartWalrusClientServicesSpec {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly walrusName: string;\n\treadonly images: {\n\t\treadonly aggregator: ImageRef | null;\n\t\treadonly publisher: ImageRef | null;\n\t\treadonly uploadRelay: ImageRef | null;\n\t};\n\treadonly options: {\n\t\treadonly aggregator: WalrusClientServiceOptions | null;\n\t\treadonly publisher: WalrusClientServiceOptions | null;\n\t\treadonly uploadRelay: WalrusClientServiceOptions | null;\n\t};\n\treadonly walrusNetworkName: string;\n\treadonly suiNetworkName: string;\n\treadonly deployHostMountPath: string;\n\treadonly stackRoot: string;\n\treadonly deployConfigHash: string;\n\treadonly suiRpcUrlInNetwork: string;\n\treadonly stopGraceSeconds?: number;\n\treadonly readyTimeoutMs?: number;\n}\n\nexport const walrusClientServiceContainerName = (parts: {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly walrusName: string;\n\treadonly role: WalrusClientServiceRole;\n}): string => `devstack-${parts.app}-${parts.stack}-walrus-${parts.walrusName}-${parts.role}`;\n\nexport const walrusClientServiceConfigHash = (parts: {\n\treadonly role: WalrusClientServiceRole;\n\treadonly deployConfigHash: string;\n\treadonly deploySourceHostPath: string;\n\treadonly deployMountTarget: string;\n\treadonly containerPort: number;\n\treadonly walrusNetworkName: string;\n\treadonly suiNetworkName: string;\n\treadonly suiRpcUrlInNetwork: string;\n}): string =>\n\t[\n\t\t'walrus-client-service-v2',\n\t\t`role=${parts.role}`,\n\t\tparts.deployConfigHash,\n\t\t`client=${WALRUS_CLIENT_CONFIG_FILE},${WALRUS_CLIENT_WALLET_FILE},${WALRUS_CLIENT_KEYSTORE_FILE}`,\n\t\t`mount=${parts.deploySourceHostPath}->${parts.deployMountTarget}`,\n\t\t`port=${parts.containerPort}`,\n\t\t`net=${parts.walrusNetworkName},${parts.suiNetworkName}`,\n\t\t`rpc=${parts.suiRpcUrlInNetwork}`,\n\t].join('|');\n\nconst SERVICE_READY_PROBE_INTERVAL_MS = 500;\n\nconst walrusClientServiceReadyPath = (role: WalrusClientServiceRole): string =>\n\trole === 'upload-relay' ? '/v1/tip-config' : '/status';\n\nexport const startWalrusClientServices = (\n\truntime: ContainerRuntime,\n\tspec: StartWalrusClientServicesSpec,\n): Effect.Effect<WalrusClientServices, WalrusPluginError, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tconst outerScope = yield* Effect.scope;\n\t\tconst serviceStopScope = yield* Scope.fork(outerScope, 'parallel');\n\t\tconst deployMount = walrusDeployMountPaths({\n\t\t\tstackRoot: spec.stackRoot,\n\t\t\tdeployOutputDirHostPath: spec.deployHostMountPath,\n\t\t\tmountTarget: '/opt/walrus/runtime',\n\t\t});\n\t\tconst readyTimeout = spec.readyTimeoutMs ?? DEFAULT_WALRUS_CLIENT_SERVICE_READY_TIMEOUT_MS;\n\t\tconst stopGraceSeconds =\n\t\t\tspec.stopGraceSeconds ?? DEFAULT_WALRUS_CLIENT_SERVICE_STOP_GRACE_SECONDS;\n\n\t\tconst startOne = (\n\t\t\trole: WalrusClientServiceRole,\n\t\t\timage: ImageRef,\n\t\t\toptions: WalrusClientServiceOptions,\n\t\t): Effect.Effect<WalrusClientService, WalrusPluginError, Scope.Scope> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst containerName = walrusClientServiceContainerName({\n\t\t\t\t\tapp: spec.app,\n\t\t\t\t\tstack: spec.stack,\n\t\t\t\t\twalrusName: spec.walrusName,\n\t\t\t\t\trole,\n\t\t\t\t});\n\n\t\t\t\tyield* setCurrentPluginPhase(`creating Walrus ${role} container ${containerName}`);\n\t\t\t\tconst ensureService: Effect.Effect<ContainerHandle, WalrusPluginError, Scope.Scope> =\n\t\t\t\t\tensureManagedContainer<WalrusPluginError>({\n\t\t\t\t\t\truntime,\n\t\t\t\t\t\tidentity: { app: spec.app, stack: spec.stack },\n\t\t\t\t\t\tplugin: 'walrus',\n\t\t\t\t\t\trole,\n\t\t\t\t\t\tspec: {\n\t\t\t\t\t\t\tname: containerName,\n\t\t\t\t\t\t\timage,\n\t\t\t\t\t\t\trecreate: 'on-config-change',\n\t\t\t\t\t\t\tconfigHash: walrusClientServiceConfigHash({\n\t\t\t\t\t\t\t\trole,\n\t\t\t\t\t\t\t\tdeployConfigHash: spec.deployConfigHash,\n\t\t\t\t\t\t\t\tdeploySourceHostPath: deployMount.sourceHostPath,\n\t\t\t\t\t\t\t\tdeployMountTarget: deployMount.mountTarget,\n\t\t\t\t\t\t\t\tcontainerPort: options.port,\n\t\t\t\t\t\t\t\twalrusNetworkName: spec.walrusNetworkName,\n\t\t\t\t\t\t\t\tsuiNetworkName: spec.suiNetworkName,\n\t\t\t\t\t\t\t\tsuiRpcUrlInNetwork: spec.suiRpcUrlInNetwork,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tenv: {\n\t\t\t\t\t\t\t\tDEPLOY_OUTPUT_DIR: deployMount.outputDirInContainer,\n\t\t\t\t\t\t\t\tSUI_RPC_URL: spec.suiRpcUrlInNetwork,\n\t\t\t\t\t\t\t\tWALRUS_CLIENT_SERVICE_BIND_ADDRESS: `0.0.0.0:${options.port}`,\n\t\t\t\t\t\t\t\tWALRUS_CONTAINER_PORT: String(options.port),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tcommand: [role],\n\t\t\t\t\t\t\tnetworkAttach: [spec.walrusNetworkName, spec.suiNetworkName],\n\t\t\t\t\t\t\textraHosts: HOST_GATEWAY_EXTRA_HOSTS,\n\t\t\t\t\t\t\tmounts: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tsource: deployMount.sourceHostPath,\n\t\t\t\t\t\t\t\t\ttarget: deployMount.mountTarget,\n\t\t\t\t\t\t\t\t\treadonly: true,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tstopGraceSeconds,\n\t\t\t\t\t\t\tstopSignal: WALRUS_CLIENT_SERVICE_STOP_SIGNAL,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmapError: (cause) =>\n\t\t\t\t\t\t\twalrusPluginError(\n\t\t\t\t\t\t\t\trole,\n\t\t\t\t\t\t\t\t`walrus ${role} ensureContainer failed: ${cause.reason}: ${cause.detail}`,\n\t\t\t\t\t\t\t\t{ cause },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t});\n\t\t\t\tconst handle = yield* Scope.provide(ensureService, serviceStopScope);\n\n\t\t\t\tyield* setCurrentPluginPhase(\n\t\t\t\t\t`waiting for Walrus ${role} HTTP status on 127.0.0.1:${options.port}`,\n\t\t\t\t);\n\t\t\t\tconst readyPath = walrusClientServiceReadyPath(role);\n\t\t\t\tyield* waitForProbe({\n\t\t\t\t\tlabel: `walrus.${role}`,\n\t\t\t\t\ttimeoutMs: readyTimeout,\n\t\t\t\t\tintervalMs: SERVICE_READY_PROBE_INTERVAL_MS,\n\t\t\t\t\tprobe: () =>\n\t\t\t\t\t\truntime\n\t\t\t\t\t\t\t.exec(handle, [\n\t\t\t\t\t\t\t\t'sh',\n\t\t\t\t\t\t\t\t'-c',\n\t\t\t\t\t\t\t\t`curl -fsS --max-time 2 http://127.0.0.1:${options.port}${readyPath} >/dev/null`,\n\t\t\t\t\t\t\t])\n\t\t\t\t\t\t\t.pipe(Effect.map(exitCodeProbeResult)),\n\t\t\t\t}).pipe(\n\t\t\t\t\tEffect.mapError((cause) => {\n\t\t\t\t\t\tif (cause instanceof ProbeTimeoutError) {\n\t\t\t\t\t\t\treturn walrusPluginError(\n\t\t\t\t\t\t\t\trole,\n\t\t\t\t\t\t\t\t`walrus ${role} never became ready within ${readyTimeout}ms ` +\n\t\t\t\t\t\t\t\t\t`(container=${containerName}, probe=http://127.0.0.1:${options.port}${readyPath}).`,\n\t\t\t\t\t\t\t\t{ cause: cause.lastError ?? cause.lastNotReady ?? cause },\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn walrusPluginError(\n\t\t\t\t\t\t\trole,\n\t\t\t\t\t\t\t`walrus ${role} ready-probe exec failed: ${cause.reason}: ${cause.detail}`,\n\t\t\t\t\t\t\t{ cause },\n\t\t\t\t\t\t);\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\treturn { role, containerName, containerPort: options.port };\n\t\t\t});\n\n\t\tconst [aggregator, publisher, uploadRelay] = yield* Effect.all(\n\t\t\t[\n\t\t\t\tspec.options.aggregator === null || spec.images.aggregator === null\n\t\t\t\t\t? Effect.succeed(null)\n\t\t\t\t\t: startOne('aggregator', spec.images.aggregator, spec.options.aggregator),\n\t\t\t\tspec.options.publisher === null || spec.images.publisher === null\n\t\t\t\t\t? Effect.succeed(null)\n\t\t\t\t\t: startOne('publisher', spec.images.publisher, spec.options.publisher),\n\t\t\t\tspec.options.uploadRelay === null || spec.images.uploadRelay === null\n\t\t\t\t\t? Effect.succeed(null)\n\t\t\t\t\t: startOne('upload-relay', spec.images.uploadRelay, spec.options.uploadRelay),\n\t\t\t],\n\t\t\t{ concurrency: 'unbounded' },\n\t\t);\n\n\t\treturn { aggregator, publisher, uploadRelay };\n\t});\n"],"mappings":"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"}
|
|
@@ -20,6 +20,7 @@ interface WalrusBindings {
|
|
|
20
20
|
readonly proxyUrl: string | null;
|
|
21
21
|
readonly aggregatorUrl: string | null;
|
|
22
22
|
readonly publisherUrl: string | null;
|
|
23
|
+
readonly uploadRelayUrl: string | null;
|
|
23
24
|
readonly nodes: ReadonlyArray<WalrusNodeBinding>;
|
|
24
25
|
}
|
|
25
26
|
//#endregion
|
|
@@ -75,6 +75,7 @@ const walrusConfigBindings = (structural) => {
|
|
|
75
75
|
field("proxyUrl", (i) => i.proxyUrl),
|
|
76
76
|
field("aggregatorUrl", (i) => i.aggregatorUrl),
|
|
77
77
|
field("publisherUrl", (i) => i.publisherUrl),
|
|
78
|
+
field("uploadRelayUrl", (i) => i.uploadRelayUrl),
|
|
78
79
|
nodesBinding
|
|
79
80
|
]
|
|
80
81
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen.mjs","names":[],"sources":["../../../src/plugins/walrus/codegen.ts"],"sourcesContent":["// Walrus plugin — Codegenable contribution, via the UNIFIED config-binding\n// declaration.\n//\n// Walrus's contribution is the SDK-ready `packageConfig` shape that the\n// `@mysten/walrus` SDK consumes — `{systemObjectId, stakingPoolId,\n// exchangeIds}` — plus the proxy / aggregator / publisher
|
|
1
|
+
{"version":3,"file":"codegen.mjs","names":[],"sources":["../../../src/plugins/walrus/codegen.ts"],"sourcesContent":["// Walrus plugin — Codegenable contribution, via the UNIFIED config-binding\n// declaration.\n//\n// Walrus's contribution is the SDK-ready `packageConfig` shape that the\n// `@mysten/walrus` SDK consumes — `{systemObjectId, stakingPoolId,\n// exchangeIds}` — plus the proxy / aggregator / publisher / upload-relay URLs\n// for HTTP consumers. Walrus is single-instance per stack, so it exports `walrus`\n// directly (a FLAT bucket, not name-keyed like coin/seal).\n//\n// ONE declaration, TWO derivations (see `contracts/config-bindings.ts`):\n// - LIVE (boot): bakes the resolved ids / URLs into the ephemeral tree AND\n// feeds the generic deployment `values` channel.\n// - STATIC (committed tree): emits `requireValue(dep, 'walrus', '<key>')` so the\n// committed `walrus.ts` carries NO baked object id / endpoint URL.\n//\n// STRUCTURAL fields (`mode`, `network`) stay literals; the on-chain ids,\n// coin type, endpoint URLs, and the storage-node committee are RUNTIME\n// (loaded config data). The composite `packageConfig` / `nodes` values are\n// resolved as whole-value blobs (the array/optional shapes don't split into\n// the flat config-path model).\n\nimport type { CodegenableDecl } from '../../contracts/codegenable.ts';\nimport {\n\tconfigCodegenable,\n\ttype ConfigBinding,\n\ttype ConfigBindingSet,\n} from '../../contracts/config-bindings.ts';\nimport type { JsonValue } from '../../orchestrators/codegen/deployment.ts';\n\n/** Per-node descriptor. */\nexport interface WalrusNodeBinding {\n\treadonly nodeIndex: number;\n\treadonly publicHostname: string;\n\treadonly rpcUrl: string;\n}\n\n/** The typed shape the emitted file exports. */\nexport interface WalrusBindings {\n\treadonly mode: 'local' | 'known';\n\treadonly network: string;\n\treadonly walrusPackageId: string | null;\n\treadonly walPackageId: string | null;\n\treadonly walCoinType: string | null;\n\treadonly packageConfig: {\n\t\treadonly systemObjectId: string;\n\t\treadonly stakingPoolId: string;\n\t\treadonly exchangeIds?: ReadonlyArray<string>;\n\t};\n\treadonly proxyUrl: string | null;\n\treadonly aggregatorUrl: string | null;\n\treadonly publisherUrl: string | null;\n\treadonly uploadRelayUrl: string | null;\n\treadonly nodes: ReadonlyArray<WalrusNodeBinding>;\n}\n\n/** Inputs to the LIVE codegen contribution — supplied at acquire-time. */\nexport interface MakeCodegenableInputs {\n\treadonly mode: 'local' | 'known';\n\treadonly network: string;\n\treadonly walrusPackageId: string | null;\n\treadonly walPackageId: string | null;\n\treadonly walCoinType: string | null;\n\treadonly systemObjectId: string;\n\treadonly stakingPoolId: string;\n\treadonly exchangeIds: ReadonlyArray<string>;\n\treadonly proxyUrl: string | null;\n\treadonly aggregatorUrl: string | null;\n\treadonly publisherUrl: string | null;\n\treadonly uploadRelayUrl: string | null;\n\treadonly nodes: ReadonlyArray<WalrusNodeBinding>;\n}\n\n/** User-declared known-deployment ids / URLs, available at factory time. A\n * KNOWN deployment's values are DECLARED config (not loaded-at-runtime data),\n * so the committed `walrus.ts` bakes them as LITERALS. Absent for a `local`\n * (dev-deployed) cluster whose ids/URLs are dynamic. */\nexport interface WalrusKnownConfig {\n\treadonly walrusPackageId: string | null;\n\treadonly walPackageId: string | null;\n\treadonly walCoinType: string | null;\n\treadonly packageConfig: WalrusBindings['packageConfig'];\n\treadonly proxyUrl: string | null;\n\treadonly aggregatorUrl: string | null;\n\treadonly publisherUrl: string | null;\n\treadonly uploadRelayUrl: string | null;\n\treadonly nodes: ReadonlyArray<WalrusNodeBinding>;\n}\n\n/** Static-config shape — what walrus knows BEFORE acquire. When `known` is\n * present (known mode), its declared ids/URLs are baked as literals;\n * otherwise (local mode) every id/URL resolves at app build/dev time. */\nexport interface WalrusStaticConfig {\n\treadonly mode: 'local' | 'known';\n\treadonly network: string;\n\treadonly known?: WalrusKnownConfig;\n}\n\nconst NAMESPACE = 'walrus';\n\n/** TS source-type strings for the resolved walrus fields — keeps the committed\n * `walrus.ts` typed as `WalrusBindings` declares (the generic `requireValue`\n * channel would otherwise return `unknown`). Composite blobs inline their\n * structural literal types so no emitted type-import is needed. */\nconst PACKAGE_CONFIG_TS_TYPE =\n\t'{ readonly systemObjectId: string; readonly stakingPoolId: string; readonly exchangeIds?: ReadonlyArray<string> }';\nconst NODES_TS_TYPE =\n\t'ReadonlyArray<{ readonly nodeIndex: number; readonly publicHostname: string; readonly rpcUrl: string }>';\n\n/** The walrus config bindings, declared ONCE. `mode` / `network` are\n * structural literals; every id / coin type / URL / committee value is a\n * RESOLVED binding on the generic `requireValue(dep, 'walrus', '<key>')` channel.\n * Both the live boot decl and the static committed-tree decl derive from it. */\nconst walrusConfigBindings = (\n\tstructural: WalrusStaticConfig,\n): ConfigBindingSet<MakeCodegenableInputs> => {\n\tconst known = structural.known;\n\t// A known deployment's declared ids/URLs are config (literal); a local\n\t// cluster's are dynamically deployed (resolved at app build/dev time).\n\tconst field = (\n\t\tkey:\n\t\t\t| 'walrusPackageId'\n\t\t\t| 'walPackageId'\n\t\t\t| 'walCoinType'\n\t\t\t| 'proxyUrl'\n\t\t\t| 'aggregatorUrl'\n\t\t\t| 'publisherUrl'\n\t\t\t| 'uploadRelayUrl',\n\t\tlive: (i: MakeCodegenableInputs) => JsonValue,\n\t): ConfigBinding<MakeCodegenableInputs> =>\n\t\tknown !== undefined\n\t\t\t? { variant: 'literal', configPath: [key], value: known[key] }\n\t\t\t: {\n\t\t\t\t\tvariant: 'resolved',\n\t\t\t\t\tconfigPath: [key],\n\t\t\t\t\tnamespace: NAMESPACE,\n\t\t\t\t\tkey,\n\t\t\t\t\ttsType: 'string | null',\n\t\t\t\t\tlive,\n\t\t\t\t};\n\tconst packageConfigBinding: ConfigBinding<MakeCodegenableInputs> =\n\t\tknown !== undefined\n\t\t\t? {\n\t\t\t\t\tvariant: 'literal',\n\t\t\t\t\tconfigPath: ['packageConfig'],\n\t\t\t\t\tvalue: known.packageConfig as JsonValue,\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tvariant: 'resolved',\n\t\t\t\t\tconfigPath: ['packageConfig'],\n\t\t\t\t\tnamespace: NAMESPACE,\n\t\t\t\t\tkey: 'packageConfig',\n\t\t\t\t\ttsType: PACKAGE_CONFIG_TS_TYPE,\n\t\t\t\t\tlive: (i) =>\n\t\t\t\t\t\t({\n\t\t\t\t\t\t\tsystemObjectId: i.systemObjectId,\n\t\t\t\t\t\t\tstakingPoolId: i.stakingPoolId,\n\t\t\t\t\t\t\t...(i.exchangeIds.length > 0 ? { exchangeIds: [...i.exchangeIds] } : {}),\n\t\t\t\t\t\t}) as JsonValue,\n\t\t\t\t};\n\tconst nodesBinding: ConfigBinding<MakeCodegenableInputs> =\n\t\tknown !== undefined\n\t\t\t? { variant: 'literal', configPath: ['nodes'], value: known.nodes as unknown as JsonValue }\n\t\t\t: {\n\t\t\t\t\tvariant: 'resolved',\n\t\t\t\t\tconfigPath: ['nodes'],\n\t\t\t\t\tnamespace: NAMESPACE,\n\t\t\t\t\tkey: 'nodes',\n\t\t\t\t\ttsType: NODES_TS_TYPE,\n\t\t\t\t\tlive: (i) => i.nodes as unknown as JsonValue,\n\t\t\t\t};\n\tconst bindings: ReadonlyArray<ConfigBinding<MakeCodegenableInputs>> = [\n\t\t{ variant: 'literal', configPath: ['mode'], value: structural.mode },\n\t\t{ variant: 'literal', configPath: ['network'], value: structural.network },\n\t\tfield('walrusPackageId', (i) => i.walrusPackageId),\n\t\tfield('walPackageId', (i) => i.walPackageId),\n\t\tfield('walCoinType', (i) => i.walCoinType),\n\t\tpackageConfigBinding,\n\t\tfield('proxyUrl', (i) => i.proxyUrl),\n\t\tfield('aggregatorUrl', (i) => i.aggregatorUrl),\n\t\tfield('publisherUrl', (i) => i.publisherUrl),\n\t\tfield('uploadRelayUrl', (i) => i.uploadRelayUrl),\n\t\tnodesBinding,\n\t];\n\treturn {\n\t\tbucket: 'walrus.ts',\n\t\tkind: 'walrus',\n\t\temitterName: 'walrus-network',\n\t\tbindings,\n\t};\n};\n\n/** Construct the LIVE Codegenable contribution. Bakes the resolved ids /\n * URLs into the ephemeral tree + feeds the generic deployment `values`\n * channel. */\nexport const makeCodegenable = (inputs: MakeCodegenableInputs): CodegenableDecl =>\n\tconfigCodegenable(walrusConfigBindings({ mode: inputs.mode, network: inputs.network }), {\n\t\tmode: 'live',\n\t\tstate: inputs,\n\t});\n\n/** Construct the STATIC (stack-free) Codegenable contribution. Emits\n * `requireValue(dep, 'walrus', '<key>')` for the runtime fields; the committed\n * `walrus.ts` carries no baked object id / endpoint URL. */\nexport const makeWalrusStaticCodegen = (config: WalrusStaticConfig): CodegenableDecl =>\n\tconfigCodegenable(walrusConfigBindings(config), 'static');\n"],"mappings":";;AAiGA,MAAM,YAAY;;;;;AAMlB,MAAM,yBACL;AACD,MAAM,gBACL;;;;;AAMD,MAAM,wBACL,eAC6C;CAC7C,MAAM,QAAQ,WAAW;CAGzB,MAAM,SACL,KAQA,SAEA,UAAU,KAAA,IACP;EAAE,SAAS;EAAW,YAAY,CAAC,GAAG;EAAG,OAAO,MAAM;CAAK,IAC3D;EACA,SAAS;EACT,YAAY,CAAC,GAAG;EAChB,WAAW;EACX;EACA,QAAQ;EACR;CACD;CACH,MAAM,uBACL,UAAU,KAAA,IACP;EACA,SAAS;EACT,YAAY,CAAC,eAAe;EAC5B,OAAO,MAAM;CACd,IACC;EACA,SAAS;EACT,YAAY,CAAC,eAAe;EAC5B,WAAW;EACX,KAAK;EACL,QAAQ;EACR,OAAO,OACL;GACA,gBAAgB,EAAE;GAClB,eAAe,EAAE;GACjB,GAAI,EAAE,YAAY,SAAS,IAAI,EAAE,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,CAAC;EACvE;CACF;CACH,MAAM,eACL,UAAU,KAAA,IACP;EAAE,SAAS;EAAW,YAAY,CAAC,OAAO;EAAG,OAAO,MAAM;CAA8B,IACxF;EACA,SAAS;EACT,YAAY,CAAC,OAAO;EACpB,WAAW;EACX,KAAK;EACL,QAAQ;EACR,OAAO,MAAM,EAAE;CAChB;CAcH,OAAO;EACN,QAAQ;EACR,MAAM;EACN,aAAa;EACb,UAAA;GAhBA;IAAE,SAAS;IAAW,YAAY,CAAC,MAAM;IAAG,OAAO,WAAW;GAAK;GACnE;IAAE,SAAS;IAAW,YAAY,CAAC,SAAS;IAAG,OAAO,WAAW;GAAQ;GACzE,MAAM,oBAAoB,MAAM,EAAE,eAAe;GACjD,MAAM,iBAAiB,MAAM,EAAE,YAAY;GAC3C,MAAM,gBAAgB,MAAM,EAAE,WAAW;GACzC;GACA,MAAM,aAAa,MAAM,EAAE,QAAQ;GACnC,MAAM,kBAAkB,MAAM,EAAE,aAAa;GAC7C,MAAM,iBAAiB,MAAM,EAAE,YAAY;GAC3C,MAAM,mBAAmB,MAAM,EAAE,cAAc;GAC/C;EAMO;CACR;AACD;;;;AAKA,MAAa,mBAAmB,WAC/B,kBAAkB,qBAAqB;CAAE,MAAM,OAAO;CAAM,SAAS,OAAO;AAAQ,CAAC,GAAG;CACvF,MAAM;CACN,OAAO;AACR,CAAC;;;;AAKF,MAAa,2BAA2B,WACvC,kBAAkB,qBAAqB,MAAM,GAAG,QAAQ"}
|
|
@@ -9,7 +9,7 @@ import { ForkIncompatibleError } from "../../substrate/runtime/mode-errors.mjs";
|
|
|
9
9
|
* creation now, so its errors surface as `ContainerRuntimeError`
|
|
10
10
|
* upstream — we narrate the *phase* as `'cluster-network'` but the
|
|
11
11
|
* fault tags through the substrate). */
|
|
12
|
-
type WalrusPhase = 'image-build' | 'cluster-network' | 'deploy' | 'aggregator' | 'publisher' | 'exchange' | 'storage-node' | 'proxy' | 'fund-wal' | 'register-known';
|
|
12
|
+
type WalrusPhase = 'image-build' | 'cluster-network' | 'deploy' | 'aggregator' | 'publisher' | 'upload-relay' | 'exchange' | 'storage-node' | 'proxy' | 'fund-wal' | 'register-known';
|
|
13
13
|
/** Generic Walrus plugin error. Raised by the plugin's acquire
|
|
14
14
|
* body, WAL funding strategy, and the per-mode builders. */
|
|
15
15
|
interface WalrusPluginError {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/plugins/walrus/errors.ts"],"sourcesContent":["// Walrus plugin — typed errors.\n//\n// Errors raised and consumed inside the Walrus plugin live here.\n// Cross-service errors that Walrus *consumes* but the substrate\n// raises (e.g. `ArtifactPublishError`) come from the substrate's\n// primitive — we don't redeclare those.\n//\n// `ForkIncompatibleError` is a cross-cutting mode-refusal shape\n// owned by `substrate/runtime/mode-errors.ts`; walrus contributes\n// the `walrusLocalCluster` variant via the factory below.\n//\n// Effect v4: plain interfaces with `_tag` discriminator (per\n// surrounding subsystem style). `Effect.catchTag` matches on `_tag`.\n\nimport { ForkIncompatibleError } from '../../substrate/runtime/mode-errors.ts';\nimport { defineConfigError, type ConfigIssue } from '../../substrate/runtime/config-validation.ts';\n\nexport { ForkIncompatibleError };\n\n/** Phases for `WalrusError`. Closed sum — keeps the cause-walker's\n * display table small. Matches the closed `WalrusPhases` from the\n * distilled doc §\"Cross-component references\" except `'network'`\n * is dropped (the substrate's `ContainerRuntime` owns docker network\n * creation now, so its errors surface as `ContainerRuntimeError`\n * upstream — we narrate the *phase* as `'cluster-network'` but the\n * fault tags through the substrate). */\nexport type WalrusPhase =\n\t| 'image-build'\n\t| 'cluster-network'\n\t| 'deploy'\n\t| 'aggregator'\n\t| 'publisher'\n\t| 'exchange'\n\t| 'storage-node'\n\t| 'proxy'\n\t| 'fund-wal'\n\t| 'register-known';\n\n/** Generic Walrus plugin error. Raised by the plugin's acquire\n * body, WAL funding strategy, and the per-mode builders. */\nexport interface WalrusPluginError {\n\treadonly _tag: 'WalrusPluginError';\n\treadonly phase: WalrusPhase;\n\treadonly message: string;\n\treadonly cause?: unknown;\n\t/** Optional sub-process capture envelope — populated for deploy\n\t * one-shot + per-node container failures. */\n\treadonly stderr?: string;\n\treadonly stdout?: string;\n\treadonly exitCode?: number;\n}\n\nexport const walrusPluginError = (\n\tphase: WalrusPhase,\n\tmessage: string,\n\tparts: Omit<WalrusPluginError, '_tag' | 'phase' | 'message'> = {},\n): WalrusPluginError => ({ _tag: 'WalrusPluginError', phase, message, ...parts });\n\n/** Configuration error — synchronous factory-time guards\n * (`nodeCount >= 1`, `shards >= nodeCount`, missing required\n * fields on `.known(...)`). Surfaces as a thrown `Error` shaped\n * like this in the factory, mirroring the distilled-doc behavior\n * of synchronous configuration faults. */\nexport interface WalrusConfigError extends ConfigIssue {\n\treadonly _tag: 'WalrusConfigError';\n}\n\nconst makeWalrusConfigError = defineConfigError('WalrusConfigError');\n\nexport const walrusConfigError = (\n\tfield: string,\n\tmessage: string,\n\thint?: string,\n\tcause?: unknown,\n): WalrusConfigError => makeWalrusConfigError({ field, message, hint, cause });\n\n/** Union of every error a Walrus-plugin caller may encounter. */\nexport type WalrusError = WalrusPluginError | ForkIncompatibleError | WalrusConfigError;\n\n/** The catchable error tags this plugin exposes. Pinned against the\n * user-facing error catalog by the error-catalog-parity test. */\nexport const WALRUS_ERROR_TAGS: ReadonlyArray<WalrusError['_tag']> = [\n\t'WalrusPluginError',\n\t'ForkIncompatibleError',\n\t'WalrusConfigError',\n] as const;\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/plugins/walrus/errors.ts"],"sourcesContent":["// Walrus plugin — typed errors.\n//\n// Errors raised and consumed inside the Walrus plugin live here.\n// Cross-service errors that Walrus *consumes* but the substrate\n// raises (e.g. `ArtifactPublishError`) come from the substrate's\n// primitive — we don't redeclare those.\n//\n// `ForkIncompatibleError` is a cross-cutting mode-refusal shape\n// owned by `substrate/runtime/mode-errors.ts`; walrus contributes\n// the `walrusLocalCluster` variant via the factory below.\n//\n// Effect v4: plain interfaces with `_tag` discriminator (per\n// surrounding subsystem style). `Effect.catchTag` matches on `_tag`.\n\nimport { ForkIncompatibleError } from '../../substrate/runtime/mode-errors.ts';\nimport { defineConfigError, type ConfigIssue } from '../../substrate/runtime/config-validation.ts';\n\nexport { ForkIncompatibleError };\n\n/** Phases for `WalrusError`. Closed sum — keeps the cause-walker's\n * display table small. Matches the closed `WalrusPhases` from the\n * distilled doc §\"Cross-component references\" except `'network'`\n * is dropped (the substrate's `ContainerRuntime` owns docker network\n * creation now, so its errors surface as `ContainerRuntimeError`\n * upstream — we narrate the *phase* as `'cluster-network'` but the\n * fault tags through the substrate). */\nexport type WalrusPhase =\n\t| 'image-build'\n\t| 'cluster-network'\n\t| 'deploy'\n\t| 'aggregator'\n\t| 'publisher'\n\t| 'upload-relay'\n\t| 'exchange'\n\t| 'storage-node'\n\t| 'proxy'\n\t| 'fund-wal'\n\t| 'register-known';\n\n/** Generic Walrus plugin error. Raised by the plugin's acquire\n * body, WAL funding strategy, and the per-mode builders. */\nexport interface WalrusPluginError {\n\treadonly _tag: 'WalrusPluginError';\n\treadonly phase: WalrusPhase;\n\treadonly message: string;\n\treadonly cause?: unknown;\n\t/** Optional sub-process capture envelope — populated for deploy\n\t * one-shot + per-node container failures. */\n\treadonly stderr?: string;\n\treadonly stdout?: string;\n\treadonly exitCode?: number;\n}\n\nexport const walrusPluginError = (\n\tphase: WalrusPhase,\n\tmessage: string,\n\tparts: Omit<WalrusPluginError, '_tag' | 'phase' | 'message'> = {},\n): WalrusPluginError => ({ _tag: 'WalrusPluginError', phase, message, ...parts });\n\n/** Configuration error — synchronous factory-time guards\n * (`nodeCount >= 1`, `shards >= nodeCount`, missing required\n * fields on `.known(...)`). Surfaces as a thrown `Error` shaped\n * like this in the factory, mirroring the distilled-doc behavior\n * of synchronous configuration faults. */\nexport interface WalrusConfigError extends ConfigIssue {\n\treadonly _tag: 'WalrusConfigError';\n}\n\nconst makeWalrusConfigError = defineConfigError('WalrusConfigError');\n\nexport const walrusConfigError = (\n\tfield: string,\n\tmessage: string,\n\thint?: string,\n\tcause?: unknown,\n): WalrusConfigError => makeWalrusConfigError({ field, message, hint, cause });\n\n/** Union of every error a Walrus-plugin caller may encounter. */\nexport type WalrusError = WalrusPluginError | ForkIncompatibleError | WalrusConfigError;\n\n/** The catchable error tags this plugin exposes. Pinned against the\n * user-facing error catalog by the error-catalog-parity test. */\nexport const WALRUS_ERROR_TAGS: ReadonlyArray<WalrusError['_tag']> = [\n\t'WalrusPluginError',\n\t'ForkIncompatibleError',\n\t'WalrusConfigError',\n] as const;\n"],"mappings":";;;AAqDA,MAAa,qBACZ,OACA,SACA,QAA+D,CAAC,OACxC;CAAE,MAAM;CAAqB;CAAO;CAAS,GAAG;AAAM;AAW/E,MAAM,wBAAwB,kBAAkB,mBAAmB;AAEnE,MAAa,qBACZ,OACA,SACA,MACA,UACuB,sBAAsB;CAAE;CAAO;CAAS;CAAM;AAAM,CAAC"}
|
|
@@ -28,6 +28,7 @@ interface WalrusResolved {
|
|
|
28
28
|
readonly proxyUrl: string | null;
|
|
29
29
|
readonly aggregatorUrl: string | null;
|
|
30
30
|
readonly publisherUrl: string | null;
|
|
31
|
+
readonly uploadRelayUrl: string | null;
|
|
31
32
|
readonly walFaucetStrategy: WalFaucetStrategy | null;
|
|
32
33
|
readonly walCoinType: string | null;
|
|
33
34
|
}
|
|
@@ -96,6 +97,7 @@ declare const walrusFor: ModeNamespace<{
|
|
|
96
97
|
proxyUrl: string | null;
|
|
97
98
|
aggregatorUrl: string | null;
|
|
98
99
|
publisherUrl: string | null;
|
|
100
|
+
uploadRelayUrl: string | null;
|
|
99
101
|
walFaucetStrategy: null;
|
|
100
102
|
walCoinType: null;
|
|
101
103
|
}, readonly [ResourceRef<"sui", SuiClient & {
|
|
@@ -115,6 +117,7 @@ declare const walrusFor: ModeNamespace<{
|
|
|
115
117
|
proxyUrl: string | null;
|
|
116
118
|
aggregatorUrl: string | null;
|
|
117
119
|
publisherUrl: string | null;
|
|
120
|
+
uploadRelayUrl: string | null;
|
|
118
121
|
walFaucetStrategy: null;
|
|
119
122
|
walCoinType: null;
|
|
120
123
|
}, readonly [ResourceRef<"sui", SuiClient & {
|
|
@@ -134,6 +137,7 @@ declare const walrusFor: ModeNamespace<{
|
|
|
134
137
|
proxyUrl: string | null;
|
|
135
138
|
aggregatorUrl: string | null;
|
|
136
139
|
publisherUrl: string | null;
|
|
140
|
+
uploadRelayUrl: string | null;
|
|
137
141
|
walFaucetStrategy: null;
|
|
138
142
|
walCoinType: null;
|
|
139
143
|
}, readonly [ResourceRef<"sui", SuiClient & {
|
|
@@ -155,6 +159,7 @@ declare const walrusFor: ModeNamespace<{
|
|
|
155
159
|
proxyUrl: string | null;
|
|
156
160
|
aggregatorUrl: string | null;
|
|
157
161
|
publisherUrl: string | null;
|
|
162
|
+
uploadRelayUrl: string | null;
|
|
158
163
|
walFaucetStrategy: null;
|
|
159
164
|
walCoinType: null;
|
|
160
165
|
}, readonly [ResourceRef<"sui", SuiClient & {
|
|
@@ -174,6 +179,7 @@ declare const walrusFor: ModeNamespace<{
|
|
|
174
179
|
proxyUrl: string | null;
|
|
175
180
|
aggregatorUrl: string | null;
|
|
176
181
|
publisherUrl: string | null;
|
|
182
|
+
uploadRelayUrl: string | null;
|
|
177
183
|
walFaucetStrategy: null;
|
|
178
184
|
walCoinType: null;
|
|
179
185
|
}, readonly [ResourceRef<"sui", SuiClient & {
|
|
@@ -193,6 +199,7 @@ declare const walrusFor: ModeNamespace<{
|
|
|
193
199
|
proxyUrl: string | null;
|
|
194
200
|
aggregatorUrl: string | null;
|
|
195
201
|
publisherUrl: string | null;
|
|
202
|
+
uploadRelayUrl: string | null;
|
|
196
203
|
walFaucetStrategy: null;
|
|
197
204
|
walCoinType: null;
|
|
198
205
|
}, readonly [ResourceRef<"sui", SuiClient & {
|
|
@@ -214,6 +221,7 @@ declare const walrusFor: ModeNamespace<{
|
|
|
214
221
|
proxyUrl: string | null;
|
|
215
222
|
aggregatorUrl: string | null;
|
|
216
223
|
publisherUrl: string | null;
|
|
224
|
+
uploadRelayUrl: string | null;
|
|
217
225
|
walFaucetStrategy: null;
|
|
218
226
|
walCoinType: null;
|
|
219
227
|
}, readonly [ResourceRef<"sui", SuiClient & {
|
|
@@ -233,6 +241,7 @@ declare const walrusFor: ModeNamespace<{
|
|
|
233
241
|
proxyUrl: string | null;
|
|
234
242
|
aggregatorUrl: string | null;
|
|
235
243
|
publisherUrl: string | null;
|
|
244
|
+
uploadRelayUrl: string | null;
|
|
236
245
|
walFaucetStrategy: null;
|
|
237
246
|
walCoinType: null;
|
|
238
247
|
}, readonly [ResourceRef<"sui", SuiClient & {
|
|
@@ -252,6 +261,7 @@ declare const walrusFor: ModeNamespace<{
|
|
|
252
261
|
proxyUrl: string | null;
|
|
253
262
|
aggregatorUrl: string | null;
|
|
254
263
|
publisherUrl: string | null;
|
|
264
|
+
uploadRelayUrl: string | null;
|
|
255
265
|
walFaucetStrategy: null;
|
|
256
266
|
walCoinType: null;
|
|
257
267
|
}, readonly [ResourceRef<"sui", SuiClient & {
|
|
@@ -11,7 +11,7 @@ import { LOCAL_NETWORK_NAME } from "../../api/inference-network.mjs";
|
|
|
11
11
|
import { renderUrl, routedHostname } from "../../substrate/runtime/routed-url.mjs";
|
|
12
12
|
import { walrusPluginError } from "./errors.mjs";
|
|
13
13
|
import { WALRUS_ROUTER_PORT, buildWalrusNetworkName } from "./storage-nodes.mjs";
|
|
14
|
-
import { WALRUS_AGGREGATOR_ENDPOINT_NAME, WALRUS_PUBLISHER_ENDPOINT_NAME, makeLocalRoutables } from "./routable.mjs";
|
|
14
|
+
import { WALRUS_AGGREGATOR_ENDPOINT_NAME, WALRUS_PUBLISHER_ENDPOINT_NAME, WALRUS_UPLOAD_RELAY_ENDPOINT_NAME, makeLocalRoutables } from "./routable.mjs";
|
|
15
15
|
import { defineModeNamespace } from "../../api/mode-narrowed-factory.mjs";
|
|
16
16
|
import { suiResource } from "../sui/index.mjs";
|
|
17
17
|
import { coinResourceId } from "../coin/index.mjs";
|
|
@@ -110,6 +110,7 @@ const buildLocalPlugin = (opts) => {
|
|
|
110
110
|
})), Effect.mapError((cause) => walrusPluginError("proxy", `walrus route URL assembly failed for ${role}: ${cause.detail}`, { cause })));
|
|
111
111
|
const aggregatorUrl = boot.clientServices.aggregator === null ? null : yield* serviceUrl(WALRUS_AGGREGATOR_ENDPOINT_NAME);
|
|
112
112
|
const publisherUrl = boot.clientServices.publisher === null ? null : yield* serviceUrl(WALRUS_PUBLISHER_ENDPOINT_NAME);
|
|
113
|
+
const uploadRelayUrl = boot.clientServices.uploadRelay === null ? null : yield* serviceUrl(WALRUS_UPLOAD_RELAY_ENDPOINT_NAME);
|
|
113
114
|
const resolvedValue = {
|
|
114
115
|
mode: "local",
|
|
115
116
|
network: identity.network,
|
|
@@ -120,11 +121,16 @@ const buildLocalPlugin = (opts) => {
|
|
|
120
121
|
proxyUrl: aggregatorUrl,
|
|
121
122
|
aggregatorUrl,
|
|
122
123
|
publisherUrl,
|
|
124
|
+
uploadRelayUrl,
|
|
123
125
|
walFaucetStrategy: boot.walFaucetStrategy,
|
|
124
126
|
walCoinType: boot.walCoinType
|
|
125
127
|
};
|
|
126
128
|
const walFaucetContribution = resolvedValue.walFaucetStrategy === null || resolvedValue.walCoinType === null ? [] : [makeWalFaucetContribution(resolvedValue.walFaucetStrategy, resolvedValue.walCoinType)];
|
|
127
|
-
const clientServiceRoles = [
|
|
129
|
+
const clientServiceRoles = [
|
|
130
|
+
...boot.clientServices.aggregator === null ? [] : ["aggregator"],
|
|
131
|
+
...boot.clientServices.publisher === null ? [] : ["publisher"],
|
|
132
|
+
...boot.clientServices.uploadRelay === null ? [] : ["upload-relay"]
|
|
133
|
+
];
|
|
128
134
|
emitContributions(ctx, [
|
|
129
135
|
makeSnapshotable("local", identity.app, identity.stack, resolved.name, resolvedValue.network, resolved.nodeCount, clientServiceRoles),
|
|
130
136
|
makeCodegenable({
|
|
@@ -139,6 +145,7 @@ const buildLocalPlugin = (opts) => {
|
|
|
139
145
|
proxyUrl: resolvedValue.proxyUrl,
|
|
140
146
|
aggregatorUrl: resolvedValue.aggregatorUrl,
|
|
141
147
|
publisherUrl: resolvedValue.publisherUrl,
|
|
148
|
+
uploadRelayUrl: resolvedValue.uploadRelayUrl,
|
|
142
149
|
nodes: resolvedValue.nodes
|
|
143
150
|
}),
|
|
144
151
|
{
|
|
@@ -156,7 +163,8 @@ const buildLocalPlugin = (opts) => {
|
|
|
156
163
|
nodeCount: resolved.nodeCount,
|
|
157
164
|
containerApiPort: resolved.containerApiPort,
|
|
158
165
|
aggregator: boot.clientServices.aggregator,
|
|
159
|
-
publisher: boot.clientServices.publisher
|
|
166
|
+
publisher: boot.clientServices.publisher,
|
|
167
|
+
uploadRelay: boot.clientServices.uploadRelay
|
|
160
168
|
})
|
|
161
169
|
]);
|
|
162
170
|
return resolvedValue;
|
|
@@ -185,6 +193,7 @@ const buildKnownPlugin = (opts) => {
|
|
|
185
193
|
proxyUrl: resolved.proxyUrl,
|
|
186
194
|
aggregatorUrl: resolved.aggregatorUrl,
|
|
187
195
|
publisherUrl: resolved.publisherUrl,
|
|
196
|
+
uploadRelayUrl: resolved.uploadRelayUrl,
|
|
188
197
|
nodes: resolved.nodes
|
|
189
198
|
}
|
|
190
199
|
})],
|
|
@@ -205,6 +214,7 @@ const buildKnownPlugin = (opts) => {
|
|
|
205
214
|
proxyUrl: resolved.proxyUrl,
|
|
206
215
|
aggregatorUrl: resolved.aggregatorUrl,
|
|
207
216
|
publisherUrl: resolved.publisherUrl,
|
|
217
|
+
uploadRelayUrl: resolved.uploadRelayUrl,
|
|
208
218
|
walFaucetStrategy: null,
|
|
209
219
|
walCoinType: null
|
|
210
220
|
};
|
|
@@ -221,6 +231,7 @@ const buildKnownPlugin = (opts) => {
|
|
|
221
231
|
proxyUrl: resolvedValue.proxyUrl,
|
|
222
232
|
aggregatorUrl: resolvedValue.aggregatorUrl,
|
|
223
233
|
publisherUrl: resolvedValue.publisherUrl,
|
|
234
|
+
uploadRelayUrl: resolvedValue.uploadRelayUrl,
|
|
224
235
|
nodes: resolvedValue.nodes
|
|
225
236
|
}));
|
|
226
237
|
ctx.provides({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/walrus/index.ts"],"sourcesContent":["// Walrus plugin — barrel + factories.\n//\n// Architecture: Walrus is a service plugin that owns a local cluster\n// or describes a known deployment. The factory at this file folds the\n// four modes behind:\n//\n// - `walrus(opts?)` — local-cluster shorthand. No env\n// defaulting; use `walrusFor(...)`\n// for known deployments.\n// - `walrusFor(network)` — mode-narrowed factory namespace\n// (architecture Tension 11). Returns\n// `{ local: …, known: … }` narrowed\n// to the network's mode. Crucially,\n// the fork branch exposes ONLY\n// `.known` — `.local` is a compile\n// error on a fork-mode network.\n//\n// During `start`, the plugin emits (via the typed `ctx.*` verbs, per mode):\n//\n// Local mode (full local cluster):\n// 1. `ctx.snapshotExtra` — runtime/walrus/<name>/deploy/ subtree\n// + storage-node managed containers.\n// 2. `ctx.codegen` — `walrus-network` bindings.\n// 3. `ctx.endpoint` × (N+2) — per-node + aggregator + publisher.\n// 4. `ctx.provides` walrus-state-registry — local entry.\n// 5. `ctx.provides` endpoint-registry — N+2 entries.\n// 6. `ctx.provides` package-registry — `walrus.<name>`.\n// 7. `ctx.provides` coinType:<WAL fullCoinType> — WAL faucet\n// strategy\n// (when exchange exists).\n//\n// Known mode (read-only deployment):\n// 1. `ctx.snapshotExtra` — identity-guard only; no subtrees.\n// 2. `ctx.codegen` — `walrus-network` bindings (mode='known').\n// 3. `ctx.provides` walrus-state-registry — known entry.\n//\n// Resource id: `'walrus'` (singular). The plugin's substrate-level\n// plugin key is the same string.\n\nimport { Effect, Path } from 'effect';\n\nimport { defineModeNamespace } from '../../api/mode-narrowed-factory.ts';\nimport { definePlugin, resource, type ResourceRef } from '../../api/define-plugin.ts';\nimport type { ContainerRuntime } from '../../contracts/container-runtime.ts';\nimport type { RoutableDecl } from '../../contracts/routable.ts';\nimport type { StrategyContributorDecl } from '../../contracts/strategy-contributor.ts';\nimport { emitContributions, PluginContext } from '../../substrate/plugin-ctx.ts';\nimport { ContainerRuntimeService } from '../../runtime/docker/service.ts';\nimport { IdentityContext, StackPathsService } from '../../substrate/runtime/paths.ts';\nimport { CacheService } from '../../substrate/runtime/cache/index.ts';\nimport { deriveSubnetPrefix, withSubnetAddressing } from '../../substrate/runtime/subnet-broker.ts';\nimport { renderUrl, routedHostname } from '../../substrate/runtime/routed-url.ts';\nimport type { AccountFundingCoinValue } from '../account/index.ts';\nimport { coinResourceId, type CoinResourceId } from '../coin/index.ts';\nimport { suiResource, type SuiProbeKey } from '../sui/index.ts';\n\nimport { chainProbeFor } from '../../substrate/runtime/strategy-registry/index.ts';\n\nimport { makeCodegenable, makeWalrusStaticCodegen } from './codegen.ts';\nimport { LOCAL_NETWORK_NAME } from '../../api/inference-network.ts';\nimport { walrusPluginKey } from './plugin-key.ts';\nimport { walrusPluginError, type WalrusPluginError } from './errors.ts';\nimport { makeWalFaucetContribution, type WalFaucetStrategy } from './faucet-strategy.ts';\nimport { bootWalrusService, type WalrusMode } from './service.ts';\nimport {\n\tresolveLocalClusterOptions,\n\ttype WalrusLocalClusterOptions,\n} from './mode/local-cluster.ts';\nimport {\n\tresolveKnownDeploymentOptions,\n\ttype WalrusKnownDeploymentOptions,\n} from './mode/known-deploy.ts';\nimport { makeSnapshotable, type WalrusSnapshotMode } from './snapshot.ts';\nimport {\n\tWALRUS_AGGREGATOR_ENDPOINT_NAME,\n\tWALRUS_PUBLISHER_ENDPOINT_NAME,\n\tmakeLocalRoutables,\n} from './routable.ts';\nimport { WALRUS_STATE_REGISTRY_KEY, type WalrusStateEntry } from './registry-publish.ts';\nimport {\n\tWALRUS_ROUTER_PORT,\n\tbuildWalrusNetworkName,\n\ttype WalrusStorageNode,\n} from './storage-nodes.ts';\n\n// ---------------------------------------------------------------------------\n// Resource — the resolved value all consumers read\n// ---------------------------------------------------------------------------\n\n/** The Walrus resolved value carried by the resource. */\nexport interface WalrusResolved {\n\treadonly mode: 'local' | 'known';\n\t/** Network name the walrus deployment targets (`localnet`/`testnet`/…). */\n\treadonly network: string;\n\treadonly walrusPackageId: string | null;\n\treadonly walPackageId: string | null;\n\t/** SDK-ready `packageConfig` — structurally compatible with\n\t * `@mysten/walrus`'s `WalrusPackageConfig`. */\n\treadonly packageConfig: {\n\t\treadonly systemObjectId: string;\n\t\treadonly stakingPoolId: string;\n\t\treadonly exchangeIds?: ReadonlyArray<string>;\n\t};\n\treadonly nodes: ReadonlyArray<WalrusStorageNode>;\n\treadonly proxyUrl: string | null;\n\treadonly aggregatorUrl: string | null;\n\treadonly publisherUrl: string | null;\n\treadonly walFaucetStrategy: WalFaucetStrategy | null;\n\treadonly walCoinType: string | null;\n}\n\n/** Walrus plugin resource. */\nexport const walrusResource = resource<'walrus', WalrusResolved>('walrus');\n\nexport interface WalrusNetworkIdentity {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly walrusName: string;\n}\n\n/** Walrus deploy records storage-node listening IPs under this /24.\n * Docker network create requests the matching subnet explicitly, with\n * the prefix derived from the Walrus network identity so parallel\n * stacks don't all claim the same Docker IPAM range. Walrus claims\n * the `10.64.*` – `10.127.*` band; see substrate/runtime/subnet-broker.ts\n * for the algorithm and band coordination with seal. */\nexport const deriveWalrusSubnetPrefix = (identity: WalrusNetworkIdentity): string =>\n\tderiveSubnetPrefix(`${identity.app}\\0${identity.stack}\\0${identity.walrusName}`, 64);\n\nconst withWalrusNetworkAddressing = (\n\truntime: ContainerRuntime,\n\twalrusNetworkName: string,\n\tsubnetPrefix: string,\n): ContainerRuntime => ({\n\t...runtime,\n\tensureNetwork: (spec) =>\n\t\truntime.ensureNetwork(\n\t\t\tspec.name === walrusNetworkName ? withSubnetAddressing(spec, subnetPrefix) : spec,\n\t\t),\n});\n\n// ---------------------------------------------------------------------------\n// Plugin construction (internal — used by walrus() + walrusFor())\n// ---------------------------------------------------------------------------\n\nconst buildLocalPlugin = (opts: WalrusLocalClusterOptions) => {\n\t// Synchronous factory-time validation (distilled-doc invariants\n\t// 11 — `nodeCount >= 1` + `shards >= nodeCount`).\n\tconst resolved = resolveLocalClusterOptions(opts);\n\n\tconst walrusKey = walrusPluginKey(resolved.name);\n\n\treturn definePlugin({\n\t\tid: walrusResource.id,\n\t\tdependsOn: [suiResource] as const,\n\t\trole: 'service',\n\t\tsection: 'service',\n\t\tpluginKey: walrusKey,\n\t\t// Stack-free codegen: a local walrus cluster's deploy ids / endpoint\n\t\t// URLs are LOADED CONFIG DATA -- the committed `walrus.ts` stub emits\n\t\t// `requireValue(dep, 'walrus', '<key>')`, never a baked object id / URL.\n\t\tstaticCodegen: () => [makeWalrusStaticCodegen({ mode: 'local', network: LOCAL_NETWORK_NAME })],\n\t\t// `deps` auto-infers the resolved `[sui]` tuple from the\n\t\t// `[suiResource] as const` dependency. `ctx` arrives via the\n\t\t// `PluginContext` service.\n\t\tstart: (deps) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst [sui] = deps;\n\n\t\t\t\t// Substrate-context primitives:\n\t\t\t\t// - `ContainerRuntimeService` + `IdentityContext` arrive\n\t\t\t\t// via the supervisor's plugin runtime context.\n\t\t\t\t// - `ArtifactPublisher` is the substrate-level\n\t\t\t\t// publisher (cache → verify → produce → register cycle).\n\t\t\t\t// - `ChainProbe<SuiProbeKey>` is looked up via the\n\t\t\t\t// StrategyRegistry under `chain-probe:<chainId>`;\n\t\t\t\t// Sui registered itself there at its own acquire.\n\t\t\t\t// - `StackPathsService` resolves the per-stack on-disk\n\t\t\t\t// root so the deploy-output bind-mount source is a\n\t\t\t\t// real path (not the `<runtime>/...` template).\n\t\t\t\tconst runtime = yield* ContainerRuntimeService;\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\tconst stackPaths = yield* StackPathsService;\n\t\t\t\tconst path = yield* Path.Path;\n\t\t\t\tconst publisher = yield* CacheService;\n\t\t\t\tconst probe = yield* chainProbeFor<SuiProbeKey>(sui.chainId);\n\n\t\t\t\t// Resolve the deploy-output bind-mount source from the\n\t\t\t\t// per-stack paths bundle. The deploy one-shot owns preparing\n\t\t\t\t// the directory immediately before its Docker bind mount.\n\t\t\t\tconst deployHostMountPath = path.join(\n\t\t\t\t\tstackPaths.stackRoot,\n\t\t\t\t\t'walrus',\n\t\t\t\t\tresolved.name,\n\t\t\t\t\t'deploy',\n\t\t\t\t);\n\n\t\t\t\t// Cross-container DNS: walrus containers (deploy one-shot\n\t\t\t\t// + N storage nodes) dial sui RPC + faucet via\n\t\t\t\t// `host.docker.internal`. The sui plugin binds brokered\n\t\t\t\t// host ports — no shared docker network needed.\n\t\t\t\t//\n\t\t\t\t// Architectural decision (B5): walrus owns its OWN docker\n\t\t\t\t// network for storage-node ↔ deploy connectivity; sui-side\n\t\t\t\t// hops go through the host gateway.\n\t\t\t\t// On Linux this requires Docker Desktop or the\n\t\t\t\t// `host.docker.internal:host-gateway` runtime hint (the\n\t\t\t\t// established devstack convention — see plugins/deepbook\n\t\t\t\t// which uses the same pattern).\n\t\t\t\tconst walrusNetworkName = buildWalrusNetworkName(\n\t\t\t\t\tidentity.app,\n\t\t\t\t\tidentity.stack,\n\t\t\t\t\tresolved.name,\n\t\t\t\t);\n\t\t\t\tconst walrusSubnetPrefix = deriveWalrusSubnetPrefix({\n\t\t\t\t\tapp: identity.app,\n\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\twalrusName: resolved.name,\n\t\t\t\t});\n\t\t\t\tconst walrusRuntime = withWalrusNetworkAddressing(\n\t\t\t\t\truntime,\n\t\t\t\t\twalrusNetworkName,\n\t\t\t\t\twalrusSubnetPrefix,\n\t\t\t\t);\n\t\t\t\tconst suiRpcUrlInNetwork = sui.hostGateway.rpcUrl;\n\t\t\t\t// sui-faucet v2 endpoint — `/v2/gas` is the supported path\n\t\t\t\t// on devnet-v1.71.0+.\n\t\t\t\tconst suiFaucetUrlInNetwork = sui.hostGateway.faucetUrl;\n\t\t\t\tif (suiFaucetUrlInNetwork === null) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\twalrusPluginError(\n\t\t\t\t\t\t\t'deploy',\n\t\t\t\t\t\t\t'walrus local-cluster requires a Sui faucet URL for deploy funding.',\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst walrusFaucetUrlInNetwork = `${suiFaucetUrlInNetwork}/v2/gas`;\n\n\t\t\t\tconst mode: WalrusMode = { mode: 'local', opts: resolved };\n\t\t\t\tconst boot = yield* bootWalrusService(\n\t\t\t\t\t{\n\t\t\t\t\t\truntime: walrusRuntime,\n\t\t\t\t\t\tpublisher,\n\t\t\t\t\t\tprobe,\n\t\t\t\t\t\tsuiSdk: sui.sdk,\n\t\t\t\t\t\tsuiChainId: sui.chainId,\n\t\t\t\t\t\tsuiRpcUrlInNetwork,\n\t\t\t\t\t\twalrusFaucetUrlInNetwork,\n\t\t\t\t\t\twaitForFundsReady: sui.waitForTransactionsReady.wait,\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\tsubnetPrefix: walrusSubnetPrefix,\n\t\t\t\t\t\twalrusNetworkName,\n\t\t\t\t\t\t// Walrus has no `sui-net` to attach to (sui binds\n\t\t\t\t\t\t// host ports). Reuse the walrus network so the\n\t\t\t\t\t\t// deploy one-shot + storage nodes land on a single\n\t\t\t\t\t\t// network and reach sui via `host.docker.internal`.\n\t\t\t\t\t\tsuiNetworkName: walrusNetworkName,\n\t\t\t\t\t\tdeployHostMountPath,\n\t\t\t\t\t\tstackRoot: stackPaths.stackRoot,\n\t\t\t\t\t},\n\t\t\t\t\tmode,\n\t\t\t\t);\n\n\t\t\t\tif (boot.mode !== 'local') {\n\t\t\t\t\t// Should be unreachable — dispatch is by mode. Defense.\n\t\t\t\t\treturn yield* Effect.die('walrus: mode mismatch in local plugin');\n\t\t\t\t}\n\n\t\t\t\tconst packageConfig: WalrusResolved['packageConfig'] =\n\t\t\t\t\tboot.exchangeObjectId === undefined\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tsystemObjectId: boot.deploy.systemObject,\n\t\t\t\t\t\t\t\tstakingPoolId: boot.deploy.stakingObject,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\tsystemObjectId: boot.deploy.systemObject,\n\t\t\t\t\t\t\t\tstakingPoolId: boot.deploy.stakingObject,\n\t\t\t\t\t\t\t\texchangeIds: [boot.exchangeObjectId],\n\t\t\t\t\t\t\t};\n\t\t\t\tconst serviceUrl = (role: string) =>\n\t\t\t\t\troutedHostname(identity, role).pipe(\n\t\t\t\t\t\tEffect.map((hostname) =>\n\t\t\t\t\t\t\trenderUrl({\n\t\t\t\t\t\t\t\tprotocol: 'http',\n\t\t\t\t\t\t\t\thostname,\n\t\t\t\t\t\t\t\tport: WALRUS_ROUTER_PORT,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tEffect.mapError((cause) =>\n\t\t\t\t\t\t\twalrusPluginError(\n\t\t\t\t\t\t\t\t'proxy',\n\t\t\t\t\t\t\t\t`walrus route URL assembly failed for ${role}: ${cause.detail}`,\n\t\t\t\t\t\t\t\t{ cause },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\tconst aggregatorUrl =\n\t\t\t\t\tboot.clientServices.aggregator === null\n\t\t\t\t\t\t? null\n\t\t\t\t\t\t: yield* serviceUrl(WALRUS_AGGREGATOR_ENDPOINT_NAME);\n\t\t\t\tconst publisherUrl =\n\t\t\t\t\tboot.clientServices.publisher === null\n\t\t\t\t\t\t? null\n\t\t\t\t\t\t: yield* serviceUrl(WALRUS_PUBLISHER_ENDPOINT_NAME);\n\n\t\t\t\tconst resolvedValue: WalrusResolved = {\n\t\t\t\t\tmode: 'local',\n\t\t\t\t\tnetwork: identity.network,\n\t\t\t\t\twalrusPackageId: boot.walrusPackageId,\n\t\t\t\t\twalPackageId: boot.walPackageId,\n\t\t\t\t\tpackageConfig,\n\t\t\t\t\tnodes: boot.nodes,\n\t\t\t\t\tproxyUrl: aggregatorUrl,\n\t\t\t\t\taggregatorUrl,\n\t\t\t\t\tpublisherUrl,\n\t\t\t\t\twalFaucetStrategy: boot.walFaucetStrategy,\n\t\t\t\t\twalCoinType: boot.walCoinType,\n\t\t\t\t};\n\t\t\t\t// Emit the resolved contributions inline: snapshot → codegen →\n\t\t\t\t// state-registry → (optional WAL faucet) → routables. `identity`\n\t\t\t\t// (from `IdentityContext`) stamps the snapshot + routable\n\t\t\t\t// container names. ID-stability surfaces (deploy/blob ids via\n\t\t\t\t// ArtifactPublisher) live OUTSIDE this emission and are untouched.\n\t\t\t\tconst walFaucetContribution: ReadonlyArray<StrategyContributorDecl> =\n\t\t\t\t\tresolvedValue.walFaucetStrategy === null || resolvedValue.walCoinType === null\n\t\t\t\t\t\t? []\n\t\t\t\t\t\t: [\n\t\t\t\t\t\t\t\tmakeWalFaucetContribution(\n\t\t\t\t\t\t\t\t\tresolvedValue.walFaucetStrategy,\n\t\t\t\t\t\t\t\t\tresolvedValue.walCoinType,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t];\n\t\t\t\tconst clientServiceRoles = [\n\t\t\t\t\t...(boot.clientServices.aggregator === null ? [] : ['aggregator' as const]),\n\t\t\t\t\t...(boot.clientServices.publisher === null ? [] : ['publisher' as const]),\n\t\t\t\t];\n\t\t\t\temitContributions(ctx, [\n\t\t\t\t\tmakeSnapshotable(\n\t\t\t\t\t\t'local' satisfies WalrusSnapshotMode,\n\t\t\t\t\t\tidentity.app,\n\t\t\t\t\t\tidentity.stack,\n\t\t\t\t\t\tresolved.name,\n\t\t\t\t\t\tresolvedValue.network,\n\t\t\t\t\t\tresolved.nodeCount,\n\t\t\t\t\t\tclientServiceRoles,\n\t\t\t\t\t),\n\t\t\t\t\tmakeCodegenable({\n\t\t\t\t\t\tmode: 'local',\n\t\t\t\t\t\tnetwork: resolvedValue.network,\n\t\t\t\t\t\twalrusPackageId: resolvedValue.walrusPackageId,\n\t\t\t\t\t\twalPackageId: resolvedValue.walPackageId,\n\t\t\t\t\t\twalCoinType: resolvedValue.walCoinType,\n\t\t\t\t\t\tsystemObjectId: resolvedValue.packageConfig.systemObjectId,\n\t\t\t\t\t\tstakingPoolId: resolvedValue.packageConfig.stakingPoolId,\n\t\t\t\t\t\texchangeIds: resolvedValue.packageConfig.exchangeIds\n\t\t\t\t\t\t\t? [...resolvedValue.packageConfig.exchangeIds]\n\t\t\t\t\t\t\t: [],\n\t\t\t\t\t\tproxyUrl: resolvedValue.proxyUrl,\n\t\t\t\t\t\taggregatorUrl: resolvedValue.aggregatorUrl,\n\t\t\t\t\t\tpublisherUrl: resolvedValue.publisherUrl,\n\t\t\t\t\t\tnodes: resolvedValue.nodes,\n\t\t\t\t\t}),\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: 'strategy-contributor',\n\t\t\t\t\t\tcapabilityKey: WALRUS_STATE_REGISTRY_KEY,\n\t\t\t\t\t\tstrategy: { noteName: resolved.name },\n\t\t\t\t\t\tautoMounted: true,\n\t\t\t\t\t} satisfies StrategyContributorDecl<\n\t\t\t\t\t\ttypeof WALRUS_STATE_REGISTRY_KEY,\n\t\t\t\t\t\t{ readonly noteName: string }\n\t\t\t\t\t>,\n\t\t\t\t\t...walFaucetContribution,\n\t\t\t\t\t...(makeLocalRoutables({\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\twalrusName: resolved.name,\n\t\t\t\t\t\tserviceKey: String(walrusKey),\n\t\t\t\t\t\tnodeCount: resolved.nodeCount,\n\t\t\t\t\t\tcontainerApiPort: resolved.containerApiPort,\n\t\t\t\t\t\taggregator: boot.clientServices.aggregator,\n\t\t\t\t\t\tpublisher: boot.clientServices.publisher,\n\t\t\t\t\t}) as readonly RoutableDecl[]),\n\t\t\t\t]);\n\t\t\t\treturn resolvedValue;\n\t\t\t}),\n\t});\n};\n\nconst buildKnownPlugin = (opts: WalrusKnownDeploymentOptions) => {\n\t// Synchronous factory-time validation for required deployment ids.\n\tconst resolved = resolveKnownDeploymentOptions(opts);\n\n\treturn definePlugin({\n\t\tid: walrusResource.id,\n\t\tdependsOn: [suiResource] as const,\n\t\t// Known deployment is a pure value-producer — no containers,\n\t\t// no long-running children.\n\t\trole: 'task',\n\t\tsection: 'service',\n\t\t// Stack-free codegen: a known deployment's ids / URLs are DECLARED\n\t\t// config (not loaded-at-runtime data) — bake them as literals in the\n\t\t// committed `walrus.ts` (mirrors `knownPackage`). `walrusPackageId` /\n\t\t// `walPackageId` / `walCoinType` are null for a known deployment.\n\t\tstaticCodegen: () => [\n\t\t\tmakeWalrusStaticCodegen({\n\t\t\t\tmode: 'known',\n\t\t\t\tnetwork: resolved.network,\n\t\t\t\tknown: {\n\t\t\t\t\twalrusPackageId: null,\n\t\t\t\t\twalPackageId: null,\n\t\t\t\t\twalCoinType: null,\n\t\t\t\t\tpackageConfig: {\n\t\t\t\t\t\tsystemObjectId: resolved.systemObjectId,\n\t\t\t\t\t\tstakingPoolId: resolved.stakingPoolId,\n\t\t\t\t\t\t...(resolved.exchangeIds.length > 0 ? { exchangeIds: [...resolved.exchangeIds] } : {}),\n\t\t\t\t\t},\n\t\t\t\t\tproxyUrl: resolved.proxyUrl,\n\t\t\t\t\taggregatorUrl: resolved.aggregatorUrl,\n\t\t\t\t\tpublisherUrl: resolved.publisherUrl,\n\t\t\t\t\tnodes: resolved.nodes,\n\t\t\t\t},\n\t\t\t}),\n\t\t],\n\t\t// Known mode is a pure value-producer — `sui` is unused (the value\n\t\t// reads `resolved.network` from the deployment options) but the\n\t\t// `dependsOn` edge still orders boot, so `start` is zero-arg.\n\t\t// `ctx` arrives via the `PluginContext` service.\n\t\tstart: () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\tconst resolvedValue = {\n\t\t\t\t\tmode: 'known',\n\t\t\t\t\tnetwork: resolved.network,\n\t\t\t\t\twalrusPackageId: null,\n\t\t\t\t\twalPackageId: null,\n\t\t\t\t\tpackageConfig: {\n\t\t\t\t\t\tsystemObjectId: resolved.systemObjectId,\n\t\t\t\t\t\tstakingPoolId: resolved.stakingPoolId,\n\t\t\t\t\t\texchangeIds: resolved.exchangeIds.length > 0 ? resolved.exchangeIds : undefined,\n\t\t\t\t\t},\n\t\t\t\t\tnodes: resolved.nodes,\n\t\t\t\t\tproxyUrl: resolved.proxyUrl,\n\t\t\t\t\taggregatorUrl: resolved.aggregatorUrl,\n\t\t\t\t\tpublisherUrl: resolved.publisherUrl,\n\t\t\t\t\twalFaucetStrategy: null,\n\t\t\t\t\twalCoinType: null,\n\t\t\t\t} satisfies WalrusResolved;\n\t\t\t\t// Emit inline: snapshot → codegen → state-registry. Known mode\n\t\t\t\t// emits no routable. `identity` (from `IdentityContext`) stamps\n\t\t\t\t// the snapshot scoping.\n\t\t\t\tctx.snapshotExtra(\n\t\t\t\t\tmakeSnapshotable(\n\t\t\t\t\t\t'known' satisfies WalrusSnapshotMode,\n\t\t\t\t\t\tidentity.app,\n\t\t\t\t\t\tidentity.stack,\n\t\t\t\t\t\t'walrusKnownDeployment',\n\t\t\t\t\t\tresolvedValue.network,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tctx.codegen(\n\t\t\t\t\tmakeCodegenable({\n\t\t\t\t\t\tmode: 'known',\n\t\t\t\t\t\tnetwork: resolvedValue.network,\n\t\t\t\t\t\twalrusPackageId: resolvedValue.walrusPackageId,\n\t\t\t\t\t\twalPackageId: resolvedValue.walPackageId,\n\t\t\t\t\t\twalCoinType: resolvedValue.walCoinType,\n\t\t\t\t\t\tsystemObjectId: resolvedValue.packageConfig.systemObjectId,\n\t\t\t\t\t\tstakingPoolId: resolvedValue.packageConfig.stakingPoolId,\n\t\t\t\t\t\texchangeIds: resolvedValue.packageConfig.exchangeIds\n\t\t\t\t\t\t\t? [...resolvedValue.packageConfig.exchangeIds]\n\t\t\t\t\t\t\t: [],\n\t\t\t\t\t\tproxyUrl: resolvedValue.proxyUrl,\n\t\t\t\t\t\taggregatorUrl: resolvedValue.aggregatorUrl,\n\t\t\t\t\t\tpublisherUrl: resolvedValue.publisherUrl,\n\t\t\t\t\t\tnodes: resolvedValue.nodes,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tctx.provides({\n\t\t\t\t\tkind: 'strategy-contributor',\n\t\t\t\t\tcapabilityKey: WALRUS_STATE_REGISTRY_KEY,\n\t\t\t\t\tstrategy: {\n\t\t\t\t\t\tname: 'walrusKnownDeployment',\n\t\t\t\t\t\tsystemObjectId: resolvedValue.packageConfig.systemObjectId,\n\t\t\t\t\t\tstakingObjectId: resolvedValue.packageConfig.stakingPoolId,\n\t\t\t\t\t\tnetwork: resolvedValue.network,\n\t\t\t\t\t},\n\t\t\t\t\tautoMounted: true,\n\t\t\t\t} satisfies StrategyContributorDecl<typeof WALRUS_STATE_REGISTRY_KEY, WalrusStateEntry>);\n\t\t\t\treturn resolvedValue;\n\t\t\t}),\n\t});\n};\n\nexport interface WalCoinValue extends AccountFundingCoinValue {\n\treadonly symbol: 'WAL';\n\treadonly fullCoinType: `${string}::wal::WAL`;\n\treadonly decimals: 9;\n\treadonly source: 'walrus';\n}\n\n/** Resolve the local Walrus deployment's WAL coin as an account-funding\n * coin ref. Accounts that need WAL should use:\n *\n * funding: [{ coin: walCoin(localWalrus), amount }]\n *\n * The funding strategy itself is contributed by the Walrus service\n * once its local exchange exists. */\nexport const walCoin = (walrusMember: ResourceRef<'walrus', WalrusResolved>) => {\n\tconst coinRef = resource<CoinResourceId<'wal'>, WalCoinValue>(coinResourceId('wal'));\n\n\treturn definePlugin({\n\t\tid: coinRef.id,\n\t\tdependsOn: walrusMember,\n\t\trole: 'task',\n\t\tsection: 'action',\n\t\tstart: (resolved): Effect.Effect<WalCoinValue, WalrusPluginError> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tif (resolved.walCoinType === null) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\twalrusPluginError(\n\t\t\t\t\t\t\t'exchange',\n\t\t\t\t\t\t\t'walCoin(...) requires a local Walrus deployment with a WAL package.',\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsymbol: 'WAL',\n\t\t\t\t\tfullCoinType: resolved.walCoinType as `${string}::wal::WAL`,\n\t\t\t\t\tdecimals: 9,\n\t\t\t\t\tsource: 'walrus',\n\t\t\t\t} satisfies WalCoinValue;\n\t\t\t}),\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// User-facing factories\n// ---------------------------------------------------------------------------\n\n/** Local-cluster shorthand. Known deployments are selected through the\n * per-network methods `walrusFor(network).testnet(...)` /\n * `.mainnet(...)` so network choice stays explicit; `.known(...)` is the\n * raw-id override form. */\nexport const walrus = (opts?: { readonly local?: WalrusLocalClusterOptions }) => {\n\treturn buildLocalPlugin(opts?.local ?? {});\n};\n\n/** Per-network factory options for `walrusFor(net).testnet()`/`.mainnet()`.\n * The `network` is injected by the namespace method, so the caller only\n * supplies the (still-required) `nodes` committee plus optional per-field\n * overrides — the on-chain ids default from the `@mysten/walrus` SDK package\n * config. Plugin-INTERNAL — deliberately NOT re-exported from `src/index.ts`. */\ntype WalrusKnownByNetworkOptions = Omit<WalrusKnownDeploymentOptions, 'network'>;\n\n/** Mode-narrowed factory namespace.\n *\n * Usage:\n * const network = { mode: 'local', network: 'localnet' } as const;\n * walrusFor(network).local({...}) // OK\n * walrusFor(liveNet).testnet({ nodes }) // OK — network injected\n * walrusFor(forkNet).local({...}) // compile error: no `.local` on fork\n *\n * Per-network methods (`.testnet`/`.mainnet`) inject the network into\n * `buildKnownPlugin`; `nodes` is still REQUIRED, so `.testnet({ nodes })` is\n * the minimal call. `.known(...)` is now the raw-id explicit-override form only\n * (`systemObjectId`/`stakingPoolId`/`nodes`) — the `network`-in-`.known()` path\n * was HARD CUT.\n *\n * Critically, the fork branch exposes NO `.local` — calling\n * `.local` on a fork-mode network is a **compile error** at the\n * call site (distilled-doc invariant 12 — type-level refusal). */\nexport const walrusFor = defineModeNamespace({\n\tlocal: {\n\t\tlocal: (opts: WalrusLocalClusterOptions = {}) => buildLocalPlugin(opts),\n\t\tknown: (opts: WalrusKnownByNetworkOptions) => buildKnownPlugin(opts),\n\t\ttestnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'testnet', ...opts }),\n\t\tmainnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'mainnet', ...opts }),\n\t},\n\tlive: {\n\t\tknown: (opts: WalrusKnownByNetworkOptions) => buildKnownPlugin(opts),\n\t\ttestnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'testnet', ...opts }),\n\t\tmainnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'mainnet', ...opts }),\n\t},\n\tfork: {\n\t\t// `.local` is intentionally absent — calling\n\t\t// `walrusFor(forkNetwork).local(...)` is a compile error.\n\t\tknown: (opts: WalrusKnownByNetworkOptions) => buildKnownPlugin(opts),\n\t\ttestnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'testnet', ...opts }),\n\t\tmainnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'mainnet', ...opts }),\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// Re-exports for advanced callers\n// ---------------------------------------------------------------------------\n\nexport type {\n\tWalrusLocalClusterOptions,\n\tWalrusLocalPublisherOptions,\n\tWalrusLocalServiceOptions,\n} from './mode/local-cluster.ts';\nexport type { WalrusKnownDeploymentOptions, WalrusKnownNetwork } from './mode/known-deploy.ts';\nexport type { WalrusStorageNode } from './storage-nodes.ts';\nexport type { WalrusBindings, WalrusNodeBinding } from './codegen.ts';\nexport type { WalrusError, WalrusPluginError, WalrusConfigError, WalrusPhase } from './errors.ts';\nexport { walCoinType, walFaucetStrategyKey, type WalFaucetStrategy } from './faucet-strategy.ts';\nexport {\n\tWALRUS_STATE_REGISTRY_KEY,\n\ttype WalrusStateEntry,\n\ttype WalrusLocalStateEntry,\n\ttype WalrusKnownStateEntry,\n} from './registry-publish.ts';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/walrus/index.ts"],"sourcesContent":["// Walrus plugin — barrel + factories.\n//\n// Architecture: Walrus is a service plugin that owns a local cluster\n// or describes a known deployment. The factory at this file folds the\n// four modes behind:\n//\n// - `walrus(opts?)` — local-cluster shorthand. No env\n// defaulting; use `walrusFor(...)`\n// for known deployments.\n// - `walrusFor(network)` — mode-narrowed factory namespace\n// (architecture Tension 11). Returns\n// `{ local: …, known: … }` narrowed\n// to the network's mode. Crucially,\n// the fork branch exposes ONLY\n// `.known` — `.local` is a compile\n// error on a fork-mode network.\n//\n// During `start`, the plugin emits (via the typed `ctx.*` verbs, per mode):\n//\n// Local mode (full local cluster):\n// 1. `ctx.snapshotExtra` — runtime/walrus/<name>/deploy/ subtree\n// + storage-node managed containers.\n// 2. `ctx.codegen` — `walrus-network` bindings.\n// 3. `ctx.endpoint` × (N+3) — per-node + aggregator + publisher + upload relay.\n// 4. `ctx.provides` walrus-state-registry — local entry.\n// 5. `ctx.provides` endpoint-registry — N+3 entries.\n// 6. `ctx.provides` package-registry — `walrus.<name>`.\n// 7. `ctx.provides` coinType:<WAL fullCoinType> — WAL faucet\n// strategy\n// (when exchange exists).\n//\n// Known mode (read-only deployment):\n// 1. `ctx.snapshotExtra` — identity-guard only; no subtrees.\n// 2. `ctx.codegen` — `walrus-network` bindings (mode='known').\n// 3. `ctx.provides` walrus-state-registry — known entry.\n//\n// Resource id: `'walrus'` (singular). The plugin's substrate-level\n// plugin key is the same string.\n\nimport { Effect, Path } from 'effect';\n\nimport { defineModeNamespace } from '../../api/mode-narrowed-factory.ts';\nimport { definePlugin, resource, type ResourceRef } from '../../api/define-plugin.ts';\nimport type { ContainerRuntime } from '../../contracts/container-runtime.ts';\nimport type { RoutableDecl } from '../../contracts/routable.ts';\nimport type { StrategyContributorDecl } from '../../contracts/strategy-contributor.ts';\nimport { emitContributions, PluginContext } from '../../substrate/plugin-ctx.ts';\nimport { ContainerRuntimeService } from '../../runtime/docker/service.ts';\nimport { IdentityContext, StackPathsService } from '../../substrate/runtime/paths.ts';\nimport { CacheService } from '../../substrate/runtime/cache/index.ts';\nimport { deriveSubnetPrefix, withSubnetAddressing } from '../../substrate/runtime/subnet-broker.ts';\nimport { renderUrl, routedHostname } from '../../substrate/runtime/routed-url.ts';\nimport type { AccountFundingCoinValue } from '../account/index.ts';\nimport { coinResourceId, type CoinResourceId } from '../coin/index.ts';\nimport { suiResource, type SuiProbeKey } from '../sui/index.ts';\n\nimport { chainProbeFor } from '../../substrate/runtime/strategy-registry/index.ts';\n\nimport { makeCodegenable, makeWalrusStaticCodegen } from './codegen.ts';\nimport { LOCAL_NETWORK_NAME } from '../../api/inference-network.ts';\nimport { walrusPluginKey } from './plugin-key.ts';\nimport { walrusPluginError, type WalrusPluginError } from './errors.ts';\nimport { makeWalFaucetContribution, type WalFaucetStrategy } from './faucet-strategy.ts';\nimport { bootWalrusService, type WalrusMode } from './service.ts';\nimport {\n\tresolveLocalClusterOptions,\n\ttype WalrusLocalClusterOptions,\n} from './mode/local-cluster.ts';\nimport {\n\tresolveKnownDeploymentOptions,\n\ttype WalrusKnownDeploymentOptions,\n} from './mode/known-deploy.ts';\nimport { makeSnapshotable, type WalrusSnapshotMode } from './snapshot.ts';\nimport {\n\tWALRUS_AGGREGATOR_ENDPOINT_NAME,\n\tWALRUS_PUBLISHER_ENDPOINT_NAME,\n\tWALRUS_UPLOAD_RELAY_ENDPOINT_NAME,\n\tmakeLocalRoutables,\n} from './routable.ts';\nimport { WALRUS_STATE_REGISTRY_KEY, type WalrusStateEntry } from './registry-publish.ts';\nimport {\n\tWALRUS_ROUTER_PORT,\n\tbuildWalrusNetworkName,\n\ttype WalrusStorageNode,\n} from './storage-nodes.ts';\n\n// ---------------------------------------------------------------------------\n// Resource — the resolved value all consumers read\n// ---------------------------------------------------------------------------\n\n/** The Walrus resolved value carried by the resource. */\nexport interface WalrusResolved {\n\treadonly mode: 'local' | 'known';\n\t/** Network name the walrus deployment targets (`localnet`/`testnet`/…). */\n\treadonly network: string;\n\treadonly walrusPackageId: string | null;\n\treadonly walPackageId: string | null;\n\t/** SDK-ready `packageConfig` — structurally compatible with\n\t * `@mysten/walrus`'s `WalrusPackageConfig`. */\n\treadonly packageConfig: {\n\t\treadonly systemObjectId: string;\n\t\treadonly stakingPoolId: string;\n\t\treadonly exchangeIds?: ReadonlyArray<string>;\n\t};\n\treadonly nodes: ReadonlyArray<WalrusStorageNode>;\n\treadonly proxyUrl: string | null;\n\treadonly aggregatorUrl: string | null;\n\treadonly publisherUrl: string | null;\n\treadonly uploadRelayUrl: string | null;\n\treadonly walFaucetStrategy: WalFaucetStrategy | null;\n\treadonly walCoinType: string | null;\n}\n\n/** Walrus plugin resource. */\nexport const walrusResource = resource<'walrus', WalrusResolved>('walrus');\n\nexport interface WalrusNetworkIdentity {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly walrusName: string;\n}\n\n/** Walrus deploy records storage-node listening IPs under this /24.\n * Docker network create requests the matching subnet explicitly, with\n * the prefix derived from the Walrus network identity so parallel\n * stacks don't all claim the same Docker IPAM range. Walrus claims\n * the `10.64.*` – `10.127.*` band; see substrate/runtime/subnet-broker.ts\n * for the algorithm and band coordination with seal. */\nexport const deriveWalrusSubnetPrefix = (identity: WalrusNetworkIdentity): string =>\n\tderiveSubnetPrefix(`${identity.app}\\0${identity.stack}\\0${identity.walrusName}`, 64);\n\nconst withWalrusNetworkAddressing = (\n\truntime: ContainerRuntime,\n\twalrusNetworkName: string,\n\tsubnetPrefix: string,\n): ContainerRuntime => ({\n\t...runtime,\n\tensureNetwork: (spec) =>\n\t\truntime.ensureNetwork(\n\t\t\tspec.name === walrusNetworkName ? withSubnetAddressing(spec, subnetPrefix) : spec,\n\t\t),\n});\n\n// ---------------------------------------------------------------------------\n// Plugin construction (internal — used by walrus() + walrusFor())\n// ---------------------------------------------------------------------------\n\nconst buildLocalPlugin = (opts: WalrusLocalClusterOptions) => {\n\t// Synchronous factory-time validation (distilled-doc invariants\n\t// 11 — `nodeCount >= 1` + `shards >= nodeCount`).\n\tconst resolved = resolveLocalClusterOptions(opts);\n\n\tconst walrusKey = walrusPluginKey(resolved.name);\n\n\treturn definePlugin({\n\t\tid: walrusResource.id,\n\t\tdependsOn: [suiResource] as const,\n\t\trole: 'service',\n\t\tsection: 'service',\n\t\tpluginKey: walrusKey,\n\t\t// Stack-free codegen: a local walrus cluster's deploy ids / endpoint\n\t\t// URLs are LOADED CONFIG DATA -- the committed `walrus.ts` stub emits\n\t\t// `requireValue(dep, 'walrus', '<key>')`, never a baked object id / URL.\n\t\tstaticCodegen: () => [makeWalrusStaticCodegen({ mode: 'local', network: LOCAL_NETWORK_NAME })],\n\t\t// `deps` auto-infers the resolved `[sui]` tuple from the\n\t\t// `[suiResource] as const` dependency. `ctx` arrives via the\n\t\t// `PluginContext` service.\n\t\tstart: (deps) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst [sui] = deps;\n\n\t\t\t\t// Substrate-context primitives:\n\t\t\t\t// - `ContainerRuntimeService` + `IdentityContext` arrive\n\t\t\t\t// via the supervisor's plugin runtime context.\n\t\t\t\t// - `ArtifactPublisher` is the substrate-level\n\t\t\t\t// publisher (cache → verify → produce → register cycle).\n\t\t\t\t// - `ChainProbe<SuiProbeKey>` is looked up via the\n\t\t\t\t// StrategyRegistry under `chain-probe:<chainId>`;\n\t\t\t\t// Sui registered itself there at its own acquire.\n\t\t\t\t// - `StackPathsService` resolves the per-stack on-disk\n\t\t\t\t// root so the deploy-output bind-mount source is a\n\t\t\t\t// real path (not the `<runtime>/...` template).\n\t\t\t\tconst runtime = yield* ContainerRuntimeService;\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\tconst stackPaths = yield* StackPathsService;\n\t\t\t\tconst path = yield* Path.Path;\n\t\t\t\tconst publisher = yield* CacheService;\n\t\t\t\tconst probe = yield* chainProbeFor<SuiProbeKey>(sui.chainId);\n\n\t\t\t\t// Resolve the deploy-output bind-mount source from the\n\t\t\t\t// per-stack paths bundle. The deploy one-shot owns preparing\n\t\t\t\t// the directory immediately before its Docker bind mount.\n\t\t\t\tconst deployHostMountPath = path.join(\n\t\t\t\t\tstackPaths.stackRoot,\n\t\t\t\t\t'walrus',\n\t\t\t\t\tresolved.name,\n\t\t\t\t\t'deploy',\n\t\t\t\t);\n\n\t\t\t\t// Cross-container DNS: walrus containers (deploy one-shot\n\t\t\t\t// + N storage nodes) dial sui RPC + faucet via\n\t\t\t\t// `host.docker.internal`. The sui plugin binds brokered\n\t\t\t\t// host ports — no shared docker network needed.\n\t\t\t\t//\n\t\t\t\t// Architectural decision (B5): walrus owns its OWN docker\n\t\t\t\t// network for storage-node ↔ deploy connectivity; sui-side\n\t\t\t\t// hops go through the host gateway.\n\t\t\t\t// On Linux this requires Docker Desktop or the\n\t\t\t\t// `host.docker.internal:host-gateway` runtime hint (the\n\t\t\t\t// established devstack convention — see plugins/deepbook\n\t\t\t\t// which uses the same pattern).\n\t\t\t\tconst walrusNetworkName = buildWalrusNetworkName(\n\t\t\t\t\tidentity.app,\n\t\t\t\t\tidentity.stack,\n\t\t\t\t\tresolved.name,\n\t\t\t\t);\n\t\t\t\tconst walrusSubnetPrefix = deriveWalrusSubnetPrefix({\n\t\t\t\t\tapp: identity.app,\n\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\twalrusName: resolved.name,\n\t\t\t\t});\n\t\t\t\tconst walrusRuntime = withWalrusNetworkAddressing(\n\t\t\t\t\truntime,\n\t\t\t\t\twalrusNetworkName,\n\t\t\t\t\twalrusSubnetPrefix,\n\t\t\t\t);\n\t\t\t\tconst suiRpcUrlInNetwork = sui.hostGateway.rpcUrl;\n\t\t\t\t// sui-faucet v2 endpoint — `/v2/gas` is the supported path\n\t\t\t\t// on devnet-v1.71.0+.\n\t\t\t\tconst suiFaucetUrlInNetwork = sui.hostGateway.faucetUrl;\n\t\t\t\tif (suiFaucetUrlInNetwork === null) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\twalrusPluginError(\n\t\t\t\t\t\t\t'deploy',\n\t\t\t\t\t\t\t'walrus local-cluster requires a Sui faucet URL for deploy funding.',\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst walrusFaucetUrlInNetwork = `${suiFaucetUrlInNetwork}/v2/gas`;\n\n\t\t\t\tconst mode: WalrusMode = { mode: 'local', opts: resolved };\n\t\t\t\tconst boot = yield* bootWalrusService(\n\t\t\t\t\t{\n\t\t\t\t\t\truntime: walrusRuntime,\n\t\t\t\t\t\tpublisher,\n\t\t\t\t\t\tprobe,\n\t\t\t\t\t\tsuiSdk: sui.sdk,\n\t\t\t\t\t\tsuiChainId: sui.chainId,\n\t\t\t\t\t\tsuiRpcUrlInNetwork,\n\t\t\t\t\t\twalrusFaucetUrlInNetwork,\n\t\t\t\t\t\twaitForFundsReady: sui.waitForTransactionsReady.wait,\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\tsubnetPrefix: walrusSubnetPrefix,\n\t\t\t\t\t\twalrusNetworkName,\n\t\t\t\t\t\t// Walrus has no `sui-net` to attach to (sui binds\n\t\t\t\t\t\t// host ports). Reuse the walrus network so the\n\t\t\t\t\t\t// deploy one-shot + storage nodes land on a single\n\t\t\t\t\t\t// network and reach sui via `host.docker.internal`.\n\t\t\t\t\t\tsuiNetworkName: walrusNetworkName,\n\t\t\t\t\t\tdeployHostMountPath,\n\t\t\t\t\t\tstackRoot: stackPaths.stackRoot,\n\t\t\t\t\t},\n\t\t\t\t\tmode,\n\t\t\t\t);\n\n\t\t\t\tif (boot.mode !== 'local') {\n\t\t\t\t\t// Should be unreachable — dispatch is by mode. Defense.\n\t\t\t\t\treturn yield* Effect.die('walrus: mode mismatch in local plugin');\n\t\t\t\t}\n\n\t\t\t\tconst packageConfig: WalrusResolved['packageConfig'] =\n\t\t\t\t\tboot.exchangeObjectId === undefined\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tsystemObjectId: boot.deploy.systemObject,\n\t\t\t\t\t\t\t\tstakingPoolId: boot.deploy.stakingObject,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\tsystemObjectId: boot.deploy.systemObject,\n\t\t\t\t\t\t\t\tstakingPoolId: boot.deploy.stakingObject,\n\t\t\t\t\t\t\t\texchangeIds: [boot.exchangeObjectId],\n\t\t\t\t\t\t\t};\n\t\t\t\tconst serviceUrl = (role: string) =>\n\t\t\t\t\troutedHostname(identity, role).pipe(\n\t\t\t\t\t\tEffect.map((hostname) =>\n\t\t\t\t\t\t\trenderUrl({\n\t\t\t\t\t\t\t\tprotocol: 'http',\n\t\t\t\t\t\t\t\thostname,\n\t\t\t\t\t\t\t\tport: WALRUS_ROUTER_PORT,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tEffect.mapError((cause) =>\n\t\t\t\t\t\t\twalrusPluginError(\n\t\t\t\t\t\t\t\t'proxy',\n\t\t\t\t\t\t\t\t`walrus route URL assembly failed for ${role}: ${cause.detail}`,\n\t\t\t\t\t\t\t\t{ cause },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\tconst aggregatorUrl =\n\t\t\t\t\tboot.clientServices.aggregator === null\n\t\t\t\t\t\t? null\n\t\t\t\t\t\t: yield* serviceUrl(WALRUS_AGGREGATOR_ENDPOINT_NAME);\n\t\t\t\tconst publisherUrl =\n\t\t\t\t\tboot.clientServices.publisher === null\n\t\t\t\t\t\t? null\n\t\t\t\t\t\t: yield* serviceUrl(WALRUS_PUBLISHER_ENDPOINT_NAME);\n\t\t\t\tconst uploadRelayUrl =\n\t\t\t\t\tboot.clientServices.uploadRelay === null\n\t\t\t\t\t\t? null\n\t\t\t\t\t\t: yield* serviceUrl(WALRUS_UPLOAD_RELAY_ENDPOINT_NAME);\n\n\t\t\t\tconst resolvedValue: WalrusResolved = {\n\t\t\t\t\tmode: 'local',\n\t\t\t\t\tnetwork: identity.network,\n\t\t\t\t\twalrusPackageId: boot.walrusPackageId,\n\t\t\t\t\twalPackageId: boot.walPackageId,\n\t\t\t\t\tpackageConfig,\n\t\t\t\t\tnodes: boot.nodes,\n\t\t\t\t\tproxyUrl: aggregatorUrl,\n\t\t\t\t\taggregatorUrl,\n\t\t\t\t\tpublisherUrl,\n\t\t\t\t\tuploadRelayUrl,\n\t\t\t\t\twalFaucetStrategy: boot.walFaucetStrategy,\n\t\t\t\t\twalCoinType: boot.walCoinType,\n\t\t\t\t};\n\t\t\t\t// Emit the resolved contributions inline: snapshot → codegen →\n\t\t\t\t// state-registry → (optional WAL faucet) → routables. `identity`\n\t\t\t\t// (from `IdentityContext`) stamps the snapshot + routable\n\t\t\t\t// container names. ID-stability surfaces (deploy/blob ids via\n\t\t\t\t// ArtifactPublisher) live OUTSIDE this emission and are untouched.\n\t\t\t\tconst walFaucetContribution: ReadonlyArray<StrategyContributorDecl> =\n\t\t\t\t\tresolvedValue.walFaucetStrategy === null || resolvedValue.walCoinType === null\n\t\t\t\t\t\t? []\n\t\t\t\t\t\t: [\n\t\t\t\t\t\t\t\tmakeWalFaucetContribution(\n\t\t\t\t\t\t\t\t\tresolvedValue.walFaucetStrategy,\n\t\t\t\t\t\t\t\t\tresolvedValue.walCoinType,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t];\n\t\t\t\tconst clientServiceRoles = [\n\t\t\t\t\t...(boot.clientServices.aggregator === null ? [] : ['aggregator' as const]),\n\t\t\t\t\t...(boot.clientServices.publisher === null ? [] : ['publisher' as const]),\n\t\t\t\t\t...(boot.clientServices.uploadRelay === null ? [] : ['upload-relay' as const]),\n\t\t\t\t];\n\t\t\t\temitContributions(ctx, [\n\t\t\t\t\tmakeSnapshotable(\n\t\t\t\t\t\t'local' satisfies WalrusSnapshotMode,\n\t\t\t\t\t\tidentity.app,\n\t\t\t\t\t\tidentity.stack,\n\t\t\t\t\t\tresolved.name,\n\t\t\t\t\t\tresolvedValue.network,\n\t\t\t\t\t\tresolved.nodeCount,\n\t\t\t\t\t\tclientServiceRoles,\n\t\t\t\t\t),\n\t\t\t\t\tmakeCodegenable({\n\t\t\t\t\t\tmode: 'local',\n\t\t\t\t\t\tnetwork: resolvedValue.network,\n\t\t\t\t\t\twalrusPackageId: resolvedValue.walrusPackageId,\n\t\t\t\t\t\twalPackageId: resolvedValue.walPackageId,\n\t\t\t\t\t\twalCoinType: resolvedValue.walCoinType,\n\t\t\t\t\t\tsystemObjectId: resolvedValue.packageConfig.systemObjectId,\n\t\t\t\t\t\tstakingPoolId: resolvedValue.packageConfig.stakingPoolId,\n\t\t\t\t\t\texchangeIds: resolvedValue.packageConfig.exchangeIds\n\t\t\t\t\t\t\t? [...resolvedValue.packageConfig.exchangeIds]\n\t\t\t\t\t\t\t: [],\n\t\t\t\t\t\tproxyUrl: resolvedValue.proxyUrl,\n\t\t\t\t\t\taggregatorUrl: resolvedValue.aggregatorUrl,\n\t\t\t\t\t\tpublisherUrl: resolvedValue.publisherUrl,\n\t\t\t\t\t\tuploadRelayUrl: resolvedValue.uploadRelayUrl,\n\t\t\t\t\t\tnodes: resolvedValue.nodes,\n\t\t\t\t\t}),\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: 'strategy-contributor',\n\t\t\t\t\t\tcapabilityKey: WALRUS_STATE_REGISTRY_KEY,\n\t\t\t\t\t\tstrategy: { noteName: resolved.name },\n\t\t\t\t\t\tautoMounted: true,\n\t\t\t\t\t} satisfies StrategyContributorDecl<\n\t\t\t\t\t\ttypeof WALRUS_STATE_REGISTRY_KEY,\n\t\t\t\t\t\t{ readonly noteName: string }\n\t\t\t\t\t>,\n\t\t\t\t\t...walFaucetContribution,\n\t\t\t\t\t...(makeLocalRoutables({\n\t\t\t\t\t\tapp: identity.app,\n\t\t\t\t\t\tstack: identity.stack,\n\t\t\t\t\t\twalrusName: resolved.name,\n\t\t\t\t\t\tserviceKey: String(walrusKey),\n\t\t\t\t\t\tnodeCount: resolved.nodeCount,\n\t\t\t\t\t\tcontainerApiPort: resolved.containerApiPort,\n\t\t\t\t\t\taggregator: boot.clientServices.aggregator,\n\t\t\t\t\t\tpublisher: boot.clientServices.publisher,\n\t\t\t\t\t\tuploadRelay: boot.clientServices.uploadRelay,\n\t\t\t\t\t}) as readonly RoutableDecl[]),\n\t\t\t\t]);\n\t\t\t\treturn resolvedValue;\n\t\t\t}),\n\t});\n};\n\nconst buildKnownPlugin = (opts: WalrusKnownDeploymentOptions) => {\n\t// Synchronous factory-time validation for required deployment ids.\n\tconst resolved = resolveKnownDeploymentOptions(opts);\n\n\treturn definePlugin({\n\t\tid: walrusResource.id,\n\t\tdependsOn: [suiResource] as const,\n\t\t// Known deployment is a pure value-producer — no containers,\n\t\t// no long-running children.\n\t\trole: 'task',\n\t\tsection: 'service',\n\t\t// Stack-free codegen: a known deployment's ids / URLs are DECLARED\n\t\t// config (not loaded-at-runtime data) — bake them as literals in the\n\t\t// committed `walrus.ts` (mirrors `knownPackage`). `walrusPackageId` /\n\t\t// `walPackageId` / `walCoinType` are null for a known deployment.\n\t\tstaticCodegen: () => [\n\t\t\tmakeWalrusStaticCodegen({\n\t\t\t\tmode: 'known',\n\t\t\t\tnetwork: resolved.network,\n\t\t\t\tknown: {\n\t\t\t\t\twalrusPackageId: null,\n\t\t\t\t\twalPackageId: null,\n\t\t\t\t\twalCoinType: null,\n\t\t\t\t\tpackageConfig: {\n\t\t\t\t\t\tsystemObjectId: resolved.systemObjectId,\n\t\t\t\t\t\tstakingPoolId: resolved.stakingPoolId,\n\t\t\t\t\t\t...(resolved.exchangeIds.length > 0 ? { exchangeIds: [...resolved.exchangeIds] } : {}),\n\t\t\t\t\t},\n\t\t\t\t\tproxyUrl: resolved.proxyUrl,\n\t\t\t\t\taggregatorUrl: resolved.aggregatorUrl,\n\t\t\t\t\tpublisherUrl: resolved.publisherUrl,\n\t\t\t\t\tuploadRelayUrl: resolved.uploadRelayUrl,\n\t\t\t\t\tnodes: resolved.nodes,\n\t\t\t\t},\n\t\t\t}),\n\t\t],\n\t\t// Known mode is a pure value-producer — `sui` is unused (the value\n\t\t// reads `resolved.network` from the deployment options) but the\n\t\t// `dependsOn` edge still orders boot, so `start` is zero-arg.\n\t\t// `ctx` arrives via the `PluginContext` service.\n\t\tstart: () =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst ctx = yield* PluginContext;\n\t\t\t\tconst identity = yield* IdentityContext;\n\t\t\t\tconst resolvedValue = {\n\t\t\t\t\tmode: 'known',\n\t\t\t\t\tnetwork: resolved.network,\n\t\t\t\t\twalrusPackageId: null,\n\t\t\t\t\twalPackageId: null,\n\t\t\t\t\tpackageConfig: {\n\t\t\t\t\t\tsystemObjectId: resolved.systemObjectId,\n\t\t\t\t\t\tstakingPoolId: resolved.stakingPoolId,\n\t\t\t\t\t\texchangeIds: resolved.exchangeIds.length > 0 ? resolved.exchangeIds : undefined,\n\t\t\t\t\t},\n\t\t\t\t\tnodes: resolved.nodes,\n\t\t\t\t\tproxyUrl: resolved.proxyUrl,\n\t\t\t\t\taggregatorUrl: resolved.aggregatorUrl,\n\t\t\t\t\tpublisherUrl: resolved.publisherUrl,\n\t\t\t\t\tuploadRelayUrl: resolved.uploadRelayUrl,\n\t\t\t\t\twalFaucetStrategy: null,\n\t\t\t\t\twalCoinType: null,\n\t\t\t\t} satisfies WalrusResolved;\n\t\t\t\t// Emit inline: snapshot → codegen → state-registry. Known mode\n\t\t\t\t// emits no routable. `identity` (from `IdentityContext`) stamps\n\t\t\t\t// the snapshot scoping.\n\t\t\t\tctx.snapshotExtra(\n\t\t\t\t\tmakeSnapshotable(\n\t\t\t\t\t\t'known' satisfies WalrusSnapshotMode,\n\t\t\t\t\t\tidentity.app,\n\t\t\t\t\t\tidentity.stack,\n\t\t\t\t\t\t'walrusKnownDeployment',\n\t\t\t\t\t\tresolvedValue.network,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tctx.codegen(\n\t\t\t\t\tmakeCodegenable({\n\t\t\t\t\t\tmode: 'known',\n\t\t\t\t\t\tnetwork: resolvedValue.network,\n\t\t\t\t\t\twalrusPackageId: resolvedValue.walrusPackageId,\n\t\t\t\t\t\twalPackageId: resolvedValue.walPackageId,\n\t\t\t\t\t\twalCoinType: resolvedValue.walCoinType,\n\t\t\t\t\t\tsystemObjectId: resolvedValue.packageConfig.systemObjectId,\n\t\t\t\t\t\tstakingPoolId: resolvedValue.packageConfig.stakingPoolId,\n\t\t\t\t\t\texchangeIds: resolvedValue.packageConfig.exchangeIds\n\t\t\t\t\t\t\t? [...resolvedValue.packageConfig.exchangeIds]\n\t\t\t\t\t\t\t: [],\n\t\t\t\t\t\tproxyUrl: resolvedValue.proxyUrl,\n\t\t\t\t\t\taggregatorUrl: resolvedValue.aggregatorUrl,\n\t\t\t\t\t\tpublisherUrl: resolvedValue.publisherUrl,\n\t\t\t\t\t\tuploadRelayUrl: resolvedValue.uploadRelayUrl,\n\t\t\t\t\t\tnodes: resolvedValue.nodes,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tctx.provides({\n\t\t\t\t\tkind: 'strategy-contributor',\n\t\t\t\t\tcapabilityKey: WALRUS_STATE_REGISTRY_KEY,\n\t\t\t\t\tstrategy: {\n\t\t\t\t\t\tname: 'walrusKnownDeployment',\n\t\t\t\t\t\tsystemObjectId: resolvedValue.packageConfig.systemObjectId,\n\t\t\t\t\t\tstakingObjectId: resolvedValue.packageConfig.stakingPoolId,\n\t\t\t\t\t\tnetwork: resolvedValue.network,\n\t\t\t\t\t},\n\t\t\t\t\tautoMounted: true,\n\t\t\t\t} satisfies StrategyContributorDecl<typeof WALRUS_STATE_REGISTRY_KEY, WalrusStateEntry>);\n\t\t\t\treturn resolvedValue;\n\t\t\t}),\n\t});\n};\n\nexport interface WalCoinValue extends AccountFundingCoinValue {\n\treadonly symbol: 'WAL';\n\treadonly fullCoinType: `${string}::wal::WAL`;\n\treadonly decimals: 9;\n\treadonly source: 'walrus';\n}\n\n/** Resolve the local Walrus deployment's WAL coin as an account-funding\n * coin ref. Accounts that need WAL should use:\n *\n * funding: [{ coin: walCoin(localWalrus), amount }]\n *\n * The funding strategy itself is contributed by the Walrus service\n * once its local exchange exists. */\nexport const walCoin = (walrusMember: ResourceRef<'walrus', WalrusResolved>) => {\n\tconst coinRef = resource<CoinResourceId<'wal'>, WalCoinValue>(coinResourceId('wal'));\n\n\treturn definePlugin({\n\t\tid: coinRef.id,\n\t\tdependsOn: walrusMember,\n\t\trole: 'task',\n\t\tsection: 'action',\n\t\tstart: (resolved): Effect.Effect<WalCoinValue, WalrusPluginError> =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tif (resolved.walCoinType === null) {\n\t\t\t\t\treturn yield* Effect.fail(\n\t\t\t\t\t\twalrusPluginError(\n\t\t\t\t\t\t\t'exchange',\n\t\t\t\t\t\t\t'walCoin(...) requires a local Walrus deployment with a WAL package.',\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tsymbol: 'WAL',\n\t\t\t\t\tfullCoinType: resolved.walCoinType as `${string}::wal::WAL`,\n\t\t\t\t\tdecimals: 9,\n\t\t\t\t\tsource: 'walrus',\n\t\t\t\t} satisfies WalCoinValue;\n\t\t\t}),\n\t});\n};\n\n// ---------------------------------------------------------------------------\n// User-facing factories\n// ---------------------------------------------------------------------------\n\n/** Local-cluster shorthand. Known deployments are selected through the\n * per-network methods `walrusFor(network).testnet(...)` /\n * `.mainnet(...)` so network choice stays explicit; `.known(...)` is the\n * raw-id override form. */\nexport const walrus = (opts?: { readonly local?: WalrusLocalClusterOptions }) => {\n\treturn buildLocalPlugin(opts?.local ?? {});\n};\n\n/** Per-network factory options for `walrusFor(net).testnet()`/`.mainnet()`.\n * The `network` is injected by the namespace method, so the caller only\n * supplies the (still-required) `nodes` committee plus optional per-field\n * overrides — the on-chain ids default from the `@mysten/walrus` SDK package\n * config. Plugin-INTERNAL — deliberately NOT re-exported from `src/index.ts`. */\ntype WalrusKnownByNetworkOptions = Omit<WalrusKnownDeploymentOptions, 'network'>;\n\n/** Mode-narrowed factory namespace.\n *\n * Usage:\n * const network = { mode: 'local', network: 'localnet' } as const;\n * walrusFor(network).local({...}) // OK\n * walrusFor(liveNet).testnet({ nodes }) // OK — network injected\n * walrusFor(forkNet).local({...}) // compile error: no `.local` on fork\n *\n * Per-network methods (`.testnet`/`.mainnet`) inject the network into\n * `buildKnownPlugin`; `nodes` is still REQUIRED, so `.testnet({ nodes })` is\n * the minimal call. `.known(...)` is now the raw-id explicit-override form only\n * (`systemObjectId`/`stakingPoolId`/`nodes`) — the `network`-in-`.known()` path\n * was HARD CUT.\n *\n * Critically, the fork branch exposes NO `.local` — calling\n * `.local` on a fork-mode network is a **compile error** at the\n * call site (distilled-doc invariant 12 — type-level refusal). */\nexport const walrusFor = defineModeNamespace({\n\tlocal: {\n\t\tlocal: (opts: WalrusLocalClusterOptions = {}) => buildLocalPlugin(opts),\n\t\tknown: (opts: WalrusKnownByNetworkOptions) => buildKnownPlugin(opts),\n\t\ttestnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'testnet', ...opts }),\n\t\tmainnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'mainnet', ...opts }),\n\t},\n\tlive: {\n\t\tknown: (opts: WalrusKnownByNetworkOptions) => buildKnownPlugin(opts),\n\t\ttestnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'testnet', ...opts }),\n\t\tmainnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'mainnet', ...opts }),\n\t},\n\tfork: {\n\t\t// `.local` is intentionally absent — calling\n\t\t// `walrusFor(forkNetwork).local(...)` is a compile error.\n\t\tknown: (opts: WalrusKnownByNetworkOptions) => buildKnownPlugin(opts),\n\t\ttestnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'testnet', ...opts }),\n\t\tmainnet: (opts: WalrusKnownByNetworkOptions = {}) =>\n\t\t\tbuildKnownPlugin({ network: 'mainnet', ...opts }),\n\t},\n});\n\n// ---------------------------------------------------------------------------\n// Re-exports for advanced callers\n// ---------------------------------------------------------------------------\n\nexport type {\n\tWalrusLocalClusterOptions,\n\tWalrusLocalPublisherOptions,\n\tWalrusLocalServiceOptions,\n} from './mode/local-cluster.ts';\nexport type { WalrusKnownDeploymentOptions, WalrusKnownNetwork } from './mode/known-deploy.ts';\nexport type { WalrusStorageNode } from './storage-nodes.ts';\nexport type { WalrusBindings, WalrusNodeBinding } from './codegen.ts';\nexport type { WalrusError, WalrusPluginError, WalrusConfigError, WalrusPhase } from './errors.ts';\nexport { walCoinType, walFaucetStrategyKey, type WalFaucetStrategy } from './faucet-strategy.ts';\nexport {\n\tWALRUS_STATE_REGISTRY_KEY,\n\ttype WalrusStateEntry,\n\ttype WalrusLocalStateEntry,\n\ttype WalrusKnownStateEntry,\n} from './registry-publish.ts';\n"],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@ import { MAINNET_WALRUS_PACKAGE_CONFIG, TESTNET_WALRUS_PACKAGE_CONFIG } from "@m
|
|
|
6
6
|
/** Known-deployment registry — baked-in record per network. The
|
|
7
7
|
* on-chain ids default from the `@mysten/walrus` SDK package config
|
|
8
8
|
* (`{TESTNET,MAINNET}_WALRUS_PACKAGE_CONFIG`) so a caller only needs
|
|
9
|
-
* to supply `nodes`; the aggregator/publisher/proxy URLs are
|
|
9
|
+
* to supply `nodes`; the aggregator/publisher/upload-relay/proxy URLs are
|
|
10
10
|
* devstack-owned (the SDK ships none). Devnet has no canonical SDK
|
|
11
11
|
* record today, so it keeps no id defaults. */
|
|
12
12
|
const KNOWN_DEPLOYMENT_REGISTRY = {
|
|
@@ -17,6 +17,7 @@ const KNOWN_DEPLOYMENT_REGISTRY = {
|
|
|
17
17
|
exchangeIds: TESTNET_WALRUS_PACKAGE_CONFIG.exchangeIds,
|
|
18
18
|
aggregatorUrl: "https://aggregator.walrus-testnet.walrus.space",
|
|
19
19
|
publisherUrl: "https://publisher.walrus-testnet.walrus.space",
|
|
20
|
+
uploadRelayUrl: "https://upload-relay.testnet.walrus.space",
|
|
20
21
|
proxyUrl: "https://aggregator.walrus-testnet.walrus.space"
|
|
21
22
|
},
|
|
22
23
|
mainnet: {
|
|
@@ -25,6 +26,7 @@ const KNOWN_DEPLOYMENT_REGISTRY = {
|
|
|
25
26
|
stakingPoolId: MAINNET_WALRUS_PACKAGE_CONFIG.stakingPoolId,
|
|
26
27
|
aggregatorUrl: "https://aggregator.walrus.space",
|
|
27
28
|
publisherUrl: "https://publisher.walrus.space",
|
|
29
|
+
uploadRelayUrl: "https://upload-relay.mainnet.walrus.space",
|
|
28
30
|
proxyUrl: "https://aggregator.walrus.space"
|
|
29
31
|
},
|
|
30
32
|
devnet: { network: "devnet" }
|
|
@@ -47,6 +49,7 @@ const resolveKnownDeploymentOptions = (opts) => {
|
|
|
47
49
|
if (!nodes) throw walrusConfigError("nodes", `walrusKnownDeployment: explicit 'nodes' committee is required — Walrus ${opts.network ?? "custom"} has nodes fetched dynamically by the SDK`, "pass an empty array if you accept the SDK-driven committee lookup, or use walrus()/walrusFor(network).local({...}) for a local self-hosted cluster");
|
|
48
50
|
const aggregatorUrl = opts.aggregatorUrl ?? reg?.aggregatorUrl ?? null;
|
|
49
51
|
const publisherUrl = opts.publisherUrl ?? reg?.publisherUrl ?? null;
|
|
52
|
+
const uploadRelayUrl = opts.uploadRelayUrl ?? reg?.uploadRelayUrl ?? null;
|
|
50
53
|
const proxyUrl = opts.proxyUrl ?? reg?.proxyUrl ?? aggregatorUrl ?? null;
|
|
51
54
|
return {
|
|
52
55
|
mode: "known",
|
|
@@ -57,7 +60,8 @@ const resolveKnownDeploymentOptions = (opts) => {
|
|
|
57
60
|
nodes,
|
|
58
61
|
proxyUrl,
|
|
59
62
|
aggregatorUrl,
|
|
60
|
-
publisherUrl
|
|
63
|
+
publisherUrl,
|
|
64
|
+
uploadRelayUrl
|
|
61
65
|
};
|
|
62
66
|
};
|
|
63
67
|
/** "Boot" the known deployment. Purely synchronous projection inside
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"known-deploy.mjs","names":[],"sources":["../../../../src/plugins/walrus/mode/known-deploy.ts"],"sourcesContent":["// Walrus mode — known deployment (testnet / mainnet / *-fork via\n// upstream translation).\n//\n// Distilled-doc reference (06-walrus.md §\"Lifecycle: Startup —\n// known deployment\"). Purely synchronous body — no ordered phases.\n// The factory validates required deployment fields and returns a\n// resolved value whose tags are eager `Layer.succeed(...)` constants.\n//\n// Distilled-doc invariants honored:\n// - 15: each of `proxyUrl / aggregatorUrl / publisherUrl` surfaces\n// INDEPENDENTLY. Encoded as `string | null` in the resolved\n// shape — a given field is null only when THAT specific URL\n// is unresolved (registry default + explicit override both\n// absent). The plugin's projection step publishes each tag\n// when its own URL is present; a missing publisher URL does\n// not suppress an available proxy/aggregator URL.\n// - 16: throw synchronously when `nodes` is missing for a\n// registered network. The on-chain ids\n// (`systemObjectId` / `stakingPoolId` / `exchangeIds`) ARE\n// stable and come from the `@mysten/walrus` SDK package config\n// — we default them from the SDK and let callers override.\n// Only the node committee stays explicit: testnet has 100+\n// nodes that the SDK fetches dynamically, so we don't pin them.\n\nimport { MAINNET_WALRUS_PACKAGE_CONFIG, TESTNET_WALRUS_PACKAGE_CONFIG } from '@mysten/walrus';\nimport { Effect, type Scope } from 'effect';\n\nimport { expectNonEmptyString } from '../../../substrate/runtime/config-validation.ts';\nimport { walrusConfigError, type WalrusConfigError } from '../errors.ts';\nimport type { WalrusStorageNode } from '../storage-nodes.ts';\n\n/** Known networks supported by the registry lookup. */\nexport type WalrusKnownNetwork = 'testnet' | 'mainnet' | 'devnet';\n\n/** Options for the known-deployment mode. */\nexport interface WalrusKnownDeploymentOptions {\n\t/** Network shortcut — looks up per-field defaults from a registry\n\t * baked into this package. */\n\treadonly network?: WalrusKnownNetwork;\n\t/** Per-field overrides for the on-chain ids. For `testnet`/`mainnet`\n\t * these default from the `@mysten/walrus` SDK package config, so\n\t * they're optional; for `devnet` (or when `network` is omitted)\n\t * `systemObjectId`/`stakingPoolId` are required. An explicit value\n\t * here always wins over the registry default. */\n\treadonly systemObjectId?: string;\n\treadonly stakingPoolId?: string;\n\treadonly exchangeIds?: ReadonlyArray<string>;\n\t/** Explicit storage-node committee. Required (throws if missing\n\t * even when `network` is set — distilled-doc invariant 16). */\n\treadonly nodes?: ReadonlyArray<WalrusStorageNode>;\n\treadonly aggregatorUrl?: string;\n\treadonly publisherUrl?: string;\n\treadonly proxyUrl?: string;\n}\n\n/** Resolved known-deployment boot artifacts. */\nexport interface KnownDeploymentBootResult {\n\treadonly mode: 'known';\n\t/** The known network's name (`testnet`/`mainnet`/`devnet`) — its stable\n\t * identity. A known remote deployment has no per-boot genesis digest, so\n\t * the network name IS its `chainId` for codegen/snapshot keying. */\n\treadonly network: string;\n\treadonly systemObjectId: string;\n\treadonly stakingPoolId: string;\n\treadonly exchangeIds: ReadonlyArray<string>;\n\treadonly nodes: ReadonlyArray<WalrusStorageNode>;\n\t/** Null only when the read/proxy URL itself is unresolved (no explicit\n\t * `proxyUrl` override, registry proxy URL, or aggregator URL). Surfaces\n\t * independently of `aggregatorUrl` / `publisherUrl` — distilled-doc\n\t * invariant 15. */\n\treadonly proxyUrl: string | null;\n\treadonly aggregatorUrl: string | null;\n\treadonly publisherUrl: string | null;\n}\n\n/** Known-deployment registry — baked-in record per network. The\n * on-chain ids default from the `@mysten/walrus` SDK package config\n * (`{TESTNET,MAINNET}_WALRUS_PACKAGE_CONFIG`) so a caller only needs\n * to supply `nodes`; the aggregator/publisher/proxy URLs are\n * devstack-owned (the SDK ships none). Devnet has no canonical SDK\n * record today, so it keeps no id defaults. */\nconst KNOWN_DEPLOYMENT_REGISTRY: Readonly<\n\tRecord<\n\t\tWalrusKnownNetwork,\n\t\t{\n\t\t\treadonly network: string;\n\t\t\treadonly systemObjectId?: string;\n\t\t\treadonly stakingPoolId?: string;\n\t\t\treadonly exchangeIds?: ReadonlyArray<string>;\n\t\t\treadonly aggregatorUrl?: string;\n\t\t\treadonly publisherUrl?: string;\n\t\t\treadonly proxyUrl?: string;\n\t\t}\n\t>\n> = {\n\ttestnet: {\n\t\tnetwork: 'testnet',\n\t\t// On-chain ids default from the SDK package config; callers may\n\t\t// still override per-field. The URLs below are devstack-owned —\n\t\t// the SDK ships none.\n\t\tsystemObjectId: TESTNET_WALRUS_PACKAGE_CONFIG.systemObjectId,\n\t\tstakingPoolId: TESTNET_WALRUS_PACKAGE_CONFIG.stakingPoolId,\n\t\texchangeIds: TESTNET_WALRUS_PACKAGE_CONFIG.exchangeIds,\n\t\taggregatorUrl: 'https://aggregator.walrus-testnet.walrus.space',\n\t\tpublisherUrl: 'https://publisher.walrus-testnet.walrus.space',\n\t\tproxyUrl: 'https://aggregator.walrus-testnet.walrus.space',\n\t},\n\tmainnet: {\n\t\tnetwork: 'mainnet',\n\t\t// On-chain ids default from the SDK package config; callers may\n\t\t// still override per-field. Mainnet ships no `exchangeIds` in the\n\t\t// SDK config (testnet-only faucet exchange), so it stays absent.\n\t\tsystemObjectId: MAINNET_WALRUS_PACKAGE_CONFIG.systemObjectId,\n\t\tstakingPoolId: MAINNET_WALRUS_PACKAGE_CONFIG.stakingPoolId,\n\t\taggregatorUrl: 'https://aggregator.walrus.space',\n\t\tpublisherUrl: 'https://publisher.walrus.space',\n\t\tproxyUrl: 'https://aggregator.walrus.space',\n\t},\n\tdevnet: {\n\t\tnetwork: 'devnet',\n\t},\n};\n\n/** Synchronous factory-time validation + projection. Throws (NOT\n * Effect-fail) on missing required fields so misconfiguration trips\n * at the `defineDevstack` call site rather than at deferred\n * Layer.build time. Distilled-doc invariants 14 + 16. */\nexport const resolveKnownDeploymentOptions = (\n\topts: WalrusKnownDeploymentOptions,\n): KnownDeploymentBootResult => {\n\tconst reg = opts.network ? KNOWN_DEPLOYMENT_REGISTRY[opts.network] : undefined;\n\n\t// On-chain ids: caller override (`opts.*`) wins, else the registry\n\t// default sourced from the `@mysten/walrus` SDK package config.\n\t// Testnet/mainnet default from the SDK so only `nodes` is required;\n\t// devnet has no SDK record, so these stay required there.\n\tconst systemObjectId = expectNonEmptyString(opts.systemObjectId ?? reg?.systemObjectId, {\n\t\tfield: 'systemObjectId',\n\t\tmkError: ({ field }) =>\n\t\t\twalrusConfigError(\n\t\t\t\tfield,\n\t\t\t\t`walrusKnownDeployment: 'systemObjectId' is required (testnet/mainnet default from the @mysten/walrus SDK; pass it explicitly for devnet or networks without an SDK record)`,\n\t\t\t),\n\t});\n\n\tconst stakingPoolId = expectNonEmptyString(opts.stakingPoolId ?? reg?.stakingPoolId, {\n\t\tfield: 'stakingPoolId',\n\t\tmkError: ({ field }) =>\n\t\t\twalrusConfigError(\n\t\t\t\tfield,\n\t\t\t\t`walrusKnownDeployment: 'stakingPoolId' is required (testnet/mainnet default from the @mysten/walrus SDK; pass it explicitly for devnet or networks without an SDK record)`,\n\t\t\t),\n\t});\n\n\tconst nodes = opts.nodes;\n\tif (!nodes) {\n\t\t// Distilled-doc invariant 16: even when `network` is set, the\n\t\t// node committee must be explicit. The on-chain ids come from\n\t\t// the SDK package config, but the committee is dynamic — testnet\n\t\t// has 100+ nodes the SDK fetches at runtime, so we don't pin it.\n\t\tthrow walrusConfigError(\n\t\t\t'nodes',\n\t\t\t`walrusKnownDeployment: explicit 'nodes' committee is required — ` +\n\t\t\t\t`Walrus ${opts.network ?? 'custom'} has nodes fetched dynamically by the SDK`,\n\t\t\t`pass an empty array if you accept the SDK-driven committee lookup, ` +\n\t\t\t\t`or use walrus()/walrusFor(network).local({...}) for a local self-hosted cluster`,\n\t\t);\n\t}\n\n\tconst aggregatorUrl = opts.aggregatorUrl ?? reg?.aggregatorUrl ?? null;\n\tconst publisherUrl = opts.publisherUrl ?? reg?.publisherUrl ?? null;\n\tconst proxyUrl = opts.proxyUrl ?? reg?.proxyUrl ?? aggregatorUrl ?? null;\n\n\t// Invariant 15: surface null per individual URL when missing so the\n\t// plugin's projection can conditionally publish each tag. Previously\n\t// any single missing URL nullified all three, dropping user-supplied\n\t// values for the URLs they did provide.\n\treturn {\n\t\tmode: 'known' as const,\n\t\tnetwork: reg?.network ?? 'custom',\n\t\tsystemObjectId,\n\t\tstakingPoolId,\n\t\texchangeIds: opts.exchangeIds ?? reg?.exchangeIds ?? [],\n\t\tnodes,\n\t\tproxyUrl,\n\t\taggregatorUrl,\n\t\tpublisherUrl,\n\t};\n};\n\n/** \"Boot\" the known deployment. Purely synchronous projection inside\n * Effect.gen — no I/O, no Scope work. Surfaces as Effect for\n * uniformity with the local-cluster boot signature. */\nexport const bootKnownDeployment = (\n\topts: WalrusKnownDeploymentOptions,\n): Effect.Effect<KnownDeploymentBootResult, WalrusConfigError, Scope.Scope> =>\n\tEffect.try({\n\t\ttry: () => resolveKnownDeploymentOptions(opts),\n\t\t// `resolveKnownDeploymentOptions` throws our typed config error\n\t\t// shape. STYLE_GUIDE §2 forbids bare error casts; runtime-guard\n\t\t// the `_tag` so an unexpected synchronous throw (e.g. a future\n\t\t// helper that throws a stock `TypeError`) is wrapped instead of\n\t\t// silently mis-tagged as `WalrusConfigError`.\n\t\tcatch: (err): WalrusConfigError => {\n\t\t\tif (\n\t\t\t\ttypeof err === 'object' &&\n\t\t\t\terr !== null &&\n\t\t\t\t'_tag' in err &&\n\t\t\t\t(err as { readonly _tag?: unknown })._tag === 'WalrusConfigError'\n\t\t\t) {\n\t\t\t\treturn err as WalrusConfigError;\n\t\t\t}\n\t\t\treturn walrusConfigError(\n\t\t\t\t'unknown',\n\t\t\t\t`walrusKnownDeployment: unexpected non-typed throw inside resolveKnownDeploymentOptions: ${\n\t\t\t\t\terr instanceof Error ? err.message : String(err)\n\t\t\t\t}`,\n\t\t\t\tundefined,\n\t\t\t\terr,\n\t\t\t);\n\t\t},\n\t});\n"],"mappings":";;;;;;;;;;;AAiFA,MAAM,4BAaF;CACH,SAAS;EACR,SAAS;EAIT,gBAAgB,8BAA8B;EAC9C,eAAe,8BAA8B;EAC7C,aAAa,8BAA8B;EAC3C,eAAe;EACf,cAAc;EACd,UAAU;CACX;CACA,SAAS;EACR,SAAS;EAIT,gBAAgB,8BAA8B;EAC9C,eAAe,8BAA8B;EAC7C,eAAe;EACf,cAAc;EACd,UAAU;CACX;CACA,QAAQ,EACP,SAAS,SACV;AACD;;;;;AAMA,MAAa,iCACZ,SAC+B;CAC/B,MAAM,MAAM,KAAK,UAAU,0BAA0B,KAAK,WAAW,KAAA;CAMrE,MAAM,iBAAiB,qBAAqB,KAAK,kBAAkB,KAAK,gBAAgB;EACvF,OAAO;EACP,UAAU,EAAE,YACX,kBACC,OACA,4KACD;CACF,CAAC;CAED,MAAM,gBAAgB,qBAAqB,KAAK,iBAAiB,KAAK,eAAe;EACpF,OAAO;EACP,UAAU,EAAE,YACX,kBACC,OACA,2KACD;CACF,CAAC;CAED,MAAM,QAAQ,KAAK;CACnB,IAAI,CAAC,OAKJ,MAAM,kBACL,SACA,0EACW,KAAK,WAAW,SAAS,4CACpC,oJAED;CAGD,MAAM,gBAAgB,KAAK,iBAAiB,KAAK,iBAAiB;CAClE,MAAM,eAAe,KAAK,gBAAgB,KAAK,gBAAgB;CAC/D,MAAM,WAAW,KAAK,YAAY,KAAK,YAAY,iBAAiB;CAMpE,OAAO;EACN,MAAM;EACN,SAAS,KAAK,WAAW;EACzB;EACA;EACA,aAAa,KAAK,eAAe,KAAK,eAAe,CAAC;EACtD;EACA;EACA;EACA;CACD;AACD;;;;AAKA,MAAa,uBACZ,SAEA,OAAO,IAAI;CACV,WAAW,8BAA8B,IAAI;CAM7C,QAAQ,QAA2B;EAClC,IACC,OAAO,QAAQ,YACf,QAAQ,QACR,UAAU,OACT,IAAoC,SAAS,qBAE9C,OAAO;EAER,OAAO,kBACN,WACA,2FACC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,KAEhD,KAAA,GACA,GACD;CACD;AACD,CAAC"}
|
|
1
|
+
{"version":3,"file":"known-deploy.mjs","names":[],"sources":["../../../../src/plugins/walrus/mode/known-deploy.ts"],"sourcesContent":["// Walrus mode — known deployment (testnet / mainnet / *-fork via\n// upstream translation).\n//\n// Distilled-doc reference (06-walrus.md §\"Lifecycle: Startup —\n// known deployment\"). Purely synchronous body — no ordered phases.\n// The factory validates required deployment fields and returns a\n// resolved value whose tags are eager `Layer.succeed(...)` constants.\n//\n// Distilled-doc invariants honored:\n// - 15: each of `proxyUrl / aggregatorUrl / publisherUrl / uploadRelayUrl` surfaces\n// INDEPENDENTLY. Encoded as `string | null` in the resolved\n// shape — a given field is null only when THAT specific URL\n// is unresolved (registry default + explicit override both\n// absent). The plugin's projection step publishes each tag\n// when its own URL is present; a missing publisher URL does\n// not suppress an available proxy/aggregator URL.\n// - 16: throw synchronously when `nodes` is missing for a\n// registered network. The on-chain ids\n// (`systemObjectId` / `stakingPoolId` / `exchangeIds`) ARE\n// stable and come from the `@mysten/walrus` SDK package config\n// — we default them from the SDK and let callers override.\n// Only the node committee stays explicit: testnet has 100+\n// nodes that the SDK fetches dynamically, so we don't pin them.\n\nimport { MAINNET_WALRUS_PACKAGE_CONFIG, TESTNET_WALRUS_PACKAGE_CONFIG } from '@mysten/walrus';\nimport { Effect, type Scope } from 'effect';\n\nimport { expectNonEmptyString } from '../../../substrate/runtime/config-validation.ts';\nimport { walrusConfigError, type WalrusConfigError } from '../errors.ts';\nimport type { WalrusStorageNode } from '../storage-nodes.ts';\n\n/** Known networks supported by the registry lookup. */\nexport type WalrusKnownNetwork = 'testnet' | 'mainnet' | 'devnet';\n\n/** Options for the known-deployment mode. */\nexport interface WalrusKnownDeploymentOptions {\n\t/** Network shortcut — looks up per-field defaults from a registry\n\t * baked into this package. */\n\treadonly network?: WalrusKnownNetwork;\n\t/** Per-field overrides for the on-chain ids. For `testnet`/`mainnet`\n\t * these default from the `@mysten/walrus` SDK package config, so\n\t * they're optional; for `devnet` (or when `network` is omitted)\n\t * `systemObjectId`/`stakingPoolId` are required. An explicit value\n\t * here always wins over the registry default. */\n\treadonly systemObjectId?: string;\n\treadonly stakingPoolId?: string;\n\treadonly exchangeIds?: ReadonlyArray<string>;\n\t/** Explicit storage-node committee. Required (throws if missing\n\t * even when `network` is set — distilled-doc invariant 16). */\n\treadonly nodes?: ReadonlyArray<WalrusStorageNode>;\n\treadonly aggregatorUrl?: string;\n\treadonly publisherUrl?: string;\n\treadonly uploadRelayUrl?: string;\n\treadonly proxyUrl?: string;\n}\n\n/** Resolved known-deployment boot artifacts. */\nexport interface KnownDeploymentBootResult {\n\treadonly mode: 'known';\n\t/** The known network's name (`testnet`/`mainnet`/`devnet`) — its stable\n\t * identity. A known remote deployment has no per-boot genesis digest, so\n\t * the network name IS its `chainId` for codegen/snapshot keying. */\n\treadonly network: string;\n\treadonly systemObjectId: string;\n\treadonly stakingPoolId: string;\n\treadonly exchangeIds: ReadonlyArray<string>;\n\treadonly nodes: ReadonlyArray<WalrusStorageNode>;\n\t/** Null only when the read/proxy URL itself is unresolved (no explicit\n\t * `proxyUrl` override, registry proxy URL, or aggregator URL). Surfaces\n\t * independently of `aggregatorUrl` / `publisherUrl` /\n\t * `uploadRelayUrl` — distilled-doc invariant 15. */\n\treadonly proxyUrl: string | null;\n\treadonly aggregatorUrl: string | null;\n\treadonly publisherUrl: string | null;\n\treadonly uploadRelayUrl: string | null;\n}\n\n/** Known-deployment registry — baked-in record per network. The\n * on-chain ids default from the `@mysten/walrus` SDK package config\n * (`{TESTNET,MAINNET}_WALRUS_PACKAGE_CONFIG`) so a caller only needs\n * to supply `nodes`; the aggregator/publisher/upload-relay/proxy URLs are\n * devstack-owned (the SDK ships none). Devnet has no canonical SDK\n * record today, so it keeps no id defaults. */\nconst KNOWN_DEPLOYMENT_REGISTRY: Readonly<\n\tRecord<\n\t\tWalrusKnownNetwork,\n\t\t{\n\t\t\treadonly network: string;\n\t\t\treadonly systemObjectId?: string;\n\t\t\treadonly stakingPoolId?: string;\n\t\t\treadonly exchangeIds?: ReadonlyArray<string>;\n\t\t\treadonly aggregatorUrl?: string;\n\t\t\treadonly publisherUrl?: string;\n\t\t\treadonly uploadRelayUrl?: string;\n\t\t\treadonly proxyUrl?: string;\n\t\t}\n\t>\n> = {\n\ttestnet: {\n\t\tnetwork: 'testnet',\n\t\t// On-chain ids default from the SDK package config; callers may\n\t\t// still override per-field. The URLs below are devstack-owned —\n\t\t// the SDK ships none.\n\t\tsystemObjectId: TESTNET_WALRUS_PACKAGE_CONFIG.systemObjectId,\n\t\tstakingPoolId: TESTNET_WALRUS_PACKAGE_CONFIG.stakingPoolId,\n\t\texchangeIds: TESTNET_WALRUS_PACKAGE_CONFIG.exchangeIds,\n\t\taggregatorUrl: 'https://aggregator.walrus-testnet.walrus.space',\n\t\tpublisherUrl: 'https://publisher.walrus-testnet.walrus.space',\n\t\tuploadRelayUrl: 'https://upload-relay.testnet.walrus.space',\n\t\tproxyUrl: 'https://aggregator.walrus-testnet.walrus.space',\n\t},\n\tmainnet: {\n\t\tnetwork: 'mainnet',\n\t\t// On-chain ids default from the SDK package config; callers may\n\t\t// still override per-field. Mainnet ships no `exchangeIds` in the\n\t\t// SDK config (testnet-only faucet exchange), so it stays absent.\n\t\tsystemObjectId: MAINNET_WALRUS_PACKAGE_CONFIG.systemObjectId,\n\t\tstakingPoolId: MAINNET_WALRUS_PACKAGE_CONFIG.stakingPoolId,\n\t\taggregatorUrl: 'https://aggregator.walrus.space',\n\t\tpublisherUrl: 'https://publisher.walrus.space',\n\t\tuploadRelayUrl: 'https://upload-relay.mainnet.walrus.space',\n\t\tproxyUrl: 'https://aggregator.walrus.space',\n\t},\n\tdevnet: {\n\t\tnetwork: 'devnet',\n\t},\n};\n\n/** Synchronous factory-time validation + projection. Throws (NOT\n * Effect-fail) on missing required fields so misconfiguration trips\n * at the `defineDevstack` call site rather than at deferred\n * Layer.build time. Distilled-doc invariants 14 + 16. */\nexport const resolveKnownDeploymentOptions = (\n\topts: WalrusKnownDeploymentOptions,\n): KnownDeploymentBootResult => {\n\tconst reg = opts.network ? KNOWN_DEPLOYMENT_REGISTRY[opts.network] : undefined;\n\n\t// On-chain ids: caller override (`opts.*`) wins, else the registry\n\t// default sourced from the `@mysten/walrus` SDK package config.\n\t// Testnet/mainnet default from the SDK so only `nodes` is required;\n\t// devnet has no SDK record, so these stay required there.\n\tconst systemObjectId = expectNonEmptyString(opts.systemObjectId ?? reg?.systemObjectId, {\n\t\tfield: 'systemObjectId',\n\t\tmkError: ({ field }) =>\n\t\t\twalrusConfigError(\n\t\t\t\tfield,\n\t\t\t\t`walrusKnownDeployment: 'systemObjectId' is required (testnet/mainnet default from the @mysten/walrus SDK; pass it explicitly for devnet or networks without an SDK record)`,\n\t\t\t),\n\t});\n\n\tconst stakingPoolId = expectNonEmptyString(opts.stakingPoolId ?? reg?.stakingPoolId, {\n\t\tfield: 'stakingPoolId',\n\t\tmkError: ({ field }) =>\n\t\t\twalrusConfigError(\n\t\t\t\tfield,\n\t\t\t\t`walrusKnownDeployment: 'stakingPoolId' is required (testnet/mainnet default from the @mysten/walrus SDK; pass it explicitly for devnet or networks without an SDK record)`,\n\t\t\t),\n\t});\n\n\tconst nodes = opts.nodes;\n\tif (!nodes) {\n\t\t// Distilled-doc invariant 16: even when `network` is set, the\n\t\t// node committee must be explicit. The on-chain ids come from\n\t\t// the SDK package config, but the committee is dynamic — testnet\n\t\t// has 100+ nodes the SDK fetches at runtime, so we don't pin it.\n\t\tthrow walrusConfigError(\n\t\t\t'nodes',\n\t\t\t`walrusKnownDeployment: explicit 'nodes' committee is required — ` +\n\t\t\t\t`Walrus ${opts.network ?? 'custom'} has nodes fetched dynamically by the SDK`,\n\t\t\t`pass an empty array if you accept the SDK-driven committee lookup, ` +\n\t\t\t\t`or use walrus()/walrusFor(network).local({...}) for a local self-hosted cluster`,\n\t\t);\n\t}\n\n\tconst aggregatorUrl = opts.aggregatorUrl ?? reg?.aggregatorUrl ?? null;\n\tconst publisherUrl = opts.publisherUrl ?? reg?.publisherUrl ?? null;\n\tconst uploadRelayUrl = opts.uploadRelayUrl ?? reg?.uploadRelayUrl ?? null;\n\tconst proxyUrl = opts.proxyUrl ?? reg?.proxyUrl ?? aggregatorUrl ?? null;\n\n\t// Invariant 15: surface null per individual URL when missing so the\n\t// plugin's projection can conditionally publish each tag. Previously\n\t// any single missing URL nullified all three, dropping user-supplied\n\t// values for the URLs they did provide.\n\treturn {\n\t\tmode: 'known' as const,\n\t\tnetwork: reg?.network ?? 'custom',\n\t\tsystemObjectId,\n\t\tstakingPoolId,\n\t\texchangeIds: opts.exchangeIds ?? reg?.exchangeIds ?? [],\n\t\tnodes,\n\t\tproxyUrl,\n\t\taggregatorUrl,\n\t\tpublisherUrl,\n\t\tuploadRelayUrl,\n\t};\n};\n\n/** \"Boot\" the known deployment. Purely synchronous projection inside\n * Effect.gen — no I/O, no Scope work. Surfaces as Effect for\n * uniformity with the local-cluster boot signature. */\nexport const bootKnownDeployment = (\n\topts: WalrusKnownDeploymentOptions,\n): Effect.Effect<KnownDeploymentBootResult, WalrusConfigError, Scope.Scope> =>\n\tEffect.try({\n\t\ttry: () => resolveKnownDeploymentOptions(opts),\n\t\t// `resolveKnownDeploymentOptions` throws our typed config error\n\t\t// shape. STYLE_GUIDE §2 forbids bare error casts; runtime-guard\n\t\t// the `_tag` so an unexpected synchronous throw (e.g. a future\n\t\t// helper that throws a stock `TypeError`) is wrapped instead of\n\t\t// silently mis-tagged as `WalrusConfigError`.\n\t\tcatch: (err): WalrusConfigError => {\n\t\t\tif (\n\t\t\t\ttypeof err === 'object' &&\n\t\t\t\terr !== null &&\n\t\t\t\t'_tag' in err &&\n\t\t\t\t(err as { readonly _tag?: unknown })._tag === 'WalrusConfigError'\n\t\t\t) {\n\t\t\t\treturn err as WalrusConfigError;\n\t\t\t}\n\t\t\treturn walrusConfigError(\n\t\t\t\t'unknown',\n\t\t\t\t`walrusKnownDeployment: unexpected non-typed throw inside resolveKnownDeploymentOptions: ${\n\t\t\t\t\terr instanceof Error ? err.message : String(err)\n\t\t\t\t}`,\n\t\t\t\tundefined,\n\t\t\t\terr,\n\t\t\t);\n\t\t},\n\t});\n"],"mappings":";;;;;;;;;;;AAmFA,MAAM,4BAcF;CACH,SAAS;EACR,SAAS;EAIT,gBAAgB,8BAA8B;EAC9C,eAAe,8BAA8B;EAC7C,aAAa,8BAA8B;EAC3C,eAAe;EACf,cAAc;EACd,gBAAgB;EAChB,UAAU;CACX;CACA,SAAS;EACR,SAAS;EAIT,gBAAgB,8BAA8B;EAC9C,eAAe,8BAA8B;EAC7C,eAAe;EACf,cAAc;EACd,gBAAgB;EAChB,UAAU;CACX;CACA,QAAQ,EACP,SAAS,SACV;AACD;;;;;AAMA,MAAa,iCACZ,SAC+B;CAC/B,MAAM,MAAM,KAAK,UAAU,0BAA0B,KAAK,WAAW,KAAA;CAMrE,MAAM,iBAAiB,qBAAqB,KAAK,kBAAkB,KAAK,gBAAgB;EACvF,OAAO;EACP,UAAU,EAAE,YACX,kBACC,OACA,4KACD;CACF,CAAC;CAED,MAAM,gBAAgB,qBAAqB,KAAK,iBAAiB,KAAK,eAAe;EACpF,OAAO;EACP,UAAU,EAAE,YACX,kBACC,OACA,2KACD;CACF,CAAC;CAED,MAAM,QAAQ,KAAK;CACnB,IAAI,CAAC,OAKJ,MAAM,kBACL,SACA,0EACW,KAAK,WAAW,SAAS,4CACpC,oJAED;CAGD,MAAM,gBAAgB,KAAK,iBAAiB,KAAK,iBAAiB;CAClE,MAAM,eAAe,KAAK,gBAAgB,KAAK,gBAAgB;CAC/D,MAAM,iBAAiB,KAAK,kBAAkB,KAAK,kBAAkB;CACrE,MAAM,WAAW,KAAK,YAAY,KAAK,YAAY,iBAAiB;CAMpE,OAAO;EACN,MAAM;EACN,SAAS,KAAK,WAAW;EACzB;EACA;EACA,aAAa,KAAK,eAAe,KAAK,eAAe,CAAC;EACtD;EACA;EACA;EACA;EACA;CACD;AACD;;;;AAKA,MAAa,uBACZ,SAEA,OAAO,IAAI;CACV,WAAW,8BAA8B,IAAI;CAM7C,QAAQ,QAA2B;EAClC,IACC,OAAO,QAAQ,YACf,QAAQ,QACR,UAAU,OACT,IAAoC,SAAS,qBAE9C,OAAO;EAER,OAAO,kBACN,WACA,2FACC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,KAEhD,KAAA,GACA,GACD;CACD;AACD,CAAC"}
|
|
@@ -34,14 +34,18 @@ interface WalrusLocalClusterOptions {
|
|
|
34
34
|
/** Local app-facing publisher service. Defaults to enabled. Set
|
|
35
35
|
* `false` to run storage nodes without the simple publish API. */
|
|
36
36
|
readonly publisher?: boolean | WalrusLocalPublisherOptions;
|
|
37
|
+
/** Local app-facing upload relay service. Defaults to enabled. Set
|
|
38
|
+
* `false` to run storage nodes without the SDK upload relay API. */
|
|
39
|
+
readonly uploadRelay?: boolean | WalrusLocalUploadRelayOptions;
|
|
37
40
|
}
|
|
38
41
|
interface WalrusLocalServiceOptions {
|
|
39
42
|
/** In-container port passed to the release service's
|
|
40
|
-
*
|
|
41
|
-
* publisher/aggregator
|
|
43
|
+
* bind-address flag. Defaults to the service-specific release
|
|
44
|
+
* port: publisher/aggregator 31415, upload relay 3000. */
|
|
42
45
|
readonly port?: number;
|
|
43
46
|
}
|
|
44
47
|
type WalrusLocalPublisherOptions = WalrusLocalServiceOptions;
|
|
48
|
+
type WalrusLocalUploadRelayOptions = WalrusLocalServiceOptions;
|
|
45
49
|
//#endregion
|
|
46
50
|
export { WalrusLocalClusterOptions, WalrusLocalPublisherOptions, WalrusLocalServiceOptions };
|
|
47
51
|
//# sourceMappingURL=local-cluster.d.mts.map
|
|
@@ -8,7 +8,7 @@ import { resolveWalExchange } from "../wal-swap.mjs";
|
|
|
8
8
|
import { makeWalFaucetStrategy, resolveWalCoinType, walPackageIdFromCoinType } from "../faucet-strategy.mjs";
|
|
9
9
|
import { deployWalrusContracts } from "../deploy.mjs";
|
|
10
10
|
import { resolveCargoImage } from "../bootstrap-assets/cargo-image.mjs";
|
|
11
|
-
import { startWalrusClientServices } from "../client-services.mjs";
|
|
11
|
+
import { DEFAULT_WALRUS_CLIENT_SERVICE_PORT, DEFAULT_WALRUS_UPLOAD_RELAY_SERVICE_PORT, startWalrusClientServices } from "../client-services.mjs";
|
|
12
12
|
import { Effect } from "effect";
|
|
13
13
|
//#region src/plugins/walrus/mode/local-cluster.ts
|
|
14
14
|
/** Synchronous factory-time validation. Throws (NOT Effect-fail) so
|
|
@@ -43,14 +43,15 @@ const expectOptionalPort = (value, field) => value === void 0 ? void 0 : expectP
|
|
|
43
43
|
field,
|
|
44
44
|
mkError: ({ field: f, message, hint }) => walrusConfigError(f, `walrusLocalCluster: ${f} ${message}`, hint)
|
|
45
45
|
});
|
|
46
|
-
const resolveLocalServiceBase = (value, fieldPrefix) => {
|
|
46
|
+
const resolveLocalServiceBase = (value, fieldPrefix, defaultPort) => {
|
|
47
47
|
if (value === false) return null;
|
|
48
|
-
return { port: expectOptionalPort((value === void 0 || value === true ? {} : value).port, `${fieldPrefix}.port`) ??
|
|
48
|
+
return { port: expectOptionalPort((value === void 0 || value === true ? {} : value).port, `${fieldPrefix}.port`) ?? defaultPort };
|
|
49
49
|
};
|
|
50
50
|
const resolveLocalServicesOptions = (opts) => {
|
|
51
51
|
return {
|
|
52
|
-
aggregator: resolveLocalServiceBase(opts.aggregator, "aggregator"),
|
|
53
|
-
publisher: resolveLocalServiceBase(opts.publisher, "publisher")
|
|
52
|
+
aggregator: resolveLocalServiceBase(opts.aggregator, "aggregator", DEFAULT_WALRUS_CLIENT_SERVICE_PORT),
|
|
53
|
+
publisher: resolveLocalServiceBase(opts.publisher, "publisher", DEFAULT_WALRUS_CLIENT_SERVICE_PORT),
|
|
54
|
+
uploadRelay: resolveLocalServiceBase(opts.uploadRelay, "uploadRelay", DEFAULT_WALRUS_UPLOAD_RELAY_SERVICE_PORT)
|
|
54
55
|
};
|
|
55
56
|
};
|
|
56
57
|
/** Boot the local cluster. Returns the resolved value the plugin
|
|
@@ -137,7 +138,8 @@ const bootLocalCluster = (deps, opts) => Effect.gen(function* () {
|
|
|
137
138
|
}, `${walrusImage.tag}-${nodeImageScope}-node-${i}`));
|
|
138
139
|
const clientServiceImages = yield* Effect.all({
|
|
139
140
|
aggregator: opts.aggregator === null ? Effect.succeed(null) : resolveServiceImage("aggregator"),
|
|
140
|
-
publisher: opts.publisher === null ? Effect.succeed(null) : resolveServiceImage("publisher")
|
|
141
|
+
publisher: opts.publisher === null ? Effect.succeed(null) : resolveServiceImage("publisher"),
|
|
142
|
+
uploadRelay: opts.uploadRelay === null ? Effect.succeed(null) : resolveServiceImage("upload-relay")
|
|
141
143
|
});
|
|
142
144
|
yield* setCurrentPluginPhase(`starting ${opts.nodeCount} Walrus storage node${opts.nodeCount === 1 ? "" : "s"}`);
|
|
143
145
|
const { nodes } = yield* startStorageNodes(deps.runtime, {
|
|
@@ -164,7 +166,8 @@ const bootLocalCluster = (deps, opts) => Effect.gen(function* () {
|
|
|
164
166
|
images: clientServiceImages,
|
|
165
167
|
options: {
|
|
166
168
|
aggregator: opts.aggregator,
|
|
167
|
-
publisher: opts.publisher
|
|
169
|
+
publisher: opts.publisher,
|
|
170
|
+
uploadRelay: opts.uploadRelay
|
|
168
171
|
},
|
|
169
172
|
walrusNetworkName: deps.walrusNetworkName,
|
|
170
173
|
suiNetworkName: deps.suiNetworkName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-cluster.mjs","names":["brandContentHash"],"sources":["../../../../src/plugins/walrus/mode/local-cluster.ts"],"sourcesContent":["// Walrus mode — local cluster.\n//\n// Distilled-doc reference (06-walrus.md §\"Lifecycle: Startup —\n// local cluster\"). Eight ordered phases:\n//\n// 0. Yield deps (Sui, Identity, ChainProbe, faucet strategy opt).\n// 1. Image build — upstream cargo image (bootstrap asset) +\n// wrapper image (inline; content-addressed on\n// the upstream tag + suiVersion).\n// 1b. Docker network create — per-stack `/24` via\n// `Docker.networkCreate(networkName, {subnet})`.\n// 2. Deploy contracts — via `ArtifactPublisher`. The\n// publisher folds chainId into the cache\n// key; verify probes BOTH the on-disk\n// `deploy` file AND on-chain object\n// existence; produce runs the deploy\n// one-shot.\n// 3. Register committee — omitted entirely (no placeholder).\n// 4. Storage nodes — parallel boot of N storage-node containers.\n// 5. Exchange resolution — on-chain getObject + parse type.\n// Degrades to undefined on\n// OBJECT_NOT_FOUND.\n// 6. WAL faucet strategy register — opt-in when an exchange exists.\n// 7. Registries — package + endpoint + walrus-state.\n//\n// What this file owns: the dispatch shape + the typed options\n// surface + the synchronous factory-time validation.\n//\n// What this file delegates: container/Move heavy paths live in\n// `storage-nodes.ts`, `deploy.ts`, and the `ArtifactPublisher`\n// primitive.\n\nimport { Effect, FileSystem, Path, type Scope } from 'effect';\n\nimport type {\n\tArtifactPublishError,\n\tArtifactPublisher,\n} from '../../../primitives/artifact-publisher.ts';\nimport type { ChainProbe } from '../../../contracts/chain-probe.ts';\nimport type { ContainerRuntime } from '../../../contracts/container-runtime.ts';\nimport type { SuiProbeKey } from '../../sui/index.ts';\nimport { contentHash as brandContentHash } from '../../../substrate/brand.ts';\nimport { expectPort, expectPositiveInteger } from '../../../substrate/runtime/config-validation.ts';\nimport { setCurrentPluginPhase } from '../../../substrate/runtime/current-plugin.ts';\nimport { walrusConfigError, walrusPluginError, type WalrusError } from '../errors.ts';\nimport { WALRUS_MAX_NODE_COUNT } from '../routable.ts';\nimport type { WalrusStorageNode } from '../storage-nodes.ts';\nimport {\n\tDEFAULT_NODE_READY_TIMEOUT_MS,\n\tDEFAULT_CONTAINER_API_PORT,\n\tWALRUS_NODE_IP_BASE,\n\tcomputeCommitteeHostname,\n\tstartStorageNodes,\n} from '../storage-nodes.ts';\nimport { deployWalrusContracts, type CachedDeployState } from '../deploy.ts';\nimport {\n\tDEFAULT_SUI_VERSION,\n\tDEFAULT_WALRUS_REF,\n\tresolveCargoImage,\n} from '../bootstrap-assets/cargo-image.ts';\nimport {\n\tDEFAULT_WALRUS_CLIENT_SERVICE_PORT,\n\tstartWalrusClientServices,\n\ttype WalrusClientServices,\n} from '../client-services.ts';\nimport {\n\tmakeWalFaucetStrategy,\n\twalPackageIdFromCoinType,\n\tresolveWalCoinType,\n\ttype WalFaucetStrategy,\n} from '../faucet-strategy.ts';\nimport { resolveWalExchange, type WalExchangeHandle, type WalSwapSdk } from '../wal-swap.ts';\n\n/** Options for the local-cluster mode (06-walrus.md\n * §\"Configuration\"). */\nexport interface WalrusLocalClusterOptions {\n\t/** Engine row name + on-disk dir suffix + registry key.\n\t * Default `'walrus'`. */\n\treadonly name?: string;\n\t/** Number of storage-node containers. Must be `>= 1` (synchronous\n\t * factory-time guard). Default `1`. */\n\treadonly nodeCount?: number;\n\t/** Total shards distributed across the committee. Must be\n\t * `>= nodeCount`. Default `100` (distilled-doc default block). */\n\treadonly shards?: number;\n\t/** Pinned walrus release — drives the wrapper image build.\n\t * Default `'testnet-v1.49.1'`. */\n\treadonly version?: string;\n\t/** Sui release whose binary the wrapper image bakes (distilled-\n\t * doc invariant 24). Default `'devnet-v1.71.0'`. */\n\treadonly suiVersion?: string;\n\t/** Port each storage node binds inside the container. Default\n\t * 9185 (same as the router entrypoint — invariant 9). */\n\treadonly containerApiPort?: number;\n\t/** Walrus epoch length passed to `walrus-deploy --epoch-duration`.\n\t * Default `'24h'`. */\n\treadonly epochDuration?: string;\n\t/** Per-node TCP ready-probe timeout (ms). Defaults to\n\t * `DEFAULT_NODE_READY_TIMEOUT_MS`. */\n\treadonly readyTimeoutMs?: number;\n\t/** Local app-facing aggregator service. Defaults to enabled. Set\n\t * `false` to expose only storage-node routes. */\n\treadonly aggregator?: boolean | WalrusLocalServiceOptions;\n\t/** Local app-facing publisher service. Defaults to enabled. Set\n\t * `false` to run storage nodes without the simple publish API. */\n\treadonly publisher?: boolean | WalrusLocalPublisherOptions;\n}\n\nexport interface WalrusLocalServiceOptions {\n\t/** In-container port passed to the release service's\n\t * `--bind-address 0.0.0.0:<port>`. Defaults to the Walrus CLI's\n\t * publisher/aggregator port. */\n\treadonly port?: number;\n}\n\nexport type WalrusLocalPublisherOptions = WalrusLocalServiceOptions;\n\n/** Resolved local-cluster boot artifacts. */\nexport interface LocalClusterBootResult {\n\treadonly mode: 'local';\n\treadonly deploy: CachedDeployState;\n\treadonly walrusPackageId: string;\n\treadonly walPackageId: string;\n\treadonly nodes: ReadonlyArray<WalrusStorageNode>;\n\treadonly exchangeObjectId: string | undefined;\n\treadonly exchange: WalExchangeHandle | null;\n\treadonly clientServices: WalrusClientServices;\n\t/** WAL faucet strategy — present only when the local cluster has a\n\t * non-empty exchange object. The barrel registers this onto the\n\t * `coinType:<fullCoinType>` strategy contributor decl. `null`\n\t * otherwise. */\n\treadonly walFaucetStrategy: WalFaucetStrategy | null;\n\treadonly walCoinType: string;\n}\n\n/** Defaults applied to options. */\nexport interface ResolvedLocalClusterOptions {\n\treadonly name: string;\n\treadonly nodeCount: number;\n\treadonly shards: number;\n\treadonly version: string;\n\treadonly suiVersion: string;\n\treadonly containerApiPort: number;\n\treadonly epochDuration: string;\n\treadonly readyTimeoutMs: number;\n\treadonly aggregator: ResolvedWalrusLocalServiceOptions | null;\n\treadonly publisher: ResolvedWalrusLocalPublisherOptions | null;\n}\n\nexport interface ResolvedWalrusLocalServiceOptions {\n\treadonly port: number;\n}\n\nexport type ResolvedWalrusLocalPublisherOptions = ResolvedWalrusLocalServiceOptions;\n\n/** Synchronous factory-time validation. Throws (NOT Effect-fail) so\n * misconfiguration trips at the `defineDevstack` call site rather\n * than at deferred Layer.build time. Distilled-doc invariant 11. */\nexport const resolveLocalClusterOptions = (\n\topts: WalrusLocalClusterOptions,\n): ResolvedLocalClusterOptions => {\n\tconst nodeCount = expectPositiveInteger(opts.nodeCount ?? 1, {\n\t\tfield: 'nodeCount',\n\t\tmkError: ({ field, message, hint }) =>\n\t\t\twalrusConfigError(\n\t\t\t\tfield,\n\t\t\t\t`walrusLocalCluster: nodeCount must be >= 1 (got ${String(opts.nodeCount ?? 1)})`,\n\t\t\t\thint ?? message,\n\t\t\t),\n\t\thint: 'set nodeCount to a positive integer (default 1)',\n\t});\n\tif (nodeCount > WALRUS_MAX_NODE_COUNT) {\n\t\tthrow walrusConfigError(\n\t\t\t'nodeCount',\n\t\t\t`walrusLocalCluster: nodeCount (${nodeCount}) exceeds the maximum ${WALRUS_MAX_NODE_COUNT} ` +\n\t\t\t\t`pre-declared Traefik entrypoints (walrus-node-0..${WALRUS_MAX_NODE_COUNT - 1}).`,\n\t\t\t`reduce nodeCount or raise WALRUS_MAX_NODE_COUNT in plugins/walrus/routable.ts`,\n\t\t);\n\t}\n\tconst shards = expectPositiveInteger(opts.shards ?? 100, {\n\t\tfield: 'shards',\n\t\tmkError: ({ field, message, hint }) =>\n\t\t\twalrusConfigError(\n\t\t\t\tfield,\n\t\t\t\t`walrusLocalCluster: shards must be a positive integer (got ${String(opts.shards ?? 100)})`,\n\t\t\t\thint ?? message,\n\t\t\t),\n\t\thint: 'set shards to a positive integer (default 100)',\n\t});\n\tif (shards < nodeCount) {\n\t\tthrow walrusConfigError(\n\t\t\t'shards',\n\t\t\t`walrusLocalCluster: shards (${shards}) must be >= nodeCount (${nodeCount})`,\n\t\t\t`set shards >= ${nodeCount} (default 100)`,\n\t\t);\n\t}\n\treturn {\n\t\tname: opts.name ?? 'walrus',\n\t\tnodeCount,\n\t\tshards,\n\t\tversion: opts.version ?? DEFAULT_WALRUS_REF,\n\t\tsuiVersion: opts.suiVersion ?? DEFAULT_SUI_VERSION,\n\t\tcontainerApiPort: opts.containerApiPort ?? DEFAULT_CONTAINER_API_PORT,\n\t\tepochDuration: opts.epochDuration ?? '24h',\n\t\treadyTimeoutMs: opts.readyTimeoutMs ?? DEFAULT_NODE_READY_TIMEOUT_MS,\n\t\t...resolveLocalServicesOptions(opts),\n\t};\n};\n\nconst expectOptionalPort = (value: number | undefined, field: string): number | undefined =>\n\tvalue === undefined\n\t\t? undefined\n\t\t: expectPort(value, {\n\t\t\t\tfield,\n\t\t\t\tmkError: ({ field: f, message, hint }) =>\n\t\t\t\t\twalrusConfigError(f, `walrusLocalCluster: ${f} ${message}`, hint),\n\t\t\t});\n\nconst resolveLocalServiceBase = (\n\tvalue: boolean | WalrusLocalServiceOptions | undefined,\n\tfieldPrefix: 'aggregator' | 'publisher',\n): ResolvedWalrusLocalServiceOptions | null => {\n\tif (value === false) return null;\n\tconst authored = value === undefined || value === true ? {} : value;\n\treturn {\n\t\tport:\n\t\t\texpectOptionalPort(authored.port, `${fieldPrefix}.port`) ??\n\t\t\tDEFAULT_WALRUS_CLIENT_SERVICE_PORT,\n\t};\n};\n\nconst resolveLocalServicesOptions = (\n\topts: WalrusLocalClusterOptions,\n): Pick<ResolvedLocalClusterOptions, 'aggregator' | 'publisher'> => {\n\treturn {\n\t\taggregator: resolveLocalServiceBase(opts.aggregator, 'aggregator'),\n\t\tpublisher: resolveLocalServiceBase(opts.publisher, 'publisher'),\n\t};\n};\n\n/** Dependencies the local service consumes at acquire-time. */\nexport interface LocalClusterDeps {\n\treadonly runtime: ContainerRuntime;\n\treadonly publisher: ArtifactPublisher;\n\treadonly probe: ChainProbe<SuiProbeKey>;\n\treadonly suiSdk: WalSwapSdk;\n\treadonly suiChainId: string;\n\treadonly suiRpcUrlInNetwork: string;\n\treadonly walrusFaucetUrlInNetwork: string;\n\treadonly waitForFundsReady: Effect.Effect<void, unknown>;\n\treadonly app: string;\n\treadonly stack: string;\n\t/** Pre-allocated /24 prefix from `subnetForStack`. */\n\treadonly subnetPrefix: string;\n\treadonly walrusNetworkName: string;\n\treadonly suiNetworkName: string;\n\treadonly deployHostMountPath: string;\n\t/** On-disk per-stack root from `StackPathsService.stackRoot`. The\n\t * walrus bind-mount is computed off this; `deployHostMountPath`\n\t * MUST live under it. */\n\treadonly stackRoot: string;\n}\n\n/** Boot the local cluster. Returns the resolved value the plugin\n * projects onto its tags.\n *\n * Steps:\n * - Image build — `resolveCargoImage` (bootstrap asset).\n * Honors `WALRUS_CARGO_IMAGE_OVERRIDE` for the pre-baked path.\n * - Docker network ensure — `runtime.ensureNetwork(walrusNet)`.\n * The sui network is owned by the sui plugin; we attach to it\n * as a secondary network on each storage node.\n * - Deploy contracts — `deployWalrusContracts` dispatches through\n * the artifact publisher primitive; the produce body runs the walrus deploy\n * one-shot.\n * - Storage nodes — parallel boot via `startStorageNodes`.\n * - WAL faucet strategy — constructed if `state.exchangeObject`\n * exists. Surfaced on the boot result; the barrel registers\n * the contributor decl.\n *\n * Per-step failures surface as `WalrusPluginError` with the phase\n * vocabulary from `errors.ts`; artifact publisher failures pass through as\n * `ArtifactPublishError`. The plugin narration vocabulary anchors\n * on these phase tags.\n */\nexport const bootLocalCluster = (\n\tdeps: LocalClusterDeps,\n\topts: ResolvedLocalClusterOptions,\n): Effect.Effect<\n\tLocalClusterBootResult,\n\tWalrusError | ArtifactPublishError,\n\tScope.Scope | FileSystem.FileSystem | Path.Path\n> =>\n\tEffect.gen(function* () {\n\t\t// ---- cargo image (bootstrap asset) -----------------------\n\t\t// The cargo image is content-addressed; the resolver owns the\n\t\t// cache check + the registry-tag override fast path.\n\t\tyield* setCurrentPluginPhase(`resolving Walrus image ${opts.version}`);\n\t\tconst walrusImage = yield* resolveCargoImage(deps.runtime, {\n\t\t\twalrusRef: opts.version,\n\t\t\tsuiVersion: opts.suiVersion,\n\t\t\towner: { app: deps.app, stack: deps.stack },\n\t\t});\n\n\t\t// ---- docker network ensure ------------------------------\n\t\tyield* setCurrentPluginPhase(`ensuring Walrus network ${deps.walrusNetworkName}`);\n\t\tyield* deps.runtime\n\t\t\t.ensureNetwork({\n\t\t\t\tname: deps.walrusNetworkName,\n\t\t\t\tapp: deps.app,\n\t\t\t\tstack: deps.stack,\n\t\t\t})\n\t\t\t.pipe(\n\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\twalrusPluginError(\n\t\t\t\t\t\t\t'cluster-network',\n\t\t\t\t\t\t\t`walrus: ensureNetwork('${deps.walrusNetworkName}') failed: ${cause.reason}: ${cause.detail}`,\n\t\t\t\t\t\t\t{ cause },\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\n\t\t// ---- deploy via ArtifactPublisher ----------------\n\t\t// The deploy one-shot needs the walrus image + sui network so it\n\t\t// can dial the per-stack sui RPC + faucet over docker DNS.\n\t\t//\n\t\t// Per-node committee hostnames are Docker-network aliases, because the\n\t\t// real Rust publisher/aggregator read these public_host values from chain\n\t\t// and dial storage nodes from inside the Walrus network. Router-facing\n\t\t// `.localhost` hostnames are still produced by `startStorageNodes` for\n\t\t// devstack's public diagnostic URLs.\n\t\tconst committeeHosts = Array.from({ length: opts.nodeCount }, (_, i) =>\n\t\t\tcomputeCommitteeHostname(i),\n\t\t).join(',');\n\t\tconst listeningIps = Array.from(\n\t\t\t{ length: opts.nodeCount },\n\t\t\t(_, i) => `${deps.subnetPrefix}.${WALRUS_NODE_IP_BASE + i}`,\n\t\t).join(',');\n\n\t\tyield* setCurrentPluginPhase(\n\t\t\t`deploying Walrus contracts (${opts.nodeCount} node${opts.nodeCount === 1 ? '' : 's'}, ${opts.shards} shards)`,\n\t\t);\n\t\tconst { state } = yield* deployWalrusContracts(deps.publisher, deps.probe, deps.runtime, {\n\t\t\twalrusName: opts.name,\n\t\t\tchainId: deps.suiChainId,\n\t\t\tcontentHash: brandContentHash(\n\t\t\t\t`walrus|${opts.version}|${opts.suiVersion}|${opts.nodeCount}|${opts.shards}|${opts.epochDuration}|committee=${committeeHosts}|listen=${listeningIps}`,\n\t\t\t),\n\t\t\toutputDirHostPath: deps.deployHostMountPath,\n\t\t\tstackRoot: deps.stackRoot,\n\t\t\tsuiRpcUrlInNetwork: deps.suiRpcUrlInNetwork,\n\t\t\twalrusFaucetUrlInNetwork: deps.walrusFaucetUrlInNetwork,\n\t\t\twaitForFundsReady: deps.waitForFundsReady,\n\t\t\tcommitteeSize: opts.nodeCount,\n\t\t\tshards: opts.shards,\n\t\t\tepochDuration: opts.epochDuration,\n\t\t\tpublicHostsCsv: committeeHosts,\n\t\t\tlisteningIpsCsv: listeningIps,\n\t\t\twalrusImage,\n\t\t\tsuiNetworkName: deps.suiNetworkName,\n\t\t});\n\t\tconst deployConfigHash = [\n\t\t\t'walrus-deploy',\n\t\t\tstate.walrusPackageId,\n\t\t\tstate.systemObject,\n\t\t\tstate.stakingObject,\n\t\t\tstate.exchangeObject ?? 'no-exchange',\n\t\t].join('|');\n\n\t\t// Per-node image tags. Each storage node runs under its OWN tag (a cheap\n\t\t// re-tag of the one content-cached cargo build) so its committed writable\n\t\t// layer — the RocksDB holding that node's slivers — promotes to a DISTINCT\n\t\t// image on snapshot restore. A single shared tag would make the restore\n\t\t// image-promote collapse all N committed layers onto one tag\n\t\t// (last-write-wins): N-1 nodes boot on the wrong layer and every\n\t\t// pre-snapshot blob is orphaned. The tag is scoped by the deployed\n\t\t// `walrusPackageId` — stable across boot1 -> restore -> boot2 (the deploy\n\t\t// cache reuses the same id, so the restore's promote target matches what\n\t\t// boot2 requests), but DISTINCT per fresh deployment, so a later cold boot\n\t\t// on a new chain never inherits a prior run's committed node layers (which\n\t\t// carry a stale committee and would reject writes). The base `walrusImage`\n\t\t// (shared) still backs the transient deploy one-shot (`--rm`, unsnapshotted).\n\t\tconst nodeImageScope = state.walrusPackageId.replace(/^0x/, '').slice(0, 12);\n\t\tconst resolveServiceImage = (role: 'aggregator' | 'publisher') =>\n\t\t\tresolveCargoImage(\n\t\t\t\tdeps.runtime,\n\t\t\t\t{\n\t\t\t\t\twalrusRef: opts.version,\n\t\t\t\t\tsuiVersion: opts.suiVersion,\n\t\t\t\t\towner: { app: deps.app, stack: deps.stack },\n\t\t\t\t},\n\t\t\t\t`${walrusImage.tag}-${nodeImageScope}-${role}`,\n\t\t\t);\n\t\tconst nodeImages = yield* Effect.forEach(\n\t\t\tArray.from({ length: opts.nodeCount }, (_, i) => i),\n\t\t\t(i) =>\n\t\t\t\tresolveCargoImage(\n\t\t\t\t\tdeps.runtime,\n\t\t\t\t\t{\n\t\t\t\t\t\twalrusRef: opts.version,\n\t\t\t\t\t\tsuiVersion: opts.suiVersion,\n\t\t\t\t\t\towner: { app: deps.app, stack: deps.stack },\n\t\t\t\t\t},\n\t\t\t\t\t`${walrusImage.tag}-${nodeImageScope}-node-${i}`,\n\t\t\t\t),\n\t\t);\n\t\tconst clientServiceImages = yield* Effect.all({\n\t\t\taggregator:\n\t\t\t\topts.aggregator === null ? Effect.succeed(null) : resolveServiceImage('aggregator'),\n\t\t\tpublisher: opts.publisher === null ? Effect.succeed(null) : resolveServiceImage('publisher'),\n\t\t});\n\n\t\t// ---- storage nodes — parallel boot ----------------------\n\t\tyield* setCurrentPluginPhase(\n\t\t\t`starting ${opts.nodeCount} Walrus storage node${opts.nodeCount === 1 ? '' : 's'}`,\n\t\t);\n\t\tconst { nodes } = yield* startStorageNodes(deps.runtime, {\n\t\t\tapp: deps.app,\n\t\t\tstack: deps.stack,\n\t\t\twalrusName: opts.name,\n\t\t\timages: nodeImages,\n\t\t\tnodeCount: opts.nodeCount,\n\t\t\tsubnetPrefix: deps.subnetPrefix,\n\t\t\tcontainerApiPort: opts.containerApiPort,\n\t\t\twalrusNetworkName: deps.walrusNetworkName,\n\t\t\tsuiNetworkName: deps.suiNetworkName,\n\t\t\tdeployHostMountPath: deps.deployHostMountPath,\n\t\t\tstackRoot: deps.stackRoot,\n\t\t\tdeployConfigHash,\n\t\t\treadyTimeoutMs: opts.readyTimeoutMs,\n\t\t});\n\n\t\t// `nodeCount >= 1` is enforced synchronously already; this is\n\t\t// defense-in-depth.\n\t\tif (nodes.length === 0) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\twalrusPluginError('storage-node', 'walrus: at least one storage node is required'),\n\t\t\t);\n\t\t}\n\n\t\t// ---- app-facing Rust publisher / aggregator services ----\n\t\tyield* setCurrentPluginPhase('starting Walrus client services');\n\t\tconst clientServices = yield* startWalrusClientServices(deps.runtime, {\n\t\t\tapp: deps.app,\n\t\t\tstack: deps.stack,\n\t\t\twalrusName: opts.name,\n\t\t\timages: clientServiceImages,\n\t\t\toptions: {\n\t\t\t\taggregator: opts.aggregator,\n\t\t\t\tpublisher: opts.publisher,\n\t\t\t},\n\t\t\twalrusNetworkName: deps.walrusNetworkName,\n\t\t\tsuiNetworkName: deps.suiNetworkName,\n\t\t\tdeployHostMountPath: deps.deployHostMountPath,\n\t\t\tstackRoot: deps.stackRoot,\n\t\t\tdeployConfigHash,\n\t\t\tsuiRpcUrlInNetwork: deps.suiRpcUrlInNetwork,\n\t\t\treadyTimeoutMs: opts.readyTimeoutMs,\n\t\t});\n\n\t\t// ---- exchange resolution + WAL funding strategy ------\n\t\t// Register a `coinType:<fullCoinType>` strategy that swaps the\n\t\t// requesting account's SUI into WAL on demand. Accounts opt in via\n\t\t// their normal `funding` list.\n\t\tyield* setCurrentPluginPhase('resolving WAL exchange');\n\t\tconst exchange = yield* resolveWalExchange(deps.probe, state.exchangeObject);\n\t\tconst walFaucetStrategy: WalFaucetStrategy | null = exchange\n\t\t\t? makeWalFaucetStrategy({\n\t\t\t\t\texchange,\n\t\t\t\t\tsdk: deps.suiSdk,\n\t\t\t\t})\n\t\t\t: null;\n\t\tconst resolvedWalCoinType = yield* resolveWalCoinType({\n\t\t\tprobe: deps.probe,\n\t\t\ttreasuryObjectId: state.treasuryObject,\n\t\t\tdeployPackageId: state.walrusPackageId,\n\t\t\trequireTreasuryObject: exchange !== null,\n\t\t});\n\t\tconst resolvedWalPackageId = walPackageIdFromCoinType(resolvedWalCoinType);\n\n\t\treturn {\n\t\t\tmode: 'local' as const,\n\t\t\tdeploy: state,\n\t\t\twalrusPackageId: state.walrusPackageId,\n\t\t\twalPackageId: resolvedWalPackageId,\n\t\t\tnodes,\n\t\t\texchangeObjectId: state.exchangeObject,\n\t\t\texchange,\n\t\t\tclientServices,\n\t\t\twalFaucetStrategy,\n\t\t\twalCoinType: resolvedWalCoinType,\n\t\t};\n\t});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"local-cluster.mjs","names":["brandContentHash"],"sources":["../../../../src/plugins/walrus/mode/local-cluster.ts"],"sourcesContent":["// Walrus mode — local cluster.\n//\n// Distilled-doc reference (06-walrus.md §\"Lifecycle: Startup —\n// local cluster\"). Eight ordered phases:\n//\n// 0. Yield deps (Sui, Identity, ChainProbe, faucet strategy opt).\n// 1. Image build — upstream cargo image (bootstrap asset) +\n// wrapper image (inline; content-addressed on\n// the upstream tag + suiVersion).\n// 1b. Docker network create — per-stack `/24` via\n// `Docker.networkCreate(networkName, {subnet})`.\n// 2. Deploy contracts — via `ArtifactPublisher`. The\n// publisher folds chainId into the cache\n// key; verify probes BOTH the on-disk\n// `deploy` file AND on-chain object\n// existence; produce runs the deploy\n// one-shot.\n// 3. Register committee — omitted entirely (no placeholder).\n// 4. Storage nodes — parallel boot of N storage-node containers.\n// 5. Exchange resolution — on-chain getObject + parse type.\n// Degrades to undefined on\n// OBJECT_NOT_FOUND.\n// 6. WAL faucet strategy register — opt-in when an exchange exists.\n// 7. Registries — package + endpoint + walrus-state.\n//\n// What this file owns: the dispatch shape + the typed options\n// surface + the synchronous factory-time validation.\n//\n// What this file delegates: container/Move heavy paths live in\n// `storage-nodes.ts`, `deploy.ts`, and the `ArtifactPublisher`\n// primitive.\n\nimport { Effect, FileSystem, Path, type Scope } from 'effect';\n\nimport type {\n\tArtifactPublishError,\n\tArtifactPublisher,\n} from '../../../primitives/artifact-publisher.ts';\nimport type { ChainProbe } from '../../../contracts/chain-probe.ts';\nimport type { ContainerRuntime } from '../../../contracts/container-runtime.ts';\nimport type { SuiProbeKey } from '../../sui/index.ts';\nimport { contentHash as brandContentHash } from '../../../substrate/brand.ts';\nimport { expectPort, expectPositiveInteger } from '../../../substrate/runtime/config-validation.ts';\nimport { setCurrentPluginPhase } from '../../../substrate/runtime/current-plugin.ts';\nimport { walrusConfigError, walrusPluginError, type WalrusError } from '../errors.ts';\nimport { WALRUS_MAX_NODE_COUNT } from '../routable.ts';\nimport type { WalrusStorageNode } from '../storage-nodes.ts';\nimport {\n\tDEFAULT_NODE_READY_TIMEOUT_MS,\n\tDEFAULT_CONTAINER_API_PORT,\n\tWALRUS_NODE_IP_BASE,\n\tcomputeCommitteeHostname,\n\tstartStorageNodes,\n} from '../storage-nodes.ts';\nimport { deployWalrusContracts, type CachedDeployState } from '../deploy.ts';\nimport {\n\tDEFAULT_SUI_VERSION,\n\tDEFAULT_WALRUS_REF,\n\tresolveCargoImage,\n} from '../bootstrap-assets/cargo-image.ts';\nimport {\n\tDEFAULT_WALRUS_CLIENT_SERVICE_PORT,\n\tDEFAULT_WALRUS_UPLOAD_RELAY_SERVICE_PORT,\n\tstartWalrusClientServices,\n\ttype WalrusClientServices,\n\ttype WalrusClientServiceRole,\n} from '../client-services.ts';\nimport {\n\tmakeWalFaucetStrategy,\n\twalPackageIdFromCoinType,\n\tresolveWalCoinType,\n\ttype WalFaucetStrategy,\n} from '../faucet-strategy.ts';\nimport { resolveWalExchange, type WalExchangeHandle, type WalSwapSdk } from '../wal-swap.ts';\n\n/** Options for the local-cluster mode (06-walrus.md\n * §\"Configuration\"). */\nexport interface WalrusLocalClusterOptions {\n\t/** Engine row name + on-disk dir suffix + registry key.\n\t * Default `'walrus'`. */\n\treadonly name?: string;\n\t/** Number of storage-node containers. Must be `>= 1` (synchronous\n\t * factory-time guard). Default `1`. */\n\treadonly nodeCount?: number;\n\t/** Total shards distributed across the committee. Must be\n\t * `>= nodeCount`. Default `100` (distilled-doc default block). */\n\treadonly shards?: number;\n\t/** Pinned walrus release — drives the wrapper image build.\n\t * Default `'testnet-v1.49.1'`. */\n\treadonly version?: string;\n\t/** Sui release whose binary the wrapper image bakes (distilled-\n\t * doc invariant 24). Default `'devnet-v1.71.0'`. */\n\treadonly suiVersion?: string;\n\t/** Port each storage node binds inside the container. Default\n\t * 9185 (same as the router entrypoint — invariant 9). */\n\treadonly containerApiPort?: number;\n\t/** Walrus epoch length passed to `walrus-deploy --epoch-duration`.\n\t * Default `'24h'`. */\n\treadonly epochDuration?: string;\n\t/** Per-node TCP ready-probe timeout (ms). Defaults to\n\t * `DEFAULT_NODE_READY_TIMEOUT_MS`. */\n\treadonly readyTimeoutMs?: number;\n\t/** Local app-facing aggregator service. Defaults to enabled. Set\n\t * `false` to expose only storage-node routes. */\n\treadonly aggregator?: boolean | WalrusLocalServiceOptions;\n\t/** Local app-facing publisher service. Defaults to enabled. Set\n\t * `false` to run storage nodes without the simple publish API. */\n\treadonly publisher?: boolean | WalrusLocalPublisherOptions;\n\t/** Local app-facing upload relay service. Defaults to enabled. Set\n\t * `false` to run storage nodes without the SDK upload relay API. */\n\treadonly uploadRelay?: boolean | WalrusLocalUploadRelayOptions;\n}\n\nexport interface WalrusLocalServiceOptions {\n\t/** In-container port passed to the release service's\n\t * bind-address flag. Defaults to the service-specific release\n\t * port: publisher/aggregator 31415, upload relay 3000. */\n\treadonly port?: number;\n}\n\nexport type WalrusLocalPublisherOptions = WalrusLocalServiceOptions;\nexport type WalrusLocalUploadRelayOptions = WalrusLocalServiceOptions;\n\n/** Resolved local-cluster boot artifacts. */\nexport interface LocalClusterBootResult {\n\treadonly mode: 'local';\n\treadonly deploy: CachedDeployState;\n\treadonly walrusPackageId: string;\n\treadonly walPackageId: string;\n\treadonly nodes: ReadonlyArray<WalrusStorageNode>;\n\treadonly exchangeObjectId: string | undefined;\n\treadonly exchange: WalExchangeHandle | null;\n\treadonly clientServices: WalrusClientServices;\n\t/** WAL faucet strategy — present only when the local cluster has a\n\t * non-empty exchange object. The barrel registers this onto the\n\t * `coinType:<fullCoinType>` strategy contributor decl. `null`\n\t * otherwise. */\n\treadonly walFaucetStrategy: WalFaucetStrategy | null;\n\treadonly walCoinType: string;\n}\n\n/** Defaults applied to options. */\nexport interface ResolvedLocalClusterOptions {\n\treadonly name: string;\n\treadonly nodeCount: number;\n\treadonly shards: number;\n\treadonly version: string;\n\treadonly suiVersion: string;\n\treadonly containerApiPort: number;\n\treadonly epochDuration: string;\n\treadonly readyTimeoutMs: number;\n\treadonly aggregator: ResolvedWalrusLocalServiceOptions | null;\n\treadonly publisher: ResolvedWalrusLocalPublisherOptions | null;\n\treadonly uploadRelay: ResolvedWalrusLocalUploadRelayOptions | null;\n}\n\nexport interface ResolvedWalrusLocalServiceOptions {\n\treadonly port: number;\n}\n\nexport type ResolvedWalrusLocalPublisherOptions = ResolvedWalrusLocalServiceOptions;\nexport type ResolvedWalrusLocalUploadRelayOptions = ResolvedWalrusLocalServiceOptions;\n\n/** Synchronous factory-time validation. Throws (NOT Effect-fail) so\n * misconfiguration trips at the `defineDevstack` call site rather\n * than at deferred Layer.build time. Distilled-doc invariant 11. */\nexport const resolveLocalClusterOptions = (\n\topts: WalrusLocalClusterOptions,\n): ResolvedLocalClusterOptions => {\n\tconst nodeCount = expectPositiveInteger(opts.nodeCount ?? 1, {\n\t\tfield: 'nodeCount',\n\t\tmkError: ({ field, message, hint }) =>\n\t\t\twalrusConfigError(\n\t\t\t\tfield,\n\t\t\t\t`walrusLocalCluster: nodeCount must be >= 1 (got ${String(opts.nodeCount ?? 1)})`,\n\t\t\t\thint ?? message,\n\t\t\t),\n\t\thint: 'set nodeCount to a positive integer (default 1)',\n\t});\n\tif (nodeCount > WALRUS_MAX_NODE_COUNT) {\n\t\tthrow walrusConfigError(\n\t\t\t'nodeCount',\n\t\t\t`walrusLocalCluster: nodeCount (${nodeCount}) exceeds the maximum ${WALRUS_MAX_NODE_COUNT} ` +\n\t\t\t\t`pre-declared Traefik entrypoints (walrus-node-0..${WALRUS_MAX_NODE_COUNT - 1}).`,\n\t\t\t`reduce nodeCount or raise WALRUS_MAX_NODE_COUNT in plugins/walrus/routable.ts`,\n\t\t);\n\t}\n\tconst shards = expectPositiveInteger(opts.shards ?? 100, {\n\t\tfield: 'shards',\n\t\tmkError: ({ field, message, hint }) =>\n\t\t\twalrusConfigError(\n\t\t\t\tfield,\n\t\t\t\t`walrusLocalCluster: shards must be a positive integer (got ${String(opts.shards ?? 100)})`,\n\t\t\t\thint ?? message,\n\t\t\t),\n\t\thint: 'set shards to a positive integer (default 100)',\n\t});\n\tif (shards < nodeCount) {\n\t\tthrow walrusConfigError(\n\t\t\t'shards',\n\t\t\t`walrusLocalCluster: shards (${shards}) must be >= nodeCount (${nodeCount})`,\n\t\t\t`set shards >= ${nodeCount} (default 100)`,\n\t\t);\n\t}\n\treturn {\n\t\tname: opts.name ?? 'walrus',\n\t\tnodeCount,\n\t\tshards,\n\t\tversion: opts.version ?? DEFAULT_WALRUS_REF,\n\t\tsuiVersion: opts.suiVersion ?? DEFAULT_SUI_VERSION,\n\t\tcontainerApiPort: opts.containerApiPort ?? DEFAULT_CONTAINER_API_PORT,\n\t\tepochDuration: opts.epochDuration ?? '24h',\n\t\treadyTimeoutMs: opts.readyTimeoutMs ?? DEFAULT_NODE_READY_TIMEOUT_MS,\n\t\t...resolveLocalServicesOptions(opts),\n\t};\n};\n\nconst expectOptionalPort = (value: number | undefined, field: string): number | undefined =>\n\tvalue === undefined\n\t\t? undefined\n\t\t: expectPort(value, {\n\t\t\t\tfield,\n\t\t\t\tmkError: ({ field: f, message, hint }) =>\n\t\t\t\t\twalrusConfigError(f, `walrusLocalCluster: ${f} ${message}`, hint),\n\t\t\t});\n\nconst resolveLocalServiceBase = (\n\tvalue: boolean | WalrusLocalServiceOptions | undefined,\n\tfieldPrefix: 'aggregator' | 'publisher' | 'uploadRelay',\n\tdefaultPort: number,\n): ResolvedWalrusLocalServiceOptions | null => {\n\tif (value === false) return null;\n\tconst authored = value === undefined || value === true ? {} : value;\n\treturn {\n\t\tport: expectOptionalPort(authored.port, `${fieldPrefix}.port`) ?? defaultPort,\n\t};\n};\n\nconst resolveLocalServicesOptions = (\n\topts: WalrusLocalClusterOptions,\n): Pick<ResolvedLocalClusterOptions, 'aggregator' | 'publisher' | 'uploadRelay'> => {\n\treturn {\n\t\taggregator: resolveLocalServiceBase(\n\t\t\topts.aggregator,\n\t\t\t'aggregator',\n\t\t\tDEFAULT_WALRUS_CLIENT_SERVICE_PORT,\n\t\t),\n\t\tpublisher: resolveLocalServiceBase(\n\t\t\topts.publisher,\n\t\t\t'publisher',\n\t\t\tDEFAULT_WALRUS_CLIENT_SERVICE_PORT,\n\t\t),\n\t\tuploadRelay: resolveLocalServiceBase(\n\t\t\topts.uploadRelay,\n\t\t\t'uploadRelay',\n\t\t\tDEFAULT_WALRUS_UPLOAD_RELAY_SERVICE_PORT,\n\t\t),\n\t};\n};\n\n/** Dependencies the local service consumes at acquire-time. */\nexport interface LocalClusterDeps {\n\treadonly runtime: ContainerRuntime;\n\treadonly publisher: ArtifactPublisher;\n\treadonly probe: ChainProbe<SuiProbeKey>;\n\treadonly suiSdk: WalSwapSdk;\n\treadonly suiChainId: string;\n\treadonly suiRpcUrlInNetwork: string;\n\treadonly walrusFaucetUrlInNetwork: string;\n\treadonly waitForFundsReady: Effect.Effect<void, unknown>;\n\treadonly app: string;\n\treadonly stack: string;\n\t/** Pre-allocated /24 prefix from `subnetForStack`. */\n\treadonly subnetPrefix: string;\n\treadonly walrusNetworkName: string;\n\treadonly suiNetworkName: string;\n\treadonly deployHostMountPath: string;\n\t/** On-disk per-stack root from `StackPathsService.stackRoot`. The\n\t * walrus bind-mount is computed off this; `deployHostMountPath`\n\t * MUST live under it. */\n\treadonly stackRoot: string;\n}\n\n/** Boot the local cluster. Returns the resolved value the plugin\n * projects onto its tags.\n *\n * Steps:\n * - Image build — `resolveCargoImage` (bootstrap asset).\n * Honors `WALRUS_CARGO_IMAGE_OVERRIDE` for the pre-baked path.\n * - Docker network ensure — `runtime.ensureNetwork(walrusNet)`.\n * The sui network is owned by the sui plugin; we attach to it\n * as a secondary network on each storage node.\n * - Deploy contracts — `deployWalrusContracts` dispatches through\n * the artifact publisher primitive; the produce body runs the walrus deploy\n * one-shot.\n * - Storage nodes — parallel boot via `startStorageNodes`.\n * - WAL faucet strategy — constructed if `state.exchangeObject`\n * exists. Surfaced on the boot result; the barrel registers\n * the contributor decl.\n *\n * Per-step failures surface as `WalrusPluginError` with the phase\n * vocabulary from `errors.ts`; artifact publisher failures pass through as\n * `ArtifactPublishError`. The plugin narration vocabulary anchors\n * on these phase tags.\n */\nexport const bootLocalCluster = (\n\tdeps: LocalClusterDeps,\n\topts: ResolvedLocalClusterOptions,\n): Effect.Effect<\n\tLocalClusterBootResult,\n\tWalrusError | ArtifactPublishError,\n\tScope.Scope | FileSystem.FileSystem | Path.Path\n> =>\n\tEffect.gen(function* () {\n\t\t// ---- cargo image (bootstrap asset) -----------------------\n\t\t// The cargo image is content-addressed; the resolver owns the\n\t\t// cache check + the registry-tag override fast path.\n\t\tyield* setCurrentPluginPhase(`resolving Walrus image ${opts.version}`);\n\t\tconst walrusImage = yield* resolveCargoImage(deps.runtime, {\n\t\t\twalrusRef: opts.version,\n\t\t\tsuiVersion: opts.suiVersion,\n\t\t\towner: { app: deps.app, stack: deps.stack },\n\t\t});\n\n\t\t// ---- docker network ensure ------------------------------\n\t\tyield* setCurrentPluginPhase(`ensuring Walrus network ${deps.walrusNetworkName}`);\n\t\tyield* deps.runtime\n\t\t\t.ensureNetwork({\n\t\t\t\tname: deps.walrusNetworkName,\n\t\t\t\tapp: deps.app,\n\t\t\t\tstack: deps.stack,\n\t\t\t})\n\t\t\t.pipe(\n\t\t\t\tEffect.catch((cause) =>\n\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\twalrusPluginError(\n\t\t\t\t\t\t\t'cluster-network',\n\t\t\t\t\t\t\t`walrus: ensureNetwork('${deps.walrusNetworkName}') failed: ${cause.reason}: ${cause.detail}`,\n\t\t\t\t\t\t\t{ cause },\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\n\t\t// ---- deploy via ArtifactPublisher ----------------\n\t\t// The deploy one-shot needs the walrus image + sui network so it\n\t\t// can dial the per-stack sui RPC + faucet over docker DNS.\n\t\t//\n\t\t// Per-node committee hostnames are Docker-network aliases, because the\n\t\t// real Rust publisher/aggregator read these public_host values from chain\n\t\t// and dial storage nodes from inside the Walrus network. Router-facing\n\t\t// `.localhost` hostnames are still produced by `startStorageNodes` for\n\t\t// devstack's public diagnostic URLs.\n\t\tconst committeeHosts = Array.from({ length: opts.nodeCount }, (_, i) =>\n\t\t\tcomputeCommitteeHostname(i),\n\t\t).join(',');\n\t\tconst listeningIps = Array.from(\n\t\t\t{ length: opts.nodeCount },\n\t\t\t(_, i) => `${deps.subnetPrefix}.${WALRUS_NODE_IP_BASE + i}`,\n\t\t).join(',');\n\n\t\tyield* setCurrentPluginPhase(\n\t\t\t`deploying Walrus contracts (${opts.nodeCount} node${opts.nodeCount === 1 ? '' : 's'}, ${opts.shards} shards)`,\n\t\t);\n\t\tconst { state } = yield* deployWalrusContracts(deps.publisher, deps.probe, deps.runtime, {\n\t\t\twalrusName: opts.name,\n\t\t\tchainId: deps.suiChainId,\n\t\t\tcontentHash: brandContentHash(\n\t\t\t\t`walrus|${opts.version}|${opts.suiVersion}|${opts.nodeCount}|${opts.shards}|${opts.epochDuration}|committee=${committeeHosts}|listen=${listeningIps}`,\n\t\t\t),\n\t\t\toutputDirHostPath: deps.deployHostMountPath,\n\t\t\tstackRoot: deps.stackRoot,\n\t\t\tsuiRpcUrlInNetwork: deps.suiRpcUrlInNetwork,\n\t\t\twalrusFaucetUrlInNetwork: deps.walrusFaucetUrlInNetwork,\n\t\t\twaitForFundsReady: deps.waitForFundsReady,\n\t\t\tcommitteeSize: opts.nodeCount,\n\t\t\tshards: opts.shards,\n\t\t\tepochDuration: opts.epochDuration,\n\t\t\tpublicHostsCsv: committeeHosts,\n\t\t\tlisteningIpsCsv: listeningIps,\n\t\t\twalrusImage,\n\t\t\tsuiNetworkName: deps.suiNetworkName,\n\t\t});\n\t\tconst deployConfigHash = [\n\t\t\t'walrus-deploy',\n\t\t\tstate.walrusPackageId,\n\t\t\tstate.systemObject,\n\t\t\tstate.stakingObject,\n\t\t\tstate.exchangeObject ?? 'no-exchange',\n\t\t].join('|');\n\n\t\t// Per-node image tags. Each storage node runs under its OWN tag (a cheap\n\t\t// re-tag of the one content-cached cargo build) so its committed writable\n\t\t// layer — the RocksDB holding that node's slivers — promotes to a DISTINCT\n\t\t// image on snapshot restore. A single shared tag would make the restore\n\t\t// image-promote collapse all N committed layers onto one tag\n\t\t// (last-write-wins): N-1 nodes boot on the wrong layer and every\n\t\t// pre-snapshot blob is orphaned. The tag is scoped by the deployed\n\t\t// `walrusPackageId` — stable across boot1 -> restore -> boot2 (the deploy\n\t\t// cache reuses the same id, so the restore's promote target matches what\n\t\t// boot2 requests), but DISTINCT per fresh deployment, so a later cold boot\n\t\t// on a new chain never inherits a prior run's committed node layers (which\n\t\t// carry a stale committee and would reject writes). The base `walrusImage`\n\t\t// (shared) still backs the transient deploy one-shot (`--rm`, unsnapshotted).\n\t\tconst nodeImageScope = state.walrusPackageId.replace(/^0x/, '').slice(0, 12);\n\t\tconst resolveServiceImage = (role: WalrusClientServiceRole) =>\n\t\t\tresolveCargoImage(\n\t\t\t\tdeps.runtime,\n\t\t\t\t{\n\t\t\t\t\twalrusRef: opts.version,\n\t\t\t\t\tsuiVersion: opts.suiVersion,\n\t\t\t\t\towner: { app: deps.app, stack: deps.stack },\n\t\t\t\t},\n\t\t\t\t`${walrusImage.tag}-${nodeImageScope}-${role}`,\n\t\t\t);\n\t\tconst nodeImages = yield* Effect.forEach(\n\t\t\tArray.from({ length: opts.nodeCount }, (_, i) => i),\n\t\t\t(i) =>\n\t\t\t\tresolveCargoImage(\n\t\t\t\t\tdeps.runtime,\n\t\t\t\t\t{\n\t\t\t\t\t\twalrusRef: opts.version,\n\t\t\t\t\t\tsuiVersion: opts.suiVersion,\n\t\t\t\t\t\towner: { app: deps.app, stack: deps.stack },\n\t\t\t\t\t},\n\t\t\t\t\t`${walrusImage.tag}-${nodeImageScope}-node-${i}`,\n\t\t\t\t),\n\t\t);\n\t\tconst clientServiceImages = yield* Effect.all({\n\t\t\taggregator:\n\t\t\t\topts.aggregator === null ? Effect.succeed(null) : resolveServiceImage('aggregator'),\n\t\t\tpublisher: opts.publisher === null ? Effect.succeed(null) : resolveServiceImage('publisher'),\n\t\t\tuploadRelay:\n\t\t\t\topts.uploadRelay === null ? Effect.succeed(null) : resolveServiceImage('upload-relay'),\n\t\t});\n\n\t\t// ---- storage nodes — parallel boot ----------------------\n\t\tyield* setCurrentPluginPhase(\n\t\t\t`starting ${opts.nodeCount} Walrus storage node${opts.nodeCount === 1 ? '' : 's'}`,\n\t\t);\n\t\tconst { nodes } = yield* startStorageNodes(deps.runtime, {\n\t\t\tapp: deps.app,\n\t\t\tstack: deps.stack,\n\t\t\twalrusName: opts.name,\n\t\t\timages: nodeImages,\n\t\t\tnodeCount: opts.nodeCount,\n\t\t\tsubnetPrefix: deps.subnetPrefix,\n\t\t\tcontainerApiPort: opts.containerApiPort,\n\t\t\twalrusNetworkName: deps.walrusNetworkName,\n\t\t\tsuiNetworkName: deps.suiNetworkName,\n\t\t\tdeployHostMountPath: deps.deployHostMountPath,\n\t\t\tstackRoot: deps.stackRoot,\n\t\t\tdeployConfigHash,\n\t\t\treadyTimeoutMs: opts.readyTimeoutMs,\n\t\t});\n\n\t\t// `nodeCount >= 1` is enforced synchronously already; this is\n\t\t// defense-in-depth.\n\t\tif (nodes.length === 0) {\n\t\t\treturn yield* Effect.fail(\n\t\t\t\twalrusPluginError('storage-node', 'walrus: at least one storage node is required'),\n\t\t\t);\n\t\t}\n\n\t\t// ---- app-facing Rust publisher / aggregator / upload-relay services ----\n\t\tyield* setCurrentPluginPhase('starting Walrus client services');\n\t\tconst clientServices = yield* startWalrusClientServices(deps.runtime, {\n\t\t\tapp: deps.app,\n\t\t\tstack: deps.stack,\n\t\t\twalrusName: opts.name,\n\t\t\timages: clientServiceImages,\n\t\t\toptions: {\n\t\t\t\taggregator: opts.aggregator,\n\t\t\t\tpublisher: opts.publisher,\n\t\t\t\tuploadRelay: opts.uploadRelay,\n\t\t\t},\n\t\t\twalrusNetworkName: deps.walrusNetworkName,\n\t\t\tsuiNetworkName: deps.suiNetworkName,\n\t\t\tdeployHostMountPath: deps.deployHostMountPath,\n\t\t\tstackRoot: deps.stackRoot,\n\t\t\tdeployConfigHash,\n\t\t\tsuiRpcUrlInNetwork: deps.suiRpcUrlInNetwork,\n\t\t\treadyTimeoutMs: opts.readyTimeoutMs,\n\t\t});\n\n\t\t// ---- exchange resolution + WAL funding strategy ------\n\t\t// Register a `coinType:<fullCoinType>` strategy that swaps the\n\t\t// requesting account's SUI into WAL on demand. Accounts opt in via\n\t\t// their normal `funding` list.\n\t\tyield* setCurrentPluginPhase('resolving WAL exchange');\n\t\tconst exchange = yield* resolveWalExchange(deps.probe, state.exchangeObject);\n\t\tconst walFaucetStrategy: WalFaucetStrategy | null = exchange\n\t\t\t? makeWalFaucetStrategy({\n\t\t\t\t\texchange,\n\t\t\t\t\tsdk: deps.suiSdk,\n\t\t\t\t})\n\t\t\t: null;\n\t\tconst resolvedWalCoinType = yield* resolveWalCoinType({\n\t\t\tprobe: deps.probe,\n\t\t\ttreasuryObjectId: state.treasuryObject,\n\t\t\tdeployPackageId: state.walrusPackageId,\n\t\t\trequireTreasuryObject: exchange !== null,\n\t\t});\n\t\tconst resolvedWalPackageId = walPackageIdFromCoinType(resolvedWalCoinType);\n\n\t\treturn {\n\t\t\tmode: 'local' as const,\n\t\t\tdeploy: state,\n\t\t\twalrusPackageId: state.walrusPackageId,\n\t\t\twalPackageId: resolvedWalPackageId,\n\t\t\tnodes,\n\t\t\texchangeObjectId: state.exchangeObject,\n\t\t\texchange,\n\t\t\tclientServices,\n\t\t\twalFaucetStrategy,\n\t\t\twalCoinType: resolvedWalCoinType,\n\t\t};\n\t});\n"],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { WALRUS_ROUTER_PORT } from "./storage-nodes.mjs";
|
|
|
3
3
|
const WALRUS_NODE_ENDPOINT_PREFIX = "walrus-node-";
|
|
4
4
|
const WALRUS_AGGREGATOR_ENDPOINT_NAME = "walrus-aggregator";
|
|
5
5
|
const WALRUS_PUBLISHER_ENDPOINT_NAME = "walrus-publisher";
|
|
6
|
+
const WALRUS_UPLOAD_RELAY_ENDPOINT_NAME = "walrus-upload-relay";
|
|
6
7
|
const WALRUS_ENTRYPOINTS = [
|
|
7
8
|
...Array.from({ length: 8 }, (_, i) => ({
|
|
8
9
|
name: `${WALRUS_NODE_ENDPOINT_PREFIX}${i}`,
|
|
@@ -18,6 +19,11 @@ const WALRUS_ENTRYPOINTS = [
|
|
|
18
19
|
name: WALRUS_PUBLISHER_ENDPOINT_NAME,
|
|
19
20
|
port: WALRUS_ROUTER_PORT,
|
|
20
21
|
protocol: "http"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: WALRUS_UPLOAD_RELAY_ENDPOINT_NAME,
|
|
25
|
+
port: WALRUS_ROUTER_PORT,
|
|
26
|
+
protocol: "http"
|
|
21
27
|
}
|
|
22
28
|
];
|
|
23
29
|
/** Build the Routable contributions for the local cluster. `nodeCount`
|
|
@@ -42,39 +48,29 @@ const makeLocalRoutables = (args) => {
|
|
|
42
48
|
wireProtocol: "https"
|
|
43
49
|
}));
|
|
44
50
|
const serviceRoutes = [];
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
role: WALRUS_PUBLISHER_ENDPOINT_NAME
|
|
66
|
-
},
|
|
67
|
-
upstream: {
|
|
68
|
-
type: "container",
|
|
69
|
-
containerName: args.publisher.containerName,
|
|
70
|
-
containerPort: args.publisher.containerPort
|
|
71
|
-
},
|
|
72
|
-
cors: true,
|
|
73
|
-
wireProtocol: "http"
|
|
74
|
-
});
|
|
51
|
+
const pushServiceRoute = (endpointName, service) => {
|
|
52
|
+
serviceRoutes.push({
|
|
53
|
+
kind: "routable",
|
|
54
|
+
endpointName,
|
|
55
|
+
dispatchId: {
|
|
56
|
+
serviceKey: args.serviceKey,
|
|
57
|
+
role: endpointName
|
|
58
|
+
},
|
|
59
|
+
upstream: {
|
|
60
|
+
type: "container",
|
|
61
|
+
containerName: service.containerName,
|
|
62
|
+
containerPort: service.containerPort
|
|
63
|
+
},
|
|
64
|
+
cors: true,
|
|
65
|
+
wireProtocol: "http"
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
if (args.aggregator !== void 0 && args.aggregator !== null) pushServiceRoute(WALRUS_AGGREGATOR_ENDPOINT_NAME, args.aggregator);
|
|
69
|
+
if (args.publisher !== void 0 && args.publisher !== null) pushServiceRoute(WALRUS_PUBLISHER_ENDPOINT_NAME, args.publisher);
|
|
70
|
+
if (args.uploadRelay !== void 0 && args.uploadRelay !== null) pushServiceRoute(WALRUS_UPLOAD_RELAY_ENDPOINT_NAME, args.uploadRelay);
|
|
75
71
|
return [...perNodeRoutes, ...serviceRoutes];
|
|
76
72
|
};
|
|
77
73
|
//#endregion
|
|
78
|
-
export { WALRUS_AGGREGATOR_ENDPOINT_NAME, WALRUS_ENTRYPOINTS, WALRUS_PUBLISHER_ENDPOINT_NAME, makeLocalRoutables };
|
|
74
|
+
export { WALRUS_AGGREGATOR_ENDPOINT_NAME, WALRUS_ENTRYPOINTS, WALRUS_PUBLISHER_ENDPOINT_NAME, WALRUS_UPLOAD_RELAY_ENDPOINT_NAME, makeLocalRoutables };
|
|
79
75
|
|
|
80
76
|
//# sourceMappingURL=routable.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routable.mjs","names":[],"sources":["../../../src/plugins/walrus/routable.ts"],"sourcesContent":["// Walrus plugin — Routable contributions.\n//\n// Distilled-doc reference (06-walrus.md §\"Routes registered\"):\n// each storage node carries one Traefik route. The router binds host\n// port 9185 once globally and dispatches by `Host:` header to the\n// per-stack backend — `routerHostname(identity, 'walrus-node-<i>')`.\n//\n// For the local cluster:\n// - N × `walrus-node-<i>` routes — one per storage node, with\n// `cors: true` (walrus storage REST API lacks CORS headers). These\n// are HTTP public routes backed by HTTPS upstreams because Walrus\n// storage nodes self-sign TLS with their network key.\n// - 1 × `walrus-aggregator` route — release `walrus aggregator`\n// service container\n// exposing `GET /v1/blobs/:id` through a single app-facing URL.\n// - 1 × `walrus-publisher` route — release `walrus publisher`\n// service container\n// exposing `PUT /v1/blobs` through a single app-facing URL.\n//\n// Known-deployment publishes no routes — the aggregator/publisher\n// URLs land on the codegen-emitted `WalrusBindings
|
|
1
|
+
{"version":3,"file":"routable.mjs","names":[],"sources":["../../../src/plugins/walrus/routable.ts"],"sourcesContent":["// Walrus plugin — Routable contributions.\n//\n// Distilled-doc reference (06-walrus.md §\"Routes registered\"):\n// each storage node carries one Traefik route. The router binds host\n// port 9185 once globally and dispatches by `Host:` header to the\n// per-stack backend — `routerHostname(identity, 'walrus-node-<i>')`.\n//\n// For the local cluster:\n// - N × `walrus-node-<i>` routes — one per storage node, with\n// `cors: true` (walrus storage REST API lacks CORS headers). These\n// are HTTP public routes backed by HTTPS upstreams because Walrus\n// storage nodes self-sign TLS with their network key.\n// - 1 × `walrus-aggregator` route — release `walrus aggregator`\n// service container\n// exposing `GET /v1/blobs/:id` through a single app-facing URL.\n// - 1 × `walrus-publisher` route — release `walrus publisher`\n// service container\n// exposing `PUT /v1/blobs` through a single app-facing URL.\n// - 1 × `walrus-upload-relay` route — release `walrus-upload-relay`\n// service container\n// exposing the SDK upload relay API through a single app-facing URL.\n//\n// Known-deployment publishes no routes — the aggregator/publisher\n// / upload-relay URLs land on the codegen-emitted `WalrusBindings`\n// URL fields instead.\n\nimport type { EntrypointDecl, RoutableDecl } from '../../contracts/routable.ts';\nimport { WALRUS_ROUTER_PORT } from './storage-nodes.ts';\nimport type { WalrusClientService } from './client-services.ts';\n\nexport const WALRUS_NODE_ENDPOINT_PREFIX = 'walrus-node-' as const;\nexport const WALRUS_AGGREGATOR_ENDPOINT_NAME = 'walrus-aggregator' as const;\nexport const WALRUS_PUBLISHER_ENDPOINT_NAME = 'walrus-publisher' as const;\nexport const WALRUS_UPLOAD_RELAY_ENDPOINT_NAME = 'walrus-upload-relay' as const;\n\n/** Upper bound on `nodeCount` — Traefik entrypoints are bound at boot,\n * so the cluster's per-node routes need pre-declared entrypoint names.\n * Validated at factory time in `mode/local-cluster.ts:resolveOptions`. */\nexport const WALRUS_MAX_NODE_COUNT = 8;\n\nexport const WALRUS_ENTRYPOINTS: ReadonlyArray<EntrypointDecl> = [\n\t...Array.from(\n\t\t{ length: WALRUS_MAX_NODE_COUNT },\n\t\t(_, i): EntrypointDecl => ({\n\t\t\tname: `${WALRUS_NODE_ENDPOINT_PREFIX}${i}`,\n\t\t\tport: WALRUS_ROUTER_PORT,\n\t\t\tprotocol: 'http',\n\t\t}),\n\t),\n\t{ name: WALRUS_AGGREGATOR_ENDPOINT_NAME, port: WALRUS_ROUTER_PORT, protocol: 'http' },\n\t{ name: WALRUS_PUBLISHER_ENDPOINT_NAME, port: WALRUS_ROUTER_PORT, protocol: 'http' },\n\t{ name: WALRUS_UPLOAD_RELAY_ENDPOINT_NAME, port: WALRUS_ROUTER_PORT, protocol: 'http' },\n];\n\n/** Build the Routable contributions for the local cluster. `nodeCount`\n * drives the per-node fan-out; the plugin's service key + route role\n * identify the dispatch target. */\nexport const makeLocalRoutables = (args: {\n\treadonly app: string;\n\treadonly stack: string;\n\treadonly walrusName: string;\n\treadonly serviceKey: string;\n\treadonly nodeCount: number;\n\treadonly containerApiPort?: number;\n\treadonly aggregator?: WalrusClientService | null;\n\treadonly publisher?: WalrusClientService | null;\n\treadonly uploadRelay?: WalrusClientService | null;\n}): ReadonlyArray<RoutableDecl> => {\n\tconst containerPort = args.containerApiPort ?? WALRUS_ROUTER_PORT;\n\tconst containerNameFor = (i: number): string =>\n\t\t`devstack-${args.app}-${args.stack}-walrus-${args.walrusName}-node-${i}`;\n\tconst perNodeRoutes: ReadonlyArray<RoutableDecl> = Array.from(\n\t\t{ length: args.nodeCount },\n\t\t(_, i): RoutableDecl => ({\n\t\t\tkind: 'routable',\n\t\t\tendpointName: `${WALRUS_NODE_ENDPOINT_PREFIX}${i}`,\n\t\t\tdispatchId: {\n\t\t\t\tserviceKey: args.serviceKey,\n\t\t\t\trole: `${WALRUS_NODE_ENDPOINT_PREFIX}${i}`,\n\t\t\t},\n\t\t\tupstream: {\n\t\t\t\ttype: 'container',\n\t\t\t\tcontainerName: containerNameFor(i),\n\t\t\t\tcontainerPort,\n\t\t\t},\n\t\t\t// Distilled-doc §\"Routes registered\": cors: true (walrus\n\t\t\t// storage REST API lacks CORS headers).\n\t\t\tcors: true,\n\t\t\twireProtocol: 'https',\n\t\t}),\n\t);\n\n\tconst serviceRoutes: RoutableDecl[] = [];\n\tconst pushServiceRoute = (endpointName: string, service: WalrusClientService) => {\n\t\tserviceRoutes.push({\n\t\t\tkind: 'routable',\n\t\t\tendpointName,\n\t\t\tdispatchId: {\n\t\t\t\tserviceKey: args.serviceKey,\n\t\t\t\trole: endpointName,\n\t\t\t},\n\t\t\tupstream: {\n\t\t\t\ttype: 'container',\n\t\t\t\tcontainerName: service.containerName,\n\t\t\t\tcontainerPort: service.containerPort,\n\t\t\t},\n\t\t\tcors: true,\n\t\t\twireProtocol: 'http',\n\t\t});\n\t};\n\tif (args.aggregator !== undefined && args.aggregator !== null) {\n\t\tpushServiceRoute(WALRUS_AGGREGATOR_ENDPOINT_NAME, args.aggregator);\n\t}\n\tif (args.publisher !== undefined && args.publisher !== null) {\n\t\tpushServiceRoute(WALRUS_PUBLISHER_ENDPOINT_NAME, args.publisher);\n\t}\n\tif (args.uploadRelay !== undefined && args.uploadRelay !== null) {\n\t\tpushServiceRoute(WALRUS_UPLOAD_RELAY_ENDPOINT_NAME, args.uploadRelay);\n\t}\n\n\treturn [...perNodeRoutes, ...serviceRoutes];\n};\n"],"mappings":";;AA8BA,MAAa,8BAA8B;AAC3C,MAAa,kCAAkC;AAC/C,MAAa,iCAAiC;AAC9C,MAAa,oCAAoC;AAOjD,MAAa,qBAAoD;CAChE,GAAG,MAAM,KACR,EAAE,QAAA,EAA8B,IAC/B,GAAG,OAAuB;EAC1B,MAAM,GAAG,8BAA8B;EACvC,MAAM;EACN,UAAU;CACX,EACD;CACA;EAAE,MAAM;EAAiC,MAAM;EAAoB,UAAU;CAAO;CACpF;EAAE,MAAM;EAAgC,MAAM;EAAoB,UAAU;CAAO;CACnF;EAAE,MAAM;EAAmC,MAAM;EAAoB,UAAU;CAAO;AACvF;;;;AAKA,MAAa,sBAAsB,SAUA;CAClC,MAAM,gBAAgB,KAAK,oBAAA;CAC3B,MAAM,oBAAoB,MACzB,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,UAAU,KAAK,WAAW,QAAQ;CACtE,MAAM,gBAA6C,MAAM,KACxD,EAAE,QAAQ,KAAK,UAAU,IACxB,GAAG,OAAqB;EACxB,MAAM;EACN,cAAc,GAAG,8BAA8B;EAC/C,YAAY;GACX,YAAY,KAAK;GACjB,MAAM,GAAG,8BAA8B;EACxC;EACA,UAAU;GACT,MAAM;GACN,eAAe,iBAAiB,CAAC;GACjC;EACD;EAGA,MAAM;EACN,cAAc;CACf,EACD;CAEA,MAAM,gBAAgC,CAAC;CACvC,MAAM,oBAAoB,cAAsB,YAAiC;EAChF,cAAc,KAAK;GAClB,MAAM;GACN;GACA,YAAY;IACX,YAAY,KAAK;IACjB,MAAM;GACP;GACA,UAAU;IACT,MAAM;IACN,eAAe,QAAQ;IACvB,eAAe,QAAQ;GACxB;GACA,MAAM;GACN,cAAc;EACf,CAAC;CACF;CACA,IAAI,KAAK,eAAe,KAAA,KAAa,KAAK,eAAe,MACxD,iBAAiB,iCAAiC,KAAK,UAAU;CAElE,IAAI,KAAK,cAAc,KAAA,KAAa,KAAK,cAAc,MACtD,iBAAiB,gCAAgC,KAAK,SAAS;CAEhE,IAAI,KAAK,gBAAgB,KAAA,KAAa,KAAK,gBAAgB,MAC1D,iBAAiB,mCAAmC,KAAK,WAAW;CAGrE,OAAO,CAAC,GAAG,eAAe,GAAG,aAAa;AAC3C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot.mjs","names":[],"sources":["../../../src/plugins/walrus/snapshot.ts"],"sourcesContent":["// Walrus plugin — Snapshotable contribution.\n//\n// Distilled-doc reference (06-walrus.md §\"Persistence model\" +\n// §\"Hard requirements\" item 7):\n// - `runtime/walrus/<name>/deploy/` MUST ride the snapshot tar.\n// It holds storage-node private keys + per-node configs that\n// `walrus-deploy` wrote; without them, a cached \"walrus is already\n// deployed\" artifact cannot be honored on resume.\n// - Local cluster: N storage-node containers plus enabled release\n// publisher/aggregator service containers are declared as managed\n// containers; runtime adapter pauses then `docker commit`. Per-role\n// labels keep every committed layer distinct on restore.\n// - Known-deployment: no containers, no subtrees. The shape still\n// exists so the identity guard fires on restore.\n//\n// Identity guard: contributes the deploy mode + network + (when\n// local) the `name` discriminator to the pre-restore identity\n// record. A snapshot taken in local mode restored under known\n// mode (or vice versa) refuses BEFORE any destructive mutation.\n\nimport { Effect } from 'effect';\n\nimport type { ContainerLabelTuple, SnapshotableDecl } from '../../contracts/snapshotable.ts';\n\n/** Discriminator passed to `makeSnapshotable` — narrower than the\n * full mode union because the snapshot shape only cares about\n * \"local with containers\" vs \"known with nothing\". */\nexport type WalrusSnapshotMode = 'local' | 'known';\n\n/** Build the Snapshotable contribution.\n *\n * `app` / `stack` / `walrusName` are resolved at the plugin's\n * acquire-time so the snapshot's identity guard can match the\n * plugin's actual container labels.\n *\n * `nodeCount` (local mode only) drives the per-node managed-container\n * decls. Each storage node carries `role: storage-node-${i}` so the\n * substrate's label filter pauses/commits each independently. */\nexport const makeSnapshotable = (\n\tmode: WalrusSnapshotMode,\n\tapp: string,\n\tstack: string,\n\twalrusName: string,\n\tnetwork: string,\n\tnodeCount = 1,\n\tclientServiceRoles: ReadonlyArray<
|
|
1
|
+
{"version":3,"file":"snapshot.mjs","names":[],"sources":["../../../src/plugins/walrus/snapshot.ts"],"sourcesContent":["// Walrus plugin — Snapshotable contribution.\n//\n// Distilled-doc reference (06-walrus.md §\"Persistence model\" +\n// §\"Hard requirements\" item 7):\n// - `runtime/walrus/<name>/deploy/` MUST ride the snapshot tar.\n// It holds storage-node private keys + per-node configs that\n// `walrus-deploy` wrote; without them, a cached \"walrus is already\n// deployed\" artifact cannot be honored on resume.\n// - Local cluster: N storage-node containers plus enabled release\n// publisher/aggregator/upload-relay service containers are declared as managed\n// containers; runtime adapter pauses then `docker commit`. Per-role\n// labels keep every committed layer distinct on restore.\n// - Known-deployment: no containers, no subtrees. The shape still\n// exists so the identity guard fires on restore.\n//\n// Identity guard: contributes the deploy mode + network + (when\n// local) the `name` discriminator to the pre-restore identity\n// record. A snapshot taken in local mode restored under known\n// mode (or vice versa) refuses BEFORE any destructive mutation.\n\nimport { Effect } from 'effect';\n\nimport type { ContainerLabelTuple, SnapshotableDecl } from '../../contracts/snapshotable.ts';\nimport type { WalrusClientServiceRole } from './client-services.ts';\n\n/** Discriminator passed to `makeSnapshotable` — narrower than the\n * full mode union because the snapshot shape only cares about\n * \"local with containers\" vs \"known with nothing\". */\nexport type WalrusSnapshotMode = 'local' | 'known';\n\n/** Build the Snapshotable contribution.\n *\n * `app` / `stack` / `walrusName` are resolved at the plugin's\n * acquire-time so the snapshot's identity guard can match the\n * plugin's actual container labels.\n *\n * `nodeCount` (local mode only) drives the per-node managed-container\n * decls. Each storage node carries `role: storage-node-${i}` so the\n * substrate's label filter pauses/commits each independently. */\nexport const makeSnapshotable = (\n\tmode: WalrusSnapshotMode,\n\tapp: string,\n\tstack: string,\n\twalrusName: string,\n\tnetwork: string,\n\tnodeCount = 1,\n\tclientServiceRoles: ReadonlyArray<WalrusClientServiceRole> = [],\n): SnapshotableDecl => {\n\tconst labels = (role: string): ContainerLabelTuple => ({\n\t\tapp,\n\t\tstack,\n\t\tplugin: 'walrus',\n\t\trole,\n\t});\n\n\tswitch (mode) {\n\t\tcase 'local': {\n\t\t\tconst perNodeContainers: ReadonlyArray<ContainerLabelTuple> = Array.from(\n\t\t\t\t{ length: nodeCount },\n\t\t\t\t(_, i) => labels(`storage-node-${i}`),\n\t\t\t);\n\t\t\tconst serviceContainers = clientServiceRoles.map(labels);\n\t\t\treturn {\n\t\t\t\tkind: 'snapshotable',\n\t\t\t\t// `runtime/walrus/<name>/deploy/` — the deploy one-shot's\n\t\t\t\t// output dir. The substrate's runtime-dir root is\n\t\t\t\t// resolved at acquire; the path here is relative to it.\n\t\t\t\tsubtrees: [`walrus/${walrusName}/deploy/`],\n\t\t\t\tmanagedContainers: [...perNodeContainers, ...serviceContainers],\n\t\t\t\tmissingTolerance: 'fine',\n\t\t\t\t// Storage nodes need >10s to flush + checkpoint RocksDB\n\t\t\t\t// on `docker stop`; the snapshot quiesce mirrors the\n\t\t\t\t// stop grace (distilled-doc invariant 22). Default\n\t\t\t\t// \"pause container\" gives us this for free in the\n\t\t\t\t// substrate's adapter.\n\t\t\t\tpreRestore: Effect.succeed({\n\t\t\t\t\tkind: 'walrus' as const,\n\t\t\t\t\tmode: 'local' as const,\n\t\t\t\t\tname: walrusName,\n\t\t\t\t\tnodeCount,\n\t\t\t\t\tnetwork,\n\t\t\t\t}),\n\t\t\t\tpostRestore: Effect.void,\n\t\t\t\t// Storage-node keystores are secret material; the\n\t\t\t\t// substrate preserves 0o600 on round-trip.\n\t\t\t\tsecretMaterial: true,\n\t\t\t};\n\t\t}\n\t\tcase 'known': {\n\t\t\treturn {\n\t\t\t\tkind: 'snapshotable',\n\t\t\t\tsubtrees: [],\n\t\t\t\tmissingTolerance: 'fine',\n\t\t\t\tpreRestore: Effect.succeed({\n\t\t\t\t\tkind: 'walrus' as const,\n\t\t\t\t\tmode: 'known' as const,\n\t\t\t\t\tnetwork,\n\t\t\t\t}),\n\t\t\t};\n\t\t}\n\t}\n};\n"],"mappings":";;;;;;;;;;;AAuCA,MAAa,oBACZ,MACA,KACA,OACA,YACA,SACA,YAAY,GACZ,qBAA6D,CAAC,MACxC;CACtB,MAAM,UAAU,UAAuC;EACtD;EACA;EACA,QAAQ;EACR;CACD;CAEA,QAAQ,MAAR;EACC,KAAK,SAAS;GACb,MAAM,oBAAwD,MAAM,KACnE,EAAE,QAAQ,UAAU,IACnB,GAAG,MAAM,OAAO,gBAAgB,GAAG,CACrC;GACA,MAAM,oBAAoB,mBAAmB,IAAI,MAAM;GACvD,OAAO;IACN,MAAM;IAIN,UAAU,CAAC,UAAU,WAAW,SAAS;IACzC,mBAAmB,CAAC,GAAG,mBAAmB,GAAG,iBAAiB;IAC9D,kBAAkB;IAMlB,YAAY,OAAO,QAAQ;KAC1B,MAAM;KACN,MAAM;KACN,MAAM;KACN;KACA;IACD,CAAC;IACD,aAAa,OAAO;IAGpB,gBAAgB;GACjB;EACD;EACA,KAAK,SACJ,OAAO;GACN,MAAM;GACN,UAAU,CAAC;GACX,kBAAkB;GAClB,YAAY,OAAO,QAAQ;IAC1B,MAAM;IACN,MAAM;IACN;GACD,CAAC;EACF;CAEF;AACD"}
|
package/images/walrus/Dockerfile
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
#
|
|
9
9
|
# Two binary sets baked in:
|
|
10
10
|
#
|
|
11
|
-
# 1. walrus / walrus-deploy / walrus-node — from the walrus release
|
|
11
|
+
# 1. walrus / walrus-deploy / walrus-node / walrus-upload-relay — from the walrus release
|
|
12
12
|
# tarball at WALRUS_VERSION.
|
|
13
13
|
#
|
|
14
14
|
# 2. sui binary — from the sui release tarball at SUI_VERSION. The
|
|
@@ -26,9 +26,10 @@
|
|
|
26
26
|
# swaps SUI for WAL, and execs `walrus-node run`.
|
|
27
27
|
#
|
|
28
28
|
# - /opt/walrus/scripts/run-walrus-client-service.sh — long-running
|
|
29
|
-
# release CLI entrypoint for `walrus aggregator
|
|
30
|
-
#
|
|
31
|
-
#
|
|
29
|
+
# release CLI entrypoint for `walrus aggregator`, `walrus publisher`,
|
|
30
|
+
# and `walrus-upload-relay`.
|
|
31
|
+
# Relocates deploy-generated client config and execs the selected
|
|
32
|
+
# service command.
|
|
32
33
|
#
|
|
33
34
|
# `ubuntu:24.04` because the walrus + sui binaries link against
|
|
34
35
|
# glibc 2.38+; debian:bookworm (glibc 2.36) fails at runtime.
|
|
@@ -59,7 +60,7 @@ RUN set -eux; \
|
|
|
59
60
|
tar -xzf /tmp/walrus.tgz -C /tmp/walrus-unpack; \
|
|
60
61
|
find /tmp/walrus-unpack -maxdepth 2 -type f -executable -exec mv {} /opt/walrus/bin/ \; ; \
|
|
61
62
|
chmod +x /opt/walrus/bin/*; \
|
|
62
|
-
for bin in walrus walrus-node walrus-deploy; do \
|
|
63
|
+
for bin in walrus walrus-node walrus-deploy walrus-upload-relay; do \
|
|
63
64
|
test -x "/opt/walrus/bin/${bin}" || { \
|
|
64
65
|
echo "walrus release ${WALRUS_VERSION} missing required binary ${bin}" >&2; \
|
|
65
66
|
ls -la /opt/walrus/bin >&2; \
|
|
@@ -75,6 +76,7 @@ RUN set -eux; \
|
|
|
75
76
|
/opt/walrus/bin/walrus --version; \
|
|
76
77
|
/opt/walrus/bin/walrus aggregator --help >/dev/null; \
|
|
77
78
|
/opt/walrus/bin/walrus publisher --help >/dev/null; \
|
|
79
|
+
/opt/walrus/bin/walrus-upload-relay --help >/dev/null; \
|
|
78
80
|
/opt/walrus/bin/walrus-node --help >/dev/null; \
|
|
79
81
|
/opt/walrus/bin/walrus-deploy --help >/dev/null
|
|
80
82
|
|
|
@@ -109,6 +111,7 @@ RUN set -eux; \
|
|
|
109
111
|
RUN ln -sf /opt/walrus/bin/walrus /usr/local/bin/walrus \
|
|
110
112
|
&& ln -sf /opt/walrus/bin/walrus-node /usr/local/bin/walrus-node \
|
|
111
113
|
&& ln -sf /opt/walrus/bin/walrus-deploy /usr/local/bin/walrus-deploy \
|
|
114
|
+
&& ln -sf /opt/walrus/bin/walrus-upload-relay /usr/local/bin/walrus-upload-relay \
|
|
112
115
|
&& ln -sf /root/sui_bin/sui /usr/local/bin/sui
|
|
113
116
|
|
|
114
117
|
RUN mkdir -p /opt/walrus/scripts /opt/walrus/outputs /var/walrus/storage
|
|
@@ -126,10 +129,11 @@ STOPSIGNAL SIGINT
|
|
|
126
129
|
# their release CLI default unless overridden by env.
|
|
127
130
|
EXPOSE 9185
|
|
128
131
|
EXPOSE 31415
|
|
132
|
+
EXPOSE 3000
|
|
129
133
|
|
|
130
134
|
# Default to storage-node entrypoint. The deploy one-shot overrides
|
|
131
135
|
# via `runOneShot` argv: argv[0] == 'deploy' runs `deploy-walrus.sh`.
|
|
132
|
-
# `aggregator` and `
|
|
136
|
+
# `aggregator`, `publisher`, and `upload-relay` run release-provided client services;
|
|
133
137
|
# any other command falls back to the storage-node entrypoint.
|
|
134
|
-
ENTRYPOINT ["/bin/bash", "-c", "case \"${1:-}\" in deploy) shift; exec /opt/walrus/scripts/deploy-walrus.sh \"$@\" ;; aggregator|publisher) mode=\"$1\"; shift; exec /opt/walrus/scripts/run-walrus-client-service.sh \"$mode\" \"$@\" ;; *) exec /opt/walrus/scripts/run-walrus.sh ;; esac", "--"]
|
|
138
|
+
ENTRYPOINT ["/bin/bash", "-c", "case \"${1:-}\" in deploy) shift; exec /opt/walrus/scripts/deploy-walrus.sh \"$@\" ;; aggregator|publisher|upload-relay) mode=\"$1\"; shift; exec /opt/walrus/scripts/run-walrus-client-service.sh \"$mode\" \"$@\" ;; *) exec /opt/walrus/scripts/run-walrus.sh ;; esac", "--"]
|
|
135
139
|
CMD []
|
|
@@ -10,7 +10,7 @@ fi
|
|
|
10
10
|
shift
|
|
11
11
|
|
|
12
12
|
case "$MODE" in
|
|
13
|
-
aggregator|publisher) ;;
|
|
13
|
+
aggregator|publisher|upload-relay) ;;
|
|
14
14
|
*)
|
|
15
15
|
echo "run-walrus-client-service: unsupported mode '$MODE'" >&2
|
|
16
16
|
exit 2
|
|
@@ -25,6 +25,7 @@ CLIENT_KEYSTORE_SOURCE="$WORKING_DIR/sui_client.keystore"
|
|
|
25
25
|
CLIENT_CONFIG_TARGET="/root/.config/walrus/client_config.yaml"
|
|
26
26
|
CLIENT_WALLET_TARGET="/root/.sui/sui_config/client.yaml"
|
|
27
27
|
CLIENT_KEYSTORE_TARGET="/root/.sui/sui_config/sui.keystore"
|
|
28
|
+
UPLOAD_RELAY_CONFIG_TARGET="/root/.config/walrus/walrus_upload_relay_config.yaml"
|
|
28
29
|
|
|
29
30
|
mkdir -p /root/.sui/sui_config /root/.config/walrus /var/walrus
|
|
30
31
|
|
|
@@ -76,27 +77,58 @@ if [ -n "${SUI_RPC_URL:-}" ]; then
|
|
|
76
77
|
echo "run-walrus-client-service: wallet config did not rewrite rpc to ${SUI_RPC_URL}" >&2
|
|
77
78
|
exit 3
|
|
78
79
|
fi
|
|
80
|
+
if grep -Eq '^[[:space:]]*rpc_urls:[[:space:]]*\[\][[:space:]]*$' "$CLIENT_CONFIG_TARGET"; then
|
|
81
|
+
sed -i -E \
|
|
82
|
+
"s|^([[:space:]]*)rpc_urls:[[:space:]]*\[\][[:space:]]*$|\\1rpc_urls:\\
|
|
83
|
+
\\1- ${escaped_sui_rpc_url}|" \
|
|
84
|
+
"$CLIENT_CONFIG_TARGET"
|
|
85
|
+
fi
|
|
86
|
+
if ! grep -Fq -- "- ${SUI_RPC_URL}" "$CLIENT_CONFIG_TARGET"; then
|
|
87
|
+
echo "run-walrus-client-service: Walrus config did not rewrite rpc_urls to ${SUI_RPC_URL}" >&2
|
|
88
|
+
exit 3
|
|
89
|
+
fi
|
|
79
90
|
fi
|
|
80
91
|
|
|
81
|
-
cmd=(
|
|
82
|
-
/opt/walrus/bin/walrus
|
|
83
|
-
"$MODE"
|
|
84
|
-
--bind-address "$BIND_ADDRESS"
|
|
85
|
-
--config "$CLIENT_CONFIG_TARGET"
|
|
86
|
-
--wallet "$CLIENT_WALLET_TARGET"
|
|
87
|
-
)
|
|
88
|
-
|
|
89
92
|
case "$MODE" in
|
|
90
93
|
aggregator)
|
|
94
|
+
cmd=(
|
|
95
|
+
/opt/walrus/bin/walrus
|
|
96
|
+
"$MODE"
|
|
97
|
+
--bind-address "$BIND_ADDRESS"
|
|
98
|
+
--config "$CLIENT_CONFIG_TARGET"
|
|
99
|
+
--wallet "$CLIENT_WALLET_TARGET"
|
|
100
|
+
)
|
|
91
101
|
if [ -n "${SUI_RPC_URL:-}" ]; then
|
|
92
102
|
cmd+=(--rpc-url "$SUI_RPC_URL")
|
|
93
103
|
fi
|
|
94
104
|
;;
|
|
95
105
|
publisher)
|
|
106
|
+
cmd=(
|
|
107
|
+
/opt/walrus/bin/walrus
|
|
108
|
+
"$MODE"
|
|
109
|
+
--bind-address "$BIND_ADDRESS"
|
|
110
|
+
--config "$CLIENT_CONFIG_TARGET"
|
|
111
|
+
--wallet "$CLIENT_WALLET_TARGET"
|
|
112
|
+
)
|
|
96
113
|
sub_wallets_dir="/var/walrus/${MODE}-sub-wallets"
|
|
97
114
|
mkdir -p "$sub_wallets_dir"
|
|
98
115
|
cmd+=(--sub-wallets-dir "$sub_wallets_dir")
|
|
99
116
|
;;
|
|
117
|
+
upload-relay)
|
|
118
|
+
cat > "$UPLOAD_RELAY_CONFIG_TARGET" <<-EOF
|
|
119
|
+
tip_config: !no_tip
|
|
120
|
+
tx_freshness_threshold_secs: 36000
|
|
121
|
+
tx_max_future_threshold:
|
|
122
|
+
secs: 30
|
|
123
|
+
nanos: 0
|
|
124
|
+
EOF
|
|
125
|
+
cmd=(
|
|
126
|
+
/opt/walrus/bin/walrus-upload-relay
|
|
127
|
+
--walrus-config "$CLIENT_CONFIG_TARGET"
|
|
128
|
+
--server-address "$BIND_ADDRESS"
|
|
129
|
+
--relay-config "$UPLOAD_RELAY_CONFIG_TARGET"
|
|
130
|
+
)
|
|
131
|
+
;;
|
|
100
132
|
esac
|
|
101
133
|
|
|
102
134
|
echo "run-walrus-client-service: starting walrus ${MODE} on ${BIND_ADDRESS}"
|