gate-evm-tools 1.0.26 → 1.0.27
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/README.md +14 -0
- package/bin/workflow.cjs +19 -0
- package/dist/core/BytecodeChecker.d.ts.map +1 -1
- package/dist/core/BytecodeChecker.js +16 -1
- package/dist/core/BytecodeChecker.js.map +1 -1
- package/dist/core/legacyProxyArtifact.d.ts +6 -0
- package/dist/core/legacyProxyArtifact.d.ts.map +1 -0
- package/dist/core/legacyProxyArtifact.js +36 -0
- package/dist/core/legacyProxyArtifact.js.map +1 -0
- package/dist-runtime/config/hardhatAdapters.d.ts +5 -0
- package/dist-runtime/config/hardhatAdapters.js +53 -0
- package/dist-runtime/config/hardhatAdapters.js.map +1 -0
- package/dist-runtime/config/networks.d.ts +8 -0
- package/dist-runtime/config/networks.js +269 -0
- package/dist-runtime/config/networks.js.map +1 -0
- package/dist-runtime/config/openzeppelin.d.ts +28 -0
- package/dist-runtime/config/openzeppelin.js +144 -0
- package/dist-runtime/config/openzeppelin.js.map +1 -0
- package/dist-runtime/config/rpc.d.ts +7 -0
- package/dist-runtime/config/rpc.js +19 -0
- package/dist-runtime/config/rpc.js.map +1 -0
- package/dist-runtime/config/zksyncSolcPath.d.ts +1 -0
- package/dist-runtime/config/zksyncSolcPath.js +61 -0
- package/dist-runtime/config/zksyncSolcPath.js.map +1 -0
- package/dist-runtime/migrate.d.ts +13 -0
- package/dist-runtime/migrate.js +208 -0
- package/dist-runtime/migrate.js.map +1 -0
- package/dist-runtime/package.json +1 -0
- package/dist-runtime/projectHardhat.d.ts +4 -0
- package/dist-runtime/projectHardhat.js +16 -0
- package/dist-runtime/projectHardhat.js.map +1 -0
- package/dist-runtime/runtime-identity.json +4 -0
- package/dist-runtime/template-migration.json +293 -0
- package/dist-runtime/workflow/checkCode.d.ts +1 -0
- package/dist-runtime/workflow/checkCode.js +16 -0
- package/dist-runtime/workflow/checkCode.js.map +1 -0
- package/dist-runtime/workflow/checkCodeFinalize.d.ts +1 -0
- package/dist-runtime/workflow/checkCodeFinalize.js +11 -0
- package/dist-runtime/workflow/checkCodeFinalize.js.map +1 -0
- package/dist-runtime/workflow/checkCodePlan.d.ts +1 -0
- package/dist-runtime/workflow/checkCodePlan.js +11 -0
- package/dist-runtime/workflow/checkCodePlan.js.map +1 -0
- package/dist-runtime/workflow/cli.d.ts +1 -0
- package/dist-runtime/workflow/cli.js +302 -0
- package/dist-runtime/workflow/cli.js.map +1 -0
- package/dist-runtime/workflow/commitContractInfo.d.ts +1 -0
- package/dist-runtime/workflow/commitContractInfo.js +64 -0
- package/dist-runtime/workflow/commitContractInfo.js.map +1 -0
- package/dist-runtime/workflow/create2Hashes.d.ts +1 -0
- package/dist-runtime/workflow/create2Hashes.js +76 -0
- package/dist-runtime/workflow/create2Hashes.js.map +1 -0
- package/dist-runtime/workflow/forkSourceProxy.d.ts +1 -0
- package/dist-runtime/workflow/forkSourceProxy.js +151 -0
- package/dist-runtime/workflow/forkSourceProxy.js.map +1 -0
- package/dist-runtime/workflow/importState.d.ts +1 -0
- package/dist-runtime/workflow/importState.js +193 -0
- package/dist-runtime/workflow/importState.js.map +1 -0
- package/dist-runtime/workflow/lib/authority.d.ts +28 -0
- package/dist-runtime/workflow/lib/authority.js +122 -0
- package/dist-runtime/workflow/lib/authority.js.map +1 -0
- package/dist-runtime/workflow/lib/calldata.d.ts +2 -0
- package/dist-runtime/workflow/lib/calldata.js +8 -0
- package/dist-runtime/workflow/lib/calldata.js.map +1 -0
- package/dist-runtime/workflow/lib/calls.d.ts +23 -0
- package/dist-runtime/workflow/lib/calls.js +316 -0
- package/dist-runtime/workflow/lib/calls.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/args.d.ts +2 -0
- package/dist-runtime/workflow/lib/checkCode/args.js +68 -0
- package/dist-runtime/workflow/lib/checkCode/args.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/command.d.ts +5 -0
- package/dist-runtime/workflow/lib/checkCode/command.js +35 -0
- package/dist-runtime/workflow/lib/checkCode/command.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/dependencies.d.ts +6 -0
- package/dist-runtime/workflow/lib/checkCode/dependencies.js +125 -0
- package/dist-runtime/workflow/lib/checkCode/dependencies.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/finalize.d.ts +17 -0
- package/dist-runtime/workflow/lib/checkCode/finalize.js +174 -0
- package/dist-runtime/workflow/lib/checkCode/finalize.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/index.d.ts +3 -0
- package/dist-runtime/workflow/lib/checkCode/index.js +20 -0
- package/dist-runtime/workflow/lib/checkCode/index.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCode/plan.d.ts +22 -0
- package/dist-runtime/workflow/lib/checkCode/plan.js +145 -0
- package/dist-runtime/workflow/lib/checkCode/plan.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCodeReport.d.ts +25 -0
- package/dist-runtime/workflow/lib/checkCodeReport.js +102 -0
- package/dist-runtime/workflow/lib/checkCodeReport.js.map +1 -0
- package/dist-runtime/workflow/lib/checkCodeRunner.d.ts +87 -0
- package/dist-runtime/workflow/lib/checkCodeRunner.js +964 -0
- package/dist-runtime/workflow/lib/checkCodeRunner.js.map +1 -0
- package/dist-runtime/workflow/lib/cliTool.d.ts +10 -0
- package/dist-runtime/workflow/lib/cliTool.js +25 -0
- package/dist-runtime/workflow/lib/cliTool.js.map +1 -0
- package/dist-runtime/workflow/lib/config.d.ts +33 -0
- package/dist-runtime/workflow/lib/config.js +100 -0
- package/dist-runtime/workflow/lib/config.js.map +1 -0
- package/dist-runtime/workflow/lib/contractInfoCandidates.d.ts +82 -0
- package/dist-runtime/workflow/lib/contractInfoCandidates.js +441 -0
- package/dist-runtime/workflow/lib/contractInfoCandidates.js.map +1 -0
- package/dist-runtime/workflow/lib/create2.d.ts +49 -0
- package/dist-runtime/workflow/lib/create2.js +194 -0
- package/dist-runtime/workflow/lib/create2.js.map +1 -0
- package/dist-runtime/workflow/lib/create2Hashes.d.ts +22 -0
- package/dist-runtime/workflow/lib/create2Hashes.js +57 -0
- package/dist-runtime/workflow/lib/create2Hashes.js.map +1 -0
- package/dist-runtime/workflow/lib/deploymentInfo.d.ts +40 -0
- package/dist-runtime/workflow/lib/deploymentInfo.js +240 -0
- package/dist-runtime/workflow/lib/deploymentInfo.js.map +1 -0
- package/dist-runtime/workflow/lib/deploymentRecords.d.ts +137 -0
- package/dist-runtime/workflow/lib/deploymentRecords.js +374 -0
- package/dist-runtime/workflow/lib/deploymentRecords.js.map +1 -0
- package/dist-runtime/workflow/lib/deployments.d.ts +21 -0
- package/dist-runtime/workflow/lib/deployments.js +240 -0
- package/dist-runtime/workflow/lib/deployments.js.map +1 -0
- package/dist-runtime/workflow/lib/erc1967.d.ts +5 -0
- package/dist-runtime/workflow/lib/erc1967.js +29 -0
- package/dist-runtime/workflow/lib/erc1967.js.map +1 -0
- package/dist-runtime/workflow/lib/errors.d.ts +6 -0
- package/dist-runtime/workflow/lib/errors.js +77 -0
- package/dist-runtime/workflow/lib/errors.js.map +1 -0
- package/dist-runtime/workflow/lib/executionId.d.ts +7 -0
- package/dist-runtime/workflow/lib/executionId.js +26 -0
- package/dist-runtime/workflow/lib/executionId.js.map +1 -0
- package/dist-runtime/workflow/lib/executionPlan.d.ts +85 -0
- package/dist-runtime/workflow/lib/executionPlan.js +450 -0
- package/dist-runtime/workflow/lib/executionPlan.js.map +1 -0
- package/dist-runtime/workflow/lib/executionProgress.d.ts +5 -0
- package/dist-runtime/workflow/lib/executionProgress.js +100 -0
- package/dist-runtime/workflow/lib/executionProgress.js.map +1 -0
- package/dist-runtime/workflow/lib/external.d.ts +62 -0
- package/dist-runtime/workflow/lib/external.js +282 -0
- package/dist-runtime/workflow/lib/external.js.map +1 -0
- package/dist-runtime/workflow/lib/externalExecutionScanner.d.ts +64 -0
- package/dist-runtime/workflow/lib/externalExecutionScanner.js +638 -0
- package/dist-runtime/workflow/lib/externalExecutionScanner.js.map +1 -0
- package/dist-runtime/workflow/lib/forkSnapshot.d.ts +34 -0
- package/dist-runtime/workflow/lib/forkSnapshot.js +167 -0
- package/dist-runtime/workflow/lib/forkSnapshot.js.map +1 -0
- package/dist-runtime/workflow/lib/gateTool.d.ts +16 -0
- package/dist-runtime/workflow/lib/gateTool.js +177 -0
- package/dist-runtime/workflow/lib/gateTool.js.map +1 -0
- package/dist-runtime/workflow/lib/gateToolSync.d.ts +22 -0
- package/dist-runtime/workflow/lib/gateToolSync.js +253 -0
- package/dist-runtime/workflow/lib/gateToolSync.js.map +1 -0
- package/dist-runtime/workflow/lib/lock.d.ts +52 -0
- package/dist-runtime/workflow/lib/lock.js +269 -0
- package/dist-runtime/workflow/lib/lock.js.map +1 -0
- package/dist-runtime/workflow/lib/migrationState.d.ts +103 -0
- package/dist-runtime/workflow/lib/migrationState.js +920 -0
- package/dist-runtime/workflow/lib/migrationState.js.map +1 -0
- package/dist-runtime/workflow/lib/operatorExecution.d.ts +28 -0
- package/dist-runtime/workflow/lib/operatorExecution.js +134 -0
- package/dist-runtime/workflow/lib/operatorExecution.js.map +1 -0
- package/dist-runtime/workflow/lib/parameters.d.ts +58 -0
- package/dist-runtime/workflow/lib/parameters.js +322 -0
- package/dist-runtime/workflow/lib/parameters.js.map +1 -0
- package/dist-runtime/workflow/lib/pendingDeploymentReuse.d.ts +29 -0
- package/dist-runtime/workflow/lib/pendingDeploymentReuse.js +277 -0
- package/dist-runtime/workflow/lib/pendingDeploymentReuse.js.map +1 -0
- package/dist-runtime/workflow/lib/proxy.d.ts +18 -0
- package/dist-runtime/workflow/lib/proxy.js +19 -0
- package/dist-runtime/workflow/lib/proxy.js.map +1 -0
- package/dist-runtime/workflow/lib/registryCommit.d.ts +33 -0
- package/dist-runtime/workflow/lib/registryCommit.js +319 -0
- package/dist-runtime/workflow/lib/registryCommit.js.map +1 -0
- package/dist-runtime/workflow/lib/release.d.ts +57 -0
- package/dist-runtime/workflow/lib/release.js +256 -0
- package/dist-runtime/workflow/lib/release.js.map +1 -0
- package/dist-runtime/workflow/lib/releaseFinalize.d.ts +14 -0
- package/dist-runtime/workflow/lib/releaseFinalize.js +571 -0
- package/dist-runtime/workflow/lib/releaseFinalize.js.map +1 -0
- package/dist-runtime/workflow/lib/releaseParameters.d.ts +43 -0
- package/dist-runtime/workflow/lib/releaseParameters.js +184 -0
- package/dist-runtime/workflow/lib/releaseParameters.js.map +1 -0
- package/dist-runtime/workflow/lib/reporter.d.ts +39 -0
- package/dist-runtime/workflow/lib/reporter.js +452 -0
- package/dist-runtime/workflow/lib/reporter.js.map +1 -0
- package/dist-runtime/workflow/lib/resultPaths.d.ts +17 -0
- package/dist-runtime/workflow/lib/resultPaths.js +29 -0
- package/dist-runtime/workflow/lib/resultPaths.js.map +1 -0
- package/dist-runtime/workflow/lib/runtimeVersion.d.ts +10 -0
- package/dist-runtime/workflow/lib/runtimeVersion.js +46 -0
- package/dist-runtime/workflow/lib/runtimeVersion.js.map +1 -0
- package/dist-runtime/workflow/lib/signer.d.ts +10 -0
- package/dist-runtime/workflow/lib/signer.js +105 -0
- package/dist-runtime/workflow/lib/signer.js.map +1 -0
- package/dist-runtime/workflow/lib/signerIdentity.d.ts +1 -0
- package/dist-runtime/workflow/lib/signerIdentity.js +23 -0
- package/dist-runtime/workflow/lib/signerIdentity.js.map +1 -0
- package/dist-runtime/workflow/lib/simulation.d.ts +35 -0
- package/dist-runtime/workflow/lib/simulation.js +282 -0
- package/dist-runtime/workflow/lib/simulation.js.map +1 -0
- package/dist-runtime/workflow/lib/simulationCallers.d.ts +44 -0
- package/dist-runtime/workflow/lib/simulationCallers.js +260 -0
- package/dist-runtime/workflow/lib/simulationCallers.js.map +1 -0
- package/dist-runtime/workflow/lib/sourceRpcPolicy.d.ts +1 -0
- package/dist-runtime/workflow/lib/sourceRpcPolicy.js +43 -0
- package/dist-runtime/workflow/lib/sourceRpcPolicy.js.map +1 -0
- package/dist-runtime/workflow/lib/state.d.ts +6 -0
- package/dist-runtime/workflow/lib/state.js +25 -0
- package/dist-runtime/workflow/lib/state.js.map +1 -0
- package/dist-runtime/workflow/lib/staticValidation.d.ts +22 -0
- package/dist-runtime/workflow/lib/staticValidation.js +124 -0
- package/dist-runtime/workflow/lib/staticValidation.js.map +1 -0
- package/dist-runtime/workflow/lib/zksyncForkCapability.d.ts +27 -0
- package/dist-runtime/workflow/lib/zksyncForkCapability.js +52 -0
- package/dist-runtime/workflow/lib/zksyncForkCapability.js.map +1 -0
- package/dist-runtime/workflow/lib/zksyncForkRunner.d.ts +57 -0
- package/dist-runtime/workflow/lib/zksyncForkRunner.js +344 -0
- package/dist-runtime/workflow/lib/zksyncForkRunner.js.map +1 -0
- package/dist-runtime/workflow/reconcileExternal.d.ts +1 -0
- package/dist-runtime/workflow/reconcileExternal.js +87 -0
- package/dist-runtime/workflow/reconcileExternal.js.map +1 -0
- package/dist-runtime/workflow/reconcileExternalRun.d.ts +1 -0
- package/dist-runtime/workflow/reconcileExternalRun.js +77 -0
- package/dist-runtime/workflow/reconcileExternalRun.js.map +1 -0
- package/dist-runtime/workflow/releaseFinalize.d.ts +1 -0
- package/dist-runtime/workflow/releaseFinalize.js +11 -0
- package/dist-runtime/workflow/releaseFinalize.js.map +1 -0
- package/dist-runtime/workflow/run.d.ts +40 -0
- package/dist-runtime/workflow/run.js +858 -0
- package/dist-runtime/workflow/run.js.map +1 -0
- package/dist-runtime/workflow/staticValidate.d.ts +1 -0
- package/dist-runtime/workflow/staticValidate.js +26 -0
- package/dist-runtime/workflow/staticValidate.js.map +1 -0
- package/dist-runtime/workflow/steps/call.d.ts +3 -0
- package/dist-runtime/workflow/steps/call.js +9 -0
- package/dist-runtime/workflow/steps/call.js.map +1 -0
- package/dist-runtime/workflow/steps/check.d.ts +3 -0
- package/dist-runtime/workflow/steps/check.js +9 -0
- package/dist-runtime/workflow/steps/check.js.map +1 -0
- package/dist-runtime/workflow/steps/custom.d.ts +3 -0
- package/dist-runtime/workflow/steps/custom.js +33 -0
- package/dist-runtime/workflow/steps/custom.js.map +1 -0
- package/dist-runtime/workflow/steps/deploy.d.ts +3 -0
- package/dist-runtime/workflow/steps/deploy.js +595 -0
- package/dist-runtime/workflow/steps/deploy.js.map +1 -0
- package/dist-runtime/workflow/steps/upgrade.d.ts +3 -0
- package/dist-runtime/workflow/steps/upgrade.js +465 -0
- package/dist-runtime/workflow/steps/upgrade.js.map +1 -0
- package/dist-runtime/workflow/steps/view.d.ts +3 -0
- package/dist-runtime/workflow/steps/view.js +9 -0
- package/dist-runtime/workflow/steps/view.js.map +1 -0
- package/package.json +25 -5
- package/runtime/config/hardhatAdapters.ts +50 -0
- package/runtime/config/networks.ts +296 -0
- package/runtime/config/openzeppelin.ts +173 -0
- package/runtime/config/rpc.ts +27 -0
- package/runtime/config/zksyncSolcPath.ts +59 -0
- package/runtime/hardhat-types.d.ts +2 -0
- package/runtime/migrate.ts +154 -0
- package/runtime/projectHardhat.ts +10 -0
- package/runtime/template-migration.json +293 -0
- package/runtime/workflow/checkCode.ts +13 -0
- package/runtime/workflow/checkCodeFinalize.ts +8 -0
- package/runtime/workflow/checkCodePlan.ts +8 -0
- package/runtime/workflow/cli.ts +356 -0
- package/runtime/workflow/commitContractInfo.ts +63 -0
- package/runtime/workflow/create2Hashes.ts +79 -0
- package/runtime/workflow/forkSourceProxy.ts +193 -0
- package/runtime/workflow/importState.ts +190 -0
- package/runtime/workflow/lib/authority.ts +170 -0
- package/runtime/workflow/lib/calldata.ts +9 -0
- package/runtime/workflow/lib/calls.ts +407 -0
- package/runtime/workflow/lib/checkCode/args.ts +76 -0
- package/runtime/workflow/lib/checkCode/command.ts +36 -0
- package/runtime/workflow/lib/checkCode/dependencies.ts +112 -0
- package/runtime/workflow/lib/checkCode/finalize.ts +198 -0
- package/runtime/workflow/lib/checkCode/index.ts +18 -0
- package/runtime/workflow/lib/checkCode/plan.ts +163 -0
- package/runtime/workflow/lib/checkCodeReport.ts +133 -0
- package/runtime/workflow/lib/checkCodeRunner.ts +1225 -0
- package/runtime/workflow/lib/cliTool.ts +31 -0
- package/runtime/workflow/lib/config.ts +130 -0
- package/runtime/workflow/lib/contractInfoCandidates.ts +690 -0
- package/runtime/workflow/lib/create2.ts +278 -0
- package/runtime/workflow/lib/create2Hashes.ts +83 -0
- package/runtime/workflow/lib/deploymentInfo.ts +286 -0
- package/runtime/workflow/lib/deploymentRecords.ts +576 -0
- package/runtime/workflow/lib/deployments.ts +276 -0
- package/runtime/workflow/lib/erc1967.ts +57 -0
- package/runtime/workflow/lib/errors.ts +95 -0
- package/runtime/workflow/lib/executionId.ts +35 -0
- package/runtime/workflow/lib/executionPlan.ts +550 -0
- package/runtime/workflow/lib/executionProgress.ts +127 -0
- package/runtime/workflow/lib/external.ts +372 -0
- package/runtime/workflow/lib/externalExecutionScanner.ts +892 -0
- package/runtime/workflow/lib/forkSnapshot.ts +231 -0
- package/runtime/workflow/lib/gateTool.ts +261 -0
- package/runtime/workflow/lib/gateToolSync.ts +398 -0
- package/runtime/workflow/lib/lock.ts +388 -0
- package/runtime/workflow/lib/migrationState.ts +1246 -0
- package/runtime/workflow/lib/operatorExecution.ts +174 -0
- package/runtime/workflow/lib/parameters.ts +437 -0
- package/runtime/workflow/lib/pendingDeploymentReuse.ts +428 -0
- package/runtime/workflow/lib/proxy.ts +45 -0
- package/runtime/workflow/lib/registryCommit.ts +455 -0
- package/runtime/workflow/lib/release.ts +329 -0
- package/runtime/workflow/lib/releaseFinalize.ts +711 -0
- package/runtime/workflow/lib/releaseParameters.ts +248 -0
- package/runtime/workflow/lib/reporter.ts +502 -0
- package/runtime/workflow/lib/resultPaths.ts +49 -0
- package/runtime/workflow/lib/runtimeVersion.ts +28 -0
- package/runtime/workflow/lib/signer.ts +124 -0
- package/runtime/workflow/lib/signerIdentity.ts +22 -0
- package/runtime/workflow/lib/simulation.ts +415 -0
- package/runtime/workflow/lib/simulationCallers.ts +335 -0
- package/runtime/workflow/lib/sourceRpcPolicy.ts +41 -0
- package/runtime/workflow/lib/state.ts +25 -0
- package/runtime/workflow/lib/staticValidation.ts +142 -0
- package/runtime/workflow/lib/zksyncForkCapability.ts +80 -0
- package/runtime/workflow/lib/zksyncForkRunner.ts +505 -0
- package/runtime/workflow/reconcileExternal.ts +86 -0
- package/runtime/workflow/reconcileExternalRun.ts +88 -0
- package/runtime/workflow/releaseFinalize.ts +8 -0
- package/runtime/workflow/run.ts +1072 -0
- package/runtime/workflow/staticValidate.ts +23 -0
- package/runtime/workflow/steps/call.ts +10 -0
- package/runtime/workflow/steps/check.ts +10 -0
- package/runtime/workflow/steps/custom.ts +43 -0
- package/runtime/workflow/steps/deploy.ts +771 -0
- package/runtime/workflow/steps/upgrade.ts +598 -0
- package/runtime/workflow/steps/view.ts +10 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { readJsonFile, writeJsonFile } from "./config";
|
|
4
|
+
import { WorkflowMode } from "./simulation";
|
|
5
|
+
import {
|
|
6
|
+
WORKFLOW_RESULT_NAMES,
|
|
7
|
+
WorkflowResultName,
|
|
8
|
+
workflowResultRelativeDirectory,
|
|
9
|
+
workflowResultRelativePath,
|
|
10
|
+
} from "./resultPaths";
|
|
11
|
+
|
|
12
|
+
const RUNTIME_STATUS_FIELDS = [
|
|
13
|
+
"blockedReasons",
|
|
14
|
+
"errorType",
|
|
15
|
+
"retryable",
|
|
16
|
+
"message",
|
|
17
|
+
"stepId",
|
|
18
|
+
"simulationResult",
|
|
19
|
+
"planResult",
|
|
20
|
+
] as const;
|
|
21
|
+
const STATUS_LOCK_STALE_MS = 30_000;
|
|
22
|
+
const STATUS_LOCK_WAIT_MS = 35_000;
|
|
23
|
+
const STATUS_LOCK_POLL_MS = 10;
|
|
24
|
+
const STATUS_LOCK_SLEEP = new Int32Array(new SharedArrayBuffer(4));
|
|
25
|
+
|
|
26
|
+
const isNonZeroEvmAddress = (value: unknown): value is string => {
|
|
27
|
+
return typeof value === "string"
|
|
28
|
+
&& /^0x[0-9a-fA-F]{40}$/.test(value)
|
|
29
|
+
&& value.toLowerCase() !== "0x0000000000000000000000000000000000000000";
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export class WorkflowReporter {
|
|
33
|
+
private readonly initializedResults = new Set<WorkflowResultName>();
|
|
34
|
+
private simulationCallerAddress?: string;
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
private readonly taskDir: string,
|
|
38
|
+
private readonly taskId: string,
|
|
39
|
+
private readonly executionId: string,
|
|
40
|
+
private readonly targetId: string,
|
|
41
|
+
private readonly mode: WorkflowMode = "execute",
|
|
42
|
+
) {}
|
|
43
|
+
|
|
44
|
+
initializeResults(names: readonly WorkflowResultName[] = WORKFLOW_RESULT_NAMES): void {
|
|
45
|
+
const directory = this.resultDirectoryPath();
|
|
46
|
+
fs.mkdirSync(path.dirname(directory), { recursive: true });
|
|
47
|
+
try {
|
|
48
|
+
fs.mkdirSync(directory);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
51
|
+
this.retryFailedVerification(directory, names);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.writeInitialResults(names);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private writeInitialResults(names: readonly WorkflowResultName[]): void {
|
|
59
|
+
for (const name of names) {
|
|
60
|
+
writeJsonFile(this.resultPath(name), this.resultEnvelope([]));
|
|
61
|
+
this.initializedResults.add(name);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private retryFailedVerification(
|
|
66
|
+
directory: string,
|
|
67
|
+
names: readonly WorkflowResultName[],
|
|
68
|
+
): void {
|
|
69
|
+
const claimedError = (): Error => new Error(
|
|
70
|
+
`Workflow result namespace already claimed for target "${this.targetId}", executionId "${this.executionId}", mode "${this.mode}"; use a new executionId`,
|
|
71
|
+
);
|
|
72
|
+
if (this.mode === "execute" && this.resumeSegmentedAdminExecution(names)) return;
|
|
73
|
+
if (this.mode !== "verify") throw claimedError();
|
|
74
|
+
|
|
75
|
+
const parent = path.dirname(directory);
|
|
76
|
+
const lockPath = path.join(parent, ".verify-retry.lock");
|
|
77
|
+
let lock: number;
|
|
78
|
+
try {
|
|
79
|
+
lock = fs.openSync(lockPath, "wx");
|
|
80
|
+
} catch {
|
|
81
|
+
throw claimedError();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let archivePath: string | undefined;
|
|
85
|
+
try {
|
|
86
|
+
const status = this.readStatusFile(path.join(this.taskDir, "status.json"));
|
|
87
|
+
const networkStatus = status.networks?.[this.targetId];
|
|
88
|
+
if (
|
|
89
|
+
networkStatus?.lastExecutionId !== this.executionId
|
|
90
|
+
|| networkStatus.lastExecutionMode !== "verify"
|
|
91
|
+
|| networkStatus.verifyStatus !== "failed"
|
|
92
|
+
) {
|
|
93
|
+
throw claimedError();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
97
|
+
const archiveBase = path.join(parent, `verify.failed.${timestamp}`);
|
|
98
|
+
archivePath = archiveBase;
|
|
99
|
+
for (let suffix = 1; fs.existsSync(archivePath); suffix += 1) {
|
|
100
|
+
archivePath = `${archiveBase}.${suffix}`;
|
|
101
|
+
}
|
|
102
|
+
fs.renameSync(directory, archivePath);
|
|
103
|
+
fs.mkdirSync(directory);
|
|
104
|
+
this.writeInitialResults(names);
|
|
105
|
+
this.status("running", { mode: this.mode });
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (archivePath && fs.existsSync(archivePath)) {
|
|
108
|
+
fs.rmSync(directory, { recursive: true, force: true });
|
|
109
|
+
fs.renameSync(archivePath, directory);
|
|
110
|
+
}
|
|
111
|
+
throw error;
|
|
112
|
+
} finally {
|
|
113
|
+
fs.closeSync(lock);
|
|
114
|
+
fs.unlinkSync(lockPath);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private resumeSegmentedAdminExecution(names: readonly WorkflowResultName[]): boolean {
|
|
119
|
+
const executionRoot = path.join(
|
|
120
|
+
this.taskDir,
|
|
121
|
+
"results",
|
|
122
|
+
this.targetId,
|
|
123
|
+
this.executionId,
|
|
124
|
+
);
|
|
125
|
+
const planPath = path.join(executionRoot, "execution-plan.json");
|
|
126
|
+
const reconciliationPath = path.join(executionRoot, "external-reconciliation.json");
|
|
127
|
+
if (!fs.existsSync(planPath) || !fs.existsSync(reconciliationPath)) return false;
|
|
128
|
+
let plan: Record<string, unknown>;
|
|
129
|
+
let reconciliation: Record<string, unknown>;
|
|
130
|
+
try {
|
|
131
|
+
plan = readJsonFile<Record<string, unknown>>(planPath);
|
|
132
|
+
reconciliation = readJsonFile<Record<string, unknown>>(reconciliationPath);
|
|
133
|
+
} catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const status = this.readStatusFile(path.join(this.taskDir, "status.json"));
|
|
137
|
+
const networkStatus = status.networks?.[this.targetId];
|
|
138
|
+
if (
|
|
139
|
+
plan.version !== 1
|
|
140
|
+
|| plan.taskId !== this.taskId
|
|
141
|
+
|| plan.target !== this.targetId
|
|
142
|
+
|| plan.executionId !== this.executionId
|
|
143
|
+
|| plan.releaseKind !== "admin_release"
|
|
144
|
+
|| typeof plan.planHash !== "string"
|
|
145
|
+
|| reconciliation.version !== 1
|
|
146
|
+
|| reconciliation.status !== "waiting_operator"
|
|
147
|
+
|| reconciliation.taskId !== this.taskId
|
|
148
|
+
|| reconciliation.target !== this.targetId
|
|
149
|
+
|| reconciliation.executionId !== this.executionId
|
|
150
|
+
|| reconciliation.executionPlanHash !== plan.planHash
|
|
151
|
+
|| networkStatus?.lastExecutionId !== this.executionId
|
|
152
|
+
|| networkStatus.lastExecutionMode !== "execute"
|
|
153
|
+
) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
for (const name of names) {
|
|
157
|
+
if (!fs.existsSync(this.resultPath(name))) return false;
|
|
158
|
+
const result = readJsonFile<Record<string, unknown>>(this.resultPath(name));
|
|
159
|
+
if (
|
|
160
|
+
result.taskId !== this.taskId
|
|
161
|
+
|| result.executionId !== this.executionId
|
|
162
|
+
|| result.target !== this.targetId
|
|
163
|
+
|| result.mode !== "execute"
|
|
164
|
+
|| !Array.isArray(result.steps)
|
|
165
|
+
) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
this.initializedResults.add(name);
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
event(type: string, payload: Record<string, unknown> = {}): void {
|
|
174
|
+
const filePath = path.join(this.taskDir, "events.jsonl");
|
|
175
|
+
fs.appendFileSync(
|
|
176
|
+
filePath,
|
|
177
|
+
`${JSON.stringify({
|
|
178
|
+
taskId: this.taskId,
|
|
179
|
+
executionId: this.executionId,
|
|
180
|
+
target: this.targetId,
|
|
181
|
+
mode: this.mode,
|
|
182
|
+
type,
|
|
183
|
+
timestamp: new Date().toISOString(),
|
|
184
|
+
...payload,
|
|
185
|
+
})}\n`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
status(status: string, payload: Record<string, unknown> = {}): void {
|
|
190
|
+
const filePath = path.join(this.taskDir, "status.json");
|
|
191
|
+
this.withStatusFileLock(filePath, () => {
|
|
192
|
+
const existing = this.readStatusFile(filePath);
|
|
193
|
+
const baseStatus = this.clearRuntimeStatusFields(existing);
|
|
194
|
+
const targetNetworks = this.targetNetworks(existing);
|
|
195
|
+
const networks = {
|
|
196
|
+
...(existing.networks || {}),
|
|
197
|
+
[this.targetId]: this.updateNetworkStatus(
|
|
198
|
+
existing.networks?.[this.targetId] || {},
|
|
199
|
+
status,
|
|
200
|
+
),
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
writeJsonFile(filePath, {
|
|
204
|
+
...baseStatus,
|
|
205
|
+
targetNetworks,
|
|
206
|
+
networks,
|
|
207
|
+
status: this.globalStatus(status, targetNetworks, networks),
|
|
208
|
+
target: this.targetId,
|
|
209
|
+
executionId: this.executionId,
|
|
210
|
+
mode: this.mode,
|
|
211
|
+
updatedAt: new Date().toISOString(),
|
|
212
|
+
...payload,
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
result(name: WorkflowResultName, payload: Record<string, unknown>): void {
|
|
218
|
+
if (!this.initializedResults.has(name)) {
|
|
219
|
+
throw new Error(`Workflow result ${name} was not initialized for this execution`);
|
|
220
|
+
}
|
|
221
|
+
const filePath = this.resultPath(name);
|
|
222
|
+
const existing = readJsonFile<{ steps?: unknown }>(filePath);
|
|
223
|
+
if (!Array.isArray(existing.steps)) {
|
|
224
|
+
throw new Error(`Workflow result ${name} has an invalid steps array`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
writeJsonFile(filePath, this.resultEnvelope([
|
|
228
|
+
...(existing.steps as Record<string, unknown>[]),
|
|
229
|
+
{
|
|
230
|
+
timestamp: new Date().toISOString(),
|
|
231
|
+
...payload,
|
|
232
|
+
...(this.simulationCallerAddress
|
|
233
|
+
? { simulationCallerAddress: this.simulationCallerAddress }
|
|
234
|
+
: {}),
|
|
235
|
+
},
|
|
236
|
+
]));
|
|
237
|
+
this.initializedResults.add(name);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
setSimulationCallerAddress(callerAddress: string): void {
|
|
241
|
+
if (this.mode !== "simulate" || !isNonZeroEvmAddress(callerAddress)) {
|
|
242
|
+
throw new Error("Simulation caller scope requires a valid simulate-mode address");
|
|
243
|
+
}
|
|
244
|
+
this.simulationCallerAddress = callerAddress;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
clearSimulationCallerAddress(): void {
|
|
248
|
+
this.simulationCallerAddress = undefined;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
simulationExecutionResultPath(expected: string | {
|
|
252
|
+
simulationCallerNetworkHash: string;
|
|
253
|
+
simulationCallers: Array<{ stepId: string; callerAddress: string }>;
|
|
254
|
+
}): string {
|
|
255
|
+
if (this.mode !== "simulate") {
|
|
256
|
+
throw new Error("Simulation execution results require simulate mode");
|
|
257
|
+
}
|
|
258
|
+
const result = readJsonFile<{
|
|
259
|
+
executionId?: unknown;
|
|
260
|
+
target?: unknown;
|
|
261
|
+
mode?: unknown;
|
|
262
|
+
steps?: unknown;
|
|
263
|
+
}>(this.resultPath("execution"));
|
|
264
|
+
if (
|
|
265
|
+
result.executionId !== this.executionId
|
|
266
|
+
|| result.target !== this.targetId
|
|
267
|
+
|| result.mode !== "simulate"
|
|
268
|
+
|| !Array.isArray(result.steps)
|
|
269
|
+
) {
|
|
270
|
+
throw new Error("Simulation execution result does not match this workflow run");
|
|
271
|
+
}
|
|
272
|
+
const terminal = result.steps.at(-1) as Record<string, unknown> | undefined;
|
|
273
|
+
if (
|
|
274
|
+
terminal?.kind !== "workflow-simulation-completed"
|
|
275
|
+
|| terminal.status !== "succeeded"
|
|
276
|
+
|| !Array.isArray(terminal.selectedStepIds)
|
|
277
|
+
|| terminal.selectedStepIds.length === 0
|
|
278
|
+
) {
|
|
279
|
+
throw new Error("Simulation execution result is missing canonical completion evidence");
|
|
280
|
+
}
|
|
281
|
+
if (typeof expected === "string") {
|
|
282
|
+
if (
|
|
283
|
+
!isNonZeroEvmAddress(terminal.signerAddress)
|
|
284
|
+
|| !isNonZeroEvmAddress(expected)
|
|
285
|
+
|| terminal.signerAddress.toLowerCase() !== expected.toLowerCase()
|
|
286
|
+
) {
|
|
287
|
+
throw new Error("Simulation execution result is missing canonical completion evidence");
|
|
288
|
+
}
|
|
289
|
+
} else {
|
|
290
|
+
const actualCallers = terminal.simulationCallers;
|
|
291
|
+
if (
|
|
292
|
+
!/^[0-9a-f]{64}$/.test(expected.simulationCallerNetworkHash)
|
|
293
|
+
|| terminal.simulationCallerNetworkHash !== expected.simulationCallerNetworkHash
|
|
294
|
+
|| terminal.signerAddress !== undefined
|
|
295
|
+
|| !Array.isArray(actualCallers)
|
|
296
|
+
|| actualCallers.length !== expected.simulationCallers.length
|
|
297
|
+
|| expected.simulationCallers.some((caller, index) => {
|
|
298
|
+
const actual = actualCallers[index] as Record<string, unknown> | undefined;
|
|
299
|
+
return !actual
|
|
300
|
+
|| actual.stepId !== caller.stepId
|
|
301
|
+
|| !isNonZeroEvmAddress(actual.callerAddress)
|
|
302
|
+
|| actual.callerAddress.toLowerCase() !== caller.callerAddress.toLowerCase();
|
|
303
|
+
})
|
|
304
|
+
) {
|
|
305
|
+
throw new Error("Simulation execution result is missing canonical caller completion evidence");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return workflowResultRelativePath({
|
|
309
|
+
target: this.targetId,
|
|
310
|
+
executionId: this.executionId,
|
|
311
|
+
mode: this.mode,
|
|
312
|
+
name: "execution",
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
clearSimulationCompletionEvidence(): void {
|
|
317
|
+
if (this.mode !== "simulate") return;
|
|
318
|
+
const filePath = this.resultPath("execution");
|
|
319
|
+
if (!fs.existsSync(filePath)) return;
|
|
320
|
+
const result = readJsonFile<{ steps?: unknown }>(filePath);
|
|
321
|
+
if (!Array.isArray(result.steps)) return;
|
|
322
|
+
const terminal = result.steps.at(-1) as Record<string, unknown> | undefined;
|
|
323
|
+
if (terminal?.kind !== "workflow-simulation-completed") return;
|
|
324
|
+
writeJsonFile(
|
|
325
|
+
filePath,
|
|
326
|
+
this.resultEnvelope(result.steps.slice(0, -1) as Record<string, unknown>[]),
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
report(name: string, lines: string[]): void {
|
|
331
|
+
const filePath = path.join(this.taskDir, "docs", name);
|
|
332
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
333
|
+
fs.writeFileSync(filePath, `${lines.join("\n")}\n`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private readStatusFile(filePath: string): {
|
|
337
|
+
status?: string;
|
|
338
|
+
targetNetworks?: string[];
|
|
339
|
+
networks?: Record<string, Record<string, unknown>>;
|
|
340
|
+
[key: string]: unknown;
|
|
341
|
+
} {
|
|
342
|
+
if (!fs.existsSync(filePath)) return {};
|
|
343
|
+
try {
|
|
344
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
345
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
346
|
+
return parsed as {
|
|
347
|
+
status?: string;
|
|
348
|
+
targetNetworks?: string[];
|
|
349
|
+
networks?: Record<string, Record<string, unknown>>;
|
|
350
|
+
[key: string]: unknown;
|
|
351
|
+
};
|
|
352
|
+
} catch {
|
|
353
|
+
return {};
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private withStatusFileLock<T>(filePath: string, operation: () => T): T {
|
|
358
|
+
const lockPath = `${filePath}.lock`;
|
|
359
|
+
const token = `${process.pid}:${Date.now()}:${Math.random().toString(16).slice(2)}`;
|
|
360
|
+
const deadline = Date.now() + STATUS_LOCK_WAIT_MS;
|
|
361
|
+
while (true) {
|
|
362
|
+
let descriptor: number | undefined;
|
|
363
|
+
try {
|
|
364
|
+
descriptor = fs.openSync(lockPath, "wx");
|
|
365
|
+
fs.writeFileSync(descriptor, `${token}\n`);
|
|
366
|
+
fs.fsyncSync(descriptor);
|
|
367
|
+
fs.closeSync(descriptor);
|
|
368
|
+
descriptor = undefined;
|
|
369
|
+
break;
|
|
370
|
+
} catch (error) {
|
|
371
|
+
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
372
|
+
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
373
|
+
try {
|
|
374
|
+
if (Date.now() - fs.statSync(lockPath).mtimeMs > STATUS_LOCK_STALE_MS) {
|
|
375
|
+
fs.unlinkSync(lockPath);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
} catch (lockError) {
|
|
379
|
+
if ((lockError as NodeJS.ErrnoException).code === "ENOENT") continue;
|
|
380
|
+
throw lockError;
|
|
381
|
+
}
|
|
382
|
+
if (Date.now() >= deadline) {
|
|
383
|
+
throw new Error(`Timed out waiting for task status lock: ${lockPath}`);
|
|
384
|
+
}
|
|
385
|
+
Atomics.wait(STATUS_LOCK_SLEEP, 0, 0, STATUS_LOCK_POLL_MS);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
return operation();
|
|
391
|
+
} finally {
|
|
392
|
+
try {
|
|
393
|
+
if (fs.readFileSync(lockPath, "utf8").trim() === token) fs.unlinkSync(lockPath);
|
|
394
|
+
} catch (error) {
|
|
395
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private resultEnvelope(steps: Record<string, unknown>[]): Record<string, unknown> {
|
|
401
|
+
return {
|
|
402
|
+
taskId: this.taskId,
|
|
403
|
+
executionId: this.executionId,
|
|
404
|
+
target: this.targetId,
|
|
405
|
+
mode: this.mode,
|
|
406
|
+
updatedAt: new Date().toISOString(),
|
|
407
|
+
steps,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
private resultPath(name: WorkflowResultName): string {
|
|
412
|
+
return path.join(this.taskDir, workflowResultRelativePath({
|
|
413
|
+
target: this.targetId,
|
|
414
|
+
executionId: this.executionId,
|
|
415
|
+
mode: this.mode,
|
|
416
|
+
name,
|
|
417
|
+
}));
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private resultDirectoryPath(): string {
|
|
421
|
+
return path.join(this.taskDir, workflowResultRelativeDirectory({
|
|
422
|
+
target: this.targetId,
|
|
423
|
+
executionId: this.executionId,
|
|
424
|
+
mode: this.mode,
|
|
425
|
+
}));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
private clearRuntimeStatusFields<T extends Record<string, unknown>>(status: T): T {
|
|
429
|
+
const next = { ...status };
|
|
430
|
+
for (const field of RUNTIME_STATUS_FIELDS) {
|
|
431
|
+
delete next[field];
|
|
432
|
+
}
|
|
433
|
+
return next as T;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private targetNetworks(existing: { targetNetworks?: string[]; networks?: Record<string, unknown> }): string[] {
|
|
437
|
+
if (Array.isArray(existing.targetNetworks) && existing.targetNetworks.length > 0) {
|
|
438
|
+
return existing.targetNetworks.map((item) => String(item));
|
|
439
|
+
}
|
|
440
|
+
const networkKeys = existing.networks ? Object.keys(existing.networks) : [];
|
|
441
|
+
return Array.from(new Set([...networkKeys, this.targetId]));
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
private updateNetworkStatus(
|
|
445
|
+
current: Record<string, unknown>,
|
|
446
|
+
status: string,
|
|
447
|
+
): Record<string, unknown> {
|
|
448
|
+
const next = {
|
|
449
|
+
...current,
|
|
450
|
+
lastExecutionId: this.executionId,
|
|
451
|
+
lastExecutionMode: this.mode,
|
|
452
|
+
updatedAt: new Date().toISOString(),
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
if (this.mode === "simulate") {
|
|
456
|
+
return {
|
|
457
|
+
...next,
|
|
458
|
+
simulationStatus: status === "completed" ? "succeeded" : status,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (this.mode === "verify") {
|
|
463
|
+
if (status === "completed") return { ...next, verifyStatus: "verified" };
|
|
464
|
+
if (status === "failed") return { ...next, verifyStatus: "failed" };
|
|
465
|
+
if (status === "running") return { ...next, verifyStatus: "running" };
|
|
466
|
+
return next;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (status === "completed") {
|
|
470
|
+
return { ...next, deployStatus: "deployed" };
|
|
471
|
+
}
|
|
472
|
+
if (status === "waiting_external") {
|
|
473
|
+
return { ...next, deployStatus: "waiting_external" };
|
|
474
|
+
}
|
|
475
|
+
if (status === "waiting_code_check") {
|
|
476
|
+
return { ...next, deployStatus: "waiting_code_check" };
|
|
477
|
+
}
|
|
478
|
+
if (status === "failed") {
|
|
479
|
+
return { ...next, deployStatus: "failed" };
|
|
480
|
+
}
|
|
481
|
+
return next;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
private globalStatus(
|
|
485
|
+
fallback: string,
|
|
486
|
+
targetNetworks: string[],
|
|
487
|
+
networks: Record<string, Record<string, unknown>>,
|
|
488
|
+
): string {
|
|
489
|
+
if (fallback === "waiting_external") return "externalPending";
|
|
490
|
+
if (fallback !== "completed") return fallback;
|
|
491
|
+
if (this.mode === "simulate") return "simulated";
|
|
492
|
+
const targetStates = targetNetworks.map((network) => networks[network] || {});
|
|
493
|
+
const verifiedCount = targetStates.filter((state) => state.verifyStatus === "verified").length;
|
|
494
|
+
if (verifiedCount === targetNetworks.length && targetNetworks.length > 0) return "verified";
|
|
495
|
+
if (verifiedCount > 0) return "partiallyVerified";
|
|
496
|
+
|
|
497
|
+
const deployedCount = targetStates.filter((state) => state.deployStatus === "deployed").length;
|
|
498
|
+
if (deployedCount === targetNetworks.length && targetNetworks.length > 0) return "deployed";
|
|
499
|
+
if (deployedCount > 0) return "partiallyDeployed";
|
|
500
|
+
return fallback;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import type { WorkflowMode } from "./simulation";
|
|
3
|
+
import { validateExecutionId } from "./executionId";
|
|
4
|
+
|
|
5
|
+
export const WORKFLOW_RESULT_NAMES = ["execution", "view", "verification"] as const;
|
|
6
|
+
|
|
7
|
+
export type WorkflowResultName = typeof WORKFLOW_RESULT_NAMES[number];
|
|
8
|
+
|
|
9
|
+
interface WorkflowResultPathInput {
|
|
10
|
+
target: string;
|
|
11
|
+
executionId: string;
|
|
12
|
+
mode: WorkflowMode;
|
|
13
|
+
name: WorkflowResultName;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface WorkflowResultDirectoryInput {
|
|
17
|
+
target: string;
|
|
18
|
+
executionId: string;
|
|
19
|
+
mode: WorkflowMode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const SAFE_SEGMENT = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9_-])?$/;
|
|
23
|
+
|
|
24
|
+
const confinedSegment = (value: string, label: string): string => {
|
|
25
|
+
if (!SAFE_SEGMENT.test(value)) {
|
|
26
|
+
throw new Error(`${label} is not safe for a workflow result path`);
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const modeSegment = (mode: WorkflowMode): string => {
|
|
32
|
+
return mode === "simulate" ? "simulation" : mode;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const workflowResultRelativeDirectory = (
|
|
36
|
+
input: WorkflowResultDirectoryInput,
|
|
37
|
+
): string => {
|
|
38
|
+
return path.posix.join(
|
|
39
|
+
"results",
|
|
40
|
+
confinedSegment(input.target, "target"),
|
|
41
|
+
validateExecutionId(input.executionId),
|
|
42
|
+
modeSegment(input.mode),
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const workflowResultRelativePath = (input: WorkflowResultPathInput): string => {
|
|
47
|
+
const name = confinedSegment(input.name, "result name");
|
|
48
|
+
return path.posix.join(workflowResultRelativeDirectory(input), `${name}.json`);
|
|
49
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export interface RuntimeIdentity { version: string; digest: string }
|
|
4
|
+
export function bindExecutionRuntime(input: {taskDir: string; target: string; executionId: string; identity?: RuntimeIdentity}): void {
|
|
5
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(input.target) || !/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(input.executionId)) throw new Error('Invalid runtime binding identity');
|
|
6
|
+
const identity: RuntimeIdentity = input.identity ?? JSON.parse(fs.readFileSync(path.join(__dirname, '../../runtime-identity.json'), 'utf8'));
|
|
7
|
+
const dir = path.join(input.taskDir, 'runtime-bindings');
|
|
8
|
+
const file = path.join(dir, `${input.target}.${input.executionId}.json`);
|
|
9
|
+
const verify = () => {
|
|
10
|
+
const saved = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
11
|
+
if (saved.version !== identity.version || saved.digest !== identity.digest) throw new Error('Workflow runtime version mismatch: restore the pinned package or prepare a new execution; signed state cannot be silently migrated');
|
|
12
|
+
};
|
|
13
|
+
if (fs.existsSync(file)) { verify(); return; }
|
|
14
|
+
const results = path.join(input.taskDir, 'results');
|
|
15
|
+
const prefix = `${input.target}.${input.executionId}.`;
|
|
16
|
+
const legacyResults = fs.existsSync(path.join(results, input.target, input.executionId))
|
|
17
|
+
|| (fs.existsSync(results) && fs.readdirSync(results).some(name => name.startsWith(prefix)));
|
|
18
|
+
const lockPath = path.join(input.taskDir, 'parameters.lock.json');
|
|
19
|
+
let legacyLock = false;
|
|
20
|
+
if (fs.existsSync(lockPath)) {
|
|
21
|
+
const contains = (value: unknown): boolean => !!value && typeof value === 'object' && (Object.hasOwn(value, input.executionId) || Object.entries(value).some(([key, item]) => (key === 'executionId' && item === input.executionId) || contains(item)));
|
|
22
|
+
legacyLock = contains(JSON.parse(fs.readFileSync(lockPath, 'utf8')));
|
|
23
|
+
}
|
|
24
|
+
if (legacyResults || legacyLock) throw new Error('Legacy execution has no runtime binding: continue with its original scripts or prepare a new execution after reconciling completed transactions');
|
|
25
|
+
fs.mkdirSync(dir, {recursive:true});
|
|
26
|
+
try { fs.writeFileSync(file, JSON.stringify(identity, null, 2)+'\n', {flag:'wx'}); }
|
|
27
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error; verify(); }
|
|
28
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { getAddress, isAddress, ZeroAddress } from "ethers";
|
|
2
|
+
import type { Signer } from "ethers";
|
|
3
|
+
import {
|
|
4
|
+
createSigner,
|
|
5
|
+
createZkSyncNodeSigner,
|
|
6
|
+
createZkSyncSigner,
|
|
7
|
+
resolveWalletCredential,
|
|
8
|
+
} from "gate-evm-tools/custody";
|
|
9
|
+
import { ethers, network } from "../../projectHardhat";
|
|
10
|
+
import { assertExecutionSignerAddress } from "./signerIdentity";
|
|
11
|
+
import type { WorkflowMode } from "./simulation";
|
|
12
|
+
|
|
13
|
+
const SIMULATION_SIGNER_BALANCE = "0x3635C9ADC5DEA00000";
|
|
14
|
+
|
|
15
|
+
export interface WorkflowSignerOptions {
|
|
16
|
+
explicitAddress?: string;
|
|
17
|
+
taskAddress?: string;
|
|
18
|
+
useLock?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const normalizedSimulationAddress = (value: string | undefined): string | undefined => {
|
|
22
|
+
if (value === undefined) return undefined;
|
|
23
|
+
const candidate = value.trim();
|
|
24
|
+
if (!isAddress(candidate) || getAddress(candidate) === ZeroAddress) {
|
|
25
|
+
throw new Error("simulation signer must be a valid non-zero EVM address");
|
|
26
|
+
}
|
|
27
|
+
return getAddress(candidate);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const resolveSimulationSignerAddress = (options: WorkflowSignerOptions): string => {
|
|
31
|
+
const explicitAddress = normalizedSimulationAddress(options.explicitAddress);
|
|
32
|
+
const taskAddress = options.useLock
|
|
33
|
+
? undefined
|
|
34
|
+
: normalizedSimulationAddress(options.taskAddress);
|
|
35
|
+
if (
|
|
36
|
+
explicitAddress
|
|
37
|
+
&& taskAddress
|
|
38
|
+
&& explicitAddress.toLowerCase() !== taskAddress.toLowerCase()
|
|
39
|
+
) {
|
|
40
|
+
throw new Error("simulation signer sources do not match");
|
|
41
|
+
}
|
|
42
|
+
const selectedAddress = explicitAddress || taskAddress;
|
|
43
|
+
if (!selectedAddress) {
|
|
44
|
+
throw new Error("Simulation requires an explicit signer address");
|
|
45
|
+
}
|
|
46
|
+
return selectedAddress;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const impersonateWorkflowSimulationSigner = async (
|
|
50
|
+
signerAddress: string,
|
|
51
|
+
): Promise<Signer> => {
|
|
52
|
+
if (network.name !== "hardhat" && network.name !== "zkFork") {
|
|
53
|
+
throw new Error("Simulation signer impersonation requires a local fork network");
|
|
54
|
+
}
|
|
55
|
+
await network.provider.request({
|
|
56
|
+
method: "hardhat_impersonateAccount",
|
|
57
|
+
params: [signerAddress],
|
|
58
|
+
});
|
|
59
|
+
await network.provider.request({
|
|
60
|
+
method: "hardhat_setBalance",
|
|
61
|
+
params: [signerAddress, SIMULATION_SIGNER_BALANCE],
|
|
62
|
+
});
|
|
63
|
+
if (network.name === "zkFork") {
|
|
64
|
+
return createZkSyncNodeSigner({
|
|
65
|
+
provider: ethers.provider as never,
|
|
66
|
+
address: signerAddress,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return ethers.getSigner(signerAddress);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const resolveWorkflowSigner = async (
|
|
73
|
+
mode: WorkflowMode,
|
|
74
|
+
options: WorkflowSignerOptions = {},
|
|
75
|
+
): Promise<Signer> => {
|
|
76
|
+
if (mode === "simulate") {
|
|
77
|
+
return impersonateWorkflowSimulationSigner(resolveSimulationSignerAddress(options));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const executionCredential = mode === "execute"
|
|
81
|
+
? resolveWalletCredential(process.env)
|
|
82
|
+
: undefined;
|
|
83
|
+
const zkSync = (network.config as { zksync?: boolean }).zksync === true;
|
|
84
|
+
if (mode === "execute" && zkSync) {
|
|
85
|
+
if (!executionCredential) {
|
|
86
|
+
throw new Error("zkSync development execution requires an explicit wallet credential");
|
|
87
|
+
}
|
|
88
|
+
const baseUrl = process.env.CUSTODY_API_BASE_URL?.trim() || undefined;
|
|
89
|
+
if (executionCredential.kind === "custody" && !baseUrl) {
|
|
90
|
+
throw new Error("Custody wallet mode requires CUSTODY_API_BASE_URL");
|
|
91
|
+
}
|
|
92
|
+
const credential = executionCredential.kind === "private-key"
|
|
93
|
+
? executionCredential.privateKey
|
|
94
|
+
: `custody:${executionCredential.secret}:${executionCredential.address}`;
|
|
95
|
+
const signer = createZkSyncSigner({
|
|
96
|
+
ethers,
|
|
97
|
+
credential,
|
|
98
|
+
provider: ethers.provider as never,
|
|
99
|
+
baseUrl,
|
|
100
|
+
}) as Signer;
|
|
101
|
+
assertExecutionSignerAddress(await signer.getAddress(), options.taskAddress);
|
|
102
|
+
return signer;
|
|
103
|
+
}
|
|
104
|
+
if (executionCredential) {
|
|
105
|
+
const baseUrl = process.env.CUSTODY_API_BASE_URL?.trim() || undefined;
|
|
106
|
+
if (executionCredential.kind === "custody" && !baseUrl) {
|
|
107
|
+
throw new Error("Custody wallet mode requires CUSTODY_API_BASE_URL");
|
|
108
|
+
}
|
|
109
|
+
const credential = executionCredential.kind === "private-key"
|
|
110
|
+
? executionCredential.privateKey
|
|
111
|
+
: `custody:${executionCredential.secret}:${executionCredential.address}`;
|
|
112
|
+
const signer = createSigner({
|
|
113
|
+
ethers,
|
|
114
|
+
credential,
|
|
115
|
+
provider: ethers.provider,
|
|
116
|
+
baseUrl,
|
|
117
|
+
}) as Signer;
|
|
118
|
+
assertExecutionSignerAddress(await signer.getAddress(), options.taskAddress);
|
|
119
|
+
return signer;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const [signer] = await ethers.getSigners();
|
|
123
|
+
return signer;
|
|
124
|
+
};
|