gate-evm-tools 1.0.31 → 1.2.1
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 +34 -20
- package/bin/cli.js +3 -0
- package/bin/delivery.cjs +8 -7
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +26 -2
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/plan/index.d.ts.map +1 -1
- package/dist/commands/plan/index.js +1 -1
- package/dist/commands/plan/index.js.map +1 -1
- package/dist/core/preparation/batch.d.ts +26 -0
- package/dist/core/preparation/batch.d.ts.map +1 -0
- package/dist/core/preparation/batch.js +72 -0
- package/dist/core/preparation/batch.js.map +1 -0
- package/dist/core/preparation/evm.d.ts +23 -4
- package/dist/core/preparation/evm.d.ts.map +1 -1
- package/dist/core/preparation/evm.js +19 -3
- package/dist/core/preparation/evm.js.map +1 -1
- package/dist/core/preparation/evmManifestSync.d.ts +5 -0
- package/dist/core/preparation/evmManifestSync.d.ts.map +1 -1
- package/dist/core/preparation/evmManifestSync.js +15 -7
- package/dist/core/preparation/evmManifestSync.js.map +1 -1
- package/dist/core/preparation/index.d.ts +2 -2
- package/dist/core/preparation/index.d.ts.map +1 -1
- package/dist/core/preparation/manifest.d.ts +2 -0
- package/dist/core/preparation/manifest.d.ts.map +1 -1
- package/dist/core/preparation/manifest.js +2 -2
- package/dist/core/preparation/manifest.js.map +1 -1
- package/dist/core/preparation/normalize.d.ts.map +1 -1
- package/dist/core/preparation/normalize.js +53 -11
- package/dist/core/preparation/normalize.js.map +1 -1
- package/dist/core/preparation/transparent.d.ts +23 -0
- package/dist/core/preparation/transparent.d.ts.map +1 -0
- package/dist/core/preparation/transparent.js +103 -0
- package/dist/core/preparation/transparent.js.map +1 -0
- package/dist/core/preparation/types.d.ts +47 -5
- package/dist/core/preparation/types.d.ts.map +1 -1
- package/dist/core/preparation/zksync.d.ts.map +1 -1
- package/dist/core/preparation/zksync.js +2 -0
- package/dist/core/preparation/zksync.js.map +1 -1
- package/dist/custody/credential.d.ts.map +1 -1
- package/dist/custody/credential.js +1 -6
- package/dist/custody/credential.js.map +1 -1
- package/dist/deployment-evm/index.d.ts +4 -0
- package/dist/deployment-evm/index.d.ts.map +1 -0
- package/dist/deployment-evm/index.js +3 -0
- package/dist/deployment-evm/index.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +5 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/workflow/index.d.ts +4 -1
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +2 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist-cjs/core/preparation/batch.js +76 -0
- package/dist-cjs/core/preparation/batch.js.map +1 -0
- package/dist-cjs/core/preparation/evm.js +25 -7
- package/dist-cjs/core/preparation/evm.js.map +1 -1
- package/dist-cjs/core/preparation/evmManifestSync.js +15 -7
- package/dist-cjs/core/preparation/evmManifestSync.js.map +1 -1
- package/dist-cjs/core/preparation/manifest.js +2 -2
- package/dist-cjs/core/preparation/manifest.js.map +1 -1
- package/dist-cjs/core/preparation/normalize.js +53 -11
- package/dist-cjs/core/preparation/normalize.js.map +1 -1
- package/dist-cjs/core/preparation/transparent.js +110 -0
- package/dist-cjs/core/preparation/transparent.js.map +1 -0
- package/dist-cjs/core/preparation/zksync.js +2 -0
- package/dist-cjs/core/preparation/zksync.js.map +1 -1
- package/dist-cjs/custody/credential.js +1 -6
- package/dist-cjs/custody/credential.js.map +1 -1
- package/dist-cjs/deployment-evm/index.js +10 -0
- package/dist-cjs/deployment-evm/index.js.map +1 -0
- package/dist-cjs/sync/index.js +5 -0
- package/dist-cjs/sync/index.js.map +1 -1
- package/dist-cjs/workflow/index.js +11 -1
- package/dist-cjs/workflow/index.js.map +1 -1
- package/dist-runtime/config/hardhatAdapters.js +2 -1
- package/dist-runtime/config/hardhatAdapters.js.map +1 -1
- package/dist-runtime/delivery/aggregate-bootstrap.js +3 -1
- package/dist-runtime/delivery/aggregate-bootstrap.js.map +1 -1
- package/dist-runtime/delivery/aggregate-channel.js +7 -3
- package/dist-runtime/delivery/aggregate-channel.js.map +1 -1
- package/dist-runtime/delivery/aggregate-diagnostics.d.ts +14 -0
- package/dist-runtime/delivery/aggregate-diagnostics.js +73 -0
- package/dist-runtime/delivery/aggregate-diagnostics.js.map +1 -0
- package/dist-runtime/delivery/aggregate-inputs.d.ts +13 -9
- package/dist-runtime/delivery/aggregate-source.js +11 -3
- package/dist-runtime/delivery/aggregate-source.js.map +1 -1
- package/dist-runtime/delivery/aggregate-worker.js +12 -4
- package/dist-runtime/delivery/aggregate-worker.js.map +1 -1
- package/dist-runtime/delivery/aggregate.js +2 -2
- package/dist-runtime/delivery/aggregate.js.map +1 -1
- package/dist-runtime/delivery/business-extension.d.ts +10 -0
- package/dist-runtime/delivery/business-extension.js +38 -0
- package/dist-runtime/delivery/business-extension.js.map +1 -0
- package/dist-runtime/delivery/calldata-archive.d.ts +18 -0
- package/dist-runtime/delivery/calldata-archive.js +31 -0
- package/dist-runtime/delivery/calldata-archive.js.map +1 -0
- package/dist-runtime/delivery/calldata-details.d.ts +47 -0
- package/dist-runtime/delivery/calldata-details.js +94 -0
- package/dist-runtime/delivery/calldata-details.js.map +1 -0
- package/dist-runtime/delivery/calldata-format.d.ts +4 -0
- package/dist-runtime/delivery/calldata-format.js +17 -0
- package/dist-runtime/delivery/calldata-format.js.map +1 -0
- package/dist-runtime/delivery/calldata-preparations.d.ts +13 -0
- package/dist-runtime/delivery/calldata-preparations.js +85 -0
- package/dist-runtime/delivery/calldata-preparations.js.map +1 -0
- package/dist-runtime/delivery/calldata-review.d.ts +44 -0
- package/dist-runtime/delivery/calldata-review.js +131 -0
- package/dist-runtime/delivery/calldata-review.js.map +1 -0
- package/dist-runtime/delivery/codec-pin.d.ts +2 -2
- package/dist-runtime/delivery/codec-pin.js +4 -4
- package/dist-runtime/delivery/codec-pin.js.map +1 -1
- package/dist-runtime/delivery/compiler-files.js +3 -1
- package/dist-runtime/delivery/compiler-files.js.map +1 -1
- package/dist-runtime/delivery/compiler-pins.js +1 -1
- package/dist-runtime/delivery/compiler.js +17 -16
- package/dist-runtime/delivery/compiler.js.map +1 -1
- package/dist-runtime/delivery/config.d.ts +1 -1
- package/dist-runtime/delivery/config.js +27 -2
- package/dist-runtime/delivery/config.js.map +1 -1
- package/dist-runtime/delivery/configuration-definition.d.ts +9 -0
- package/dist-runtime/delivery/configuration-definition.js +33 -6
- package/dist-runtime/delivery/configuration-definition.js.map +1 -1
- package/dist-runtime/delivery/configuration.js +7 -4
- package/dist-runtime/delivery/configuration.js.map +1 -1
- package/dist-runtime/delivery/contract-source.d.ts +14 -0
- package/dist-runtime/delivery/contract-source.js +138 -0
- package/dist-runtime/delivery/contract-source.js.map +1 -0
- package/dist-runtime/delivery/custom-format.js +51 -24
- package/dist-runtime/delivery/custom-format.js.map +1 -1
- package/dist-runtime/delivery/development-inputs.d.ts +14 -0
- package/dist-runtime/delivery/development-inputs.js +76 -0
- package/dist-runtime/delivery/development-inputs.js.map +1 -0
- package/dist-runtime/delivery/development.d.ts +3 -1
- package/dist-runtime/delivery/development.js +24 -10
- package/dist-runtime/delivery/development.js.map +1 -1
- package/dist-runtime/delivery/eoa.d.ts +1 -0
- package/dist-runtime/delivery/execution-identity.d.ts +15 -0
- package/dist-runtime/delivery/execution-identity.js +33 -0
- package/dist-runtime/delivery/execution-identity.js.map +1 -0
- package/dist-runtime/delivery/existing-code.js +14 -6
- package/dist-runtime/delivery/existing-code.js.map +1 -1
- package/dist-runtime/delivery/history-package.d.ts +2 -0
- package/dist-runtime/delivery/history-package.js +70 -0
- package/dist-runtime/delivery/history-package.js.map +1 -0
- package/dist-runtime/delivery/history-registration.d.ts +1 -1
- package/dist-runtime/delivery/inputs.js +22 -15
- package/dist-runtime/delivery/inputs.js.map +1 -1
- package/dist-runtime/delivery/journal.js +18 -10
- package/dist-runtime/delivery/journal.js.map +1 -1
- package/dist-runtime/delivery/parameter-defaults.d.ts +10 -0
- package/dist-runtime/delivery/parameter-defaults.js +86 -0
- package/dist-runtime/delivery/parameter-defaults.js.map +1 -0
- package/dist-runtime/delivery/parameters.d.ts +2 -0
- package/dist-runtime/delivery/parameters.js +22 -0
- package/dist-runtime/delivery/parameters.js.map +1 -1
- package/dist-runtime/delivery/planning.d.ts +4 -1
- package/dist-runtime/delivery/planning.js +19 -3
- package/dist-runtime/delivery/planning.js.map +1 -1
- package/dist-runtime/delivery/preparation.js +43 -23
- package/dist-runtime/delivery/preparation.js.map +1 -1
- package/dist-runtime/delivery/production-environment.js +2 -1
- package/dist-runtime/delivery/production-environment.js.map +1 -1
- package/dist-runtime/delivery/production-finalization.js +23 -9
- package/dist-runtime/delivery/production-finalization.js.map +1 -1
- package/dist-runtime/delivery/production-intent.d.ts +2 -0
- package/dist-runtime/delivery/production-intent.js +16 -0
- package/dist-runtime/delivery/production-intent.js.map +1 -0
- package/dist-runtime/delivery/production.d.ts +129 -8
- package/dist-runtime/delivery/production.js +60 -11
- package/dist-runtime/delivery/production.js.map +1 -1
- package/dist-runtime/delivery/project-files.d.ts +3 -3
- package/dist-runtime/delivery/project-files.js +19 -14
- package/dist-runtime/delivery/project-files.js.map +1 -1
- package/dist-runtime/delivery/project-rpc.d.ts +19 -0
- package/dist-runtime/delivery/project-rpc.js +138 -0
- package/dist-runtime/delivery/project-rpc.js.map +1 -0
- package/dist-runtime/delivery/project.d.ts +3 -1
- package/dist-runtime/delivery/project.js +21 -8
- package/dist-runtime/delivery/project.js.map +1 -1
- package/dist-runtime/delivery/public-calldata.d.ts +25 -0
- package/dist-runtime/delivery/public-calldata.js +97 -0
- package/dist-runtime/delivery/public-calldata.js.map +1 -0
- package/dist-runtime/delivery/release-graph.d.ts +7 -3
- package/dist-runtime/delivery/release-graph.js +58 -25
- package/dist-runtime/delivery/release-graph.js.map +1 -1
- package/dist-runtime/delivery/release-history-capture.js +22 -26
- package/dist-runtime/delivery/release-history-capture.js.map +1 -1
- package/dist-runtime/delivery/release-history.d.ts +3 -2
- package/dist-runtime/delivery/release-history.js +97 -50
- package/dist-runtime/delivery/release-history.js.map +1 -1
- package/dist-runtime/delivery/release-inputs.d.ts +33 -0
- package/dist-runtime/delivery/release-inputs.js +129 -0
- package/dist-runtime/delivery/release-inputs.js.map +1 -0
- package/dist-runtime/delivery/release.d.ts +38 -14
- package/dist-runtime/delivery/release.js +81 -29
- package/dist-runtime/delivery/release.js.map +1 -1
- package/dist-runtime/delivery/safe.d.ts +1 -0
- package/dist-runtime/delivery/safe.js +13 -2
- package/dist-runtime/delivery/safe.js.map +1 -1
- package/dist-runtime/delivery/same-fork-baseline.d.ts +1 -1
- package/dist-runtime/delivery/same-fork-baseline.js +5 -4
- package/dist-runtime/delivery/same-fork-baseline.js.map +1 -1
- package/dist-runtime/delivery/setup.d.ts +1 -1
- package/dist-runtime/delivery/setup.js +21 -6
- package/dist-runtime/delivery/setup.js.map +1 -1
- package/dist-runtime/delivery/source.d.ts +1 -0
- package/dist-runtime/delivery/source.js +12 -10
- package/dist-runtime/delivery/source.js.map +1 -1
- package/dist-runtime/delivery/task-identity.d.ts +23 -0
- package/dist-runtime/delivery/task-identity.js +109 -0
- package/dist-runtime/delivery/task-identity.js.map +1 -0
- package/dist-runtime/delivery/toolchain.d.ts +3 -0
- package/dist-runtime/delivery/toolchain.js +52 -0
- package/dist-runtime/delivery/toolchain.js.map +1 -0
- package/dist-runtime/delivery/validation.js +7 -2
- package/dist-runtime/delivery/validation.js.map +1 -1
- package/dist-runtime/runtime-identity.json +2 -2
- package/dist-runtime/vendor/plan-codec/index.cjs +97 -20
- package/dist-runtime/vendor/plan-codec/index.d.cts +6 -0
- package/dist-runtime/vendor/plan-codec/provenance.json +9 -8
- package/dist-runtime/workflow/lib/calls.js +1 -0
- package/dist-runtime/workflow/lib/calls.js.map +1 -1
- package/dist-runtime/workflow/lib/checkCodeRunner.js +7 -4
- package/dist-runtime/workflow/lib/checkCodeRunner.js.map +1 -1
- package/dist-runtime/workflow/lib/context.d.ts +1 -0
- package/dist-runtime/workflow/lib/create2.d.ts +4 -26
- package/dist-runtime/workflow/lib/create2.js +2 -67
- package/dist-runtime/workflow/lib/create2.js.map +1 -1
- package/dist-runtime/workflow/lib/deploymentInfo.d.ts +2 -0
- package/dist-runtime/workflow/lib/deploymentInfo.js +6 -0
- package/dist-runtime/workflow/lib/deploymentInfo.js.map +1 -1
- package/dist-runtime/workflow/lib/deploymentRecords.d.ts +2 -0
- package/dist-runtime/workflow/lib/deploymentRecords.js +1 -1
- package/dist-runtime/workflow/lib/deploymentRecords.js.map +1 -1
- package/dist-runtime/workflow/lib/externalExecutionScanner.js +2 -1
- package/dist-runtime/workflow/lib/externalExecutionScanner.js.map +1 -1
- package/dist-runtime/workflow/lib/gateTool.js +2 -2
- package/dist-runtime/workflow/lib/gateTool.js.map +1 -1
- package/dist-runtime/workflow/lib/gateToolSync.d.ts +3 -0
- package/dist-runtime/workflow/lib/gateToolSync.js +5 -0
- package/dist-runtime/workflow/lib/gateToolSync.js.map +1 -1
- package/dist-runtime/workflow/lib/parameters.js +5 -0
- package/dist-runtime/workflow/lib/parameters.js.map +1 -1
- package/dist-runtime/workflow/lib/proxy.d.ts +1 -0
- package/dist-runtime/workflow/lib/proxy.js.map +1 -1
- package/dist-runtime/workflow/run.js +1 -3
- package/dist-runtime/workflow/run.js.map +1 -1
- package/dist-runtime/workflow/steps/deploy.js +53 -0
- package/dist-runtime/workflow/steps/deploy.js.map +1 -1
- package/dist-runtime/workflow/steps/upgrade.js +24 -10
- package/dist-runtime/workflow/steps/upgrade.js.map +1 -1
- package/docs/compiler-qualification.md +8 -2
- package/docs/delivery-aggregate.md +2 -0
- package/docs/delivery-calldata.md +27 -0
- package/docs/delivery-development.md +6 -0
- package/docs/delivery-execution-store.md +1 -1
- package/docs/delivery-inputs.md +55 -6
- package/docs/delivery-parameters.md +1 -1
- package/docs/delivery-release.md +77 -7
- package/docs/delivery-safe.md +7 -4
- package/docs/delivery-setup.md +72 -20
- package/docs/delivery-source.md +8 -8
- package/docs/direct-package-acceptance.md +2 -0
- package/docs/openzeppelin-acceptance.md +86 -0
- package/docs/openzeppelin-baseline.json +27 -0
- package/docs/openzeppelin-integration.md +115 -0
- package/docs/plans/2026-10-28-openzeppelin-toolkit-consolidation.md +396 -0
- package/docs/plans/2026-11-02-transparent-proxy-delivery.md +78 -0
- package/docs/superpowers/plans/2026-08-17-custody-hardhat-signers.md +1 -1
- package/docs/workflow-reference.md +12 -4
- package/docs/workflow-runtime.md +6 -0
- package/package.json +19 -5
- package/runtime/config/hardhatAdapters.ts +2 -1
- package/runtime/delivery/aggregate-bootstrap.ts +3 -1
- package/runtime/delivery/aggregate-channel.ts +6 -3
- package/runtime/delivery/aggregate-diagnostics.ts +63 -0
- package/runtime/delivery/aggregate-source.ts +6 -2
- package/runtime/delivery/aggregate-worker.ts +15 -5
- package/runtime/delivery/aggregate.ts +2 -2
- package/runtime/delivery/business-extension.ts +33 -0
- package/runtime/delivery/calldata-archive.ts +25 -0
- package/runtime/delivery/calldata-details.ts +90 -0
- package/runtime/delivery/calldata-format.ts +14 -0
- package/runtime/delivery/calldata-preparations.ts +76 -0
- package/runtime/delivery/calldata-review.ts +126 -0
- package/runtime/delivery/codec-pin.ts +3 -3
- package/runtime/delivery/compiler-files.ts +3 -1
- package/runtime/delivery/compiler-pins.ts +1 -1
- package/runtime/delivery/compiler.ts +18 -17
- package/runtime/delivery/config.ts +25 -2
- package/runtime/delivery/configuration-definition.ts +34 -7
- package/runtime/delivery/configuration.ts +8 -5
- package/runtime/delivery/contract-source.ts +112 -0
- package/runtime/delivery/custom-format.ts +20 -5
- package/runtime/delivery/development-inputs.ts +42 -0
- package/runtime/delivery/development.ts +14 -11
- package/runtime/delivery/execution-identity.ts +32 -0
- package/runtime/delivery/existing-code.ts +7 -6
- package/runtime/delivery/history-package.ts +50 -0
- package/runtime/delivery/inputs.ts +18 -11
- package/runtime/delivery/journal.ts +14 -10
- package/runtime/delivery/parameter-defaults.ts +75 -0
- package/runtime/delivery/parameters.ts +18 -0
- package/runtime/delivery/planning.ts +19 -5
- package/runtime/delivery/preparation.ts +17 -1
- package/runtime/delivery/production-environment.ts +2 -1
- package/runtime/delivery/production-finalization.ts +19 -10
- package/runtime/delivery/production-intent.ts +14 -0
- package/runtime/delivery/production.ts +51 -11
- package/runtime/delivery/project-files.ts +16 -11
- package/runtime/delivery/project-rpc.ts +116 -0
- package/runtime/delivery/project.ts +19 -7
- package/runtime/delivery/public-calldata.ts +74 -0
- package/runtime/delivery/release-graph.ts +44 -14
- package/runtime/delivery/release-history-capture.ts +19 -9
- package/runtime/delivery/release-history.ts +76 -22
- package/runtime/delivery/release-inputs.ts +107 -0
- package/runtime/delivery/release.ts +56 -18
- package/runtime/delivery/safe.ts +14 -3
- package/runtime/delivery/same-fork-baseline.ts +5 -4
- package/runtime/delivery/setup.ts +20 -8
- package/runtime/delivery/source.ts +10 -10
- package/runtime/delivery/task-identity.ts +85 -0
- package/runtime/delivery/toolchain.ts +48 -0
- package/runtime/delivery/validation.ts +7 -2
- package/runtime/vendor/plan-codec/index.cjs +97 -20
- package/runtime/vendor/plan-codec/index.d.cts +6 -0
- package/runtime/vendor/plan-codec/provenance.json +9 -8
- package/runtime/workflow/lib/calls.ts +1 -0
- package/runtime/workflow/lib/checkCodeRunner.ts +7 -3
- package/runtime/workflow/lib/context.ts +1 -0
- package/runtime/workflow/lib/create2.ts +7 -108
- package/runtime/workflow/lib/deploymentInfo.ts +8 -0
- package/runtime/workflow/lib/deploymentRecords.ts +3 -1
- package/runtime/workflow/lib/externalExecutionScanner.ts +2 -1
- package/runtime/workflow/lib/gateTool.ts +2 -2
- package/runtime/workflow/lib/gateToolSync.ts +8 -0
- package/runtime/workflow/lib/parameters.ts +4 -0
- package/runtime/workflow/lib/proxy.ts +1 -0
- package/runtime/workflow/run.ts +1 -2
- package/runtime/workflow/steps/deploy.ts +42 -0
- package/runtime/workflow/steps/upgrade.ts +19 -11
- package/bin/workflow.cjs +0 -19
|
@@ -6,8 +6,9 @@ import ts from 'typescript';
|
|
|
6
6
|
import {existingCodeCoverage,requiredExistingAddresses} from './existing-code';
|
|
7
7
|
import {yieldCompilerIo} from './compiler-io';
|
|
8
8
|
import {openDeliveryConfiguration} from './configuration';
|
|
9
|
+
import {aggregateInputHash} from './execution-identity';
|
|
9
10
|
import {getCreate2Address} from 'ethers';
|
|
10
|
-
import {artifactIdentity,BytecodeChecker,prepareEvmManifestCandidate,mergeEvmManifestCandidate,parseTargetManifest,hashStorageLayout,resolveTargetManifestPaths} from 'gate-evm-tools/workflow';
|
|
11
|
+
import {TRANSPARENT_PROXY_ARTIFACT,verifyTransparentAdmin,artifactIdentity,BytecodeChecker,prepareEvmManifestCandidate,mergeEvmManifestCandidate,parseTargetManifest,hashStorageLayout,resolveTargetManifestPaths} from 'gate-evm-tools/workflow';
|
|
11
12
|
import {canonicalData,canonicalJson,deepFreeze,semanticHash} from '../workflow/lib/canonicalData';
|
|
12
13
|
import {typedValue,parseTypedValue} from '../workflow/lib/typedValue';
|
|
13
14
|
import type {VerificationObservation,TypedValue} from '../workflow/lib/evidencePorts';
|
|
@@ -24,6 +25,7 @@ import {DeliveryOperationIdentities} from './identities';
|
|
|
24
25
|
import {parsePreparationJournal,type PreparationEntry} from './journal';
|
|
25
26
|
import type {DeliveryCollection} from './planning';
|
|
26
27
|
import type {DeliveryAggregate,DeliveryAggregateOptions} from './aggregate';
|
|
28
|
+
export {persistDevelopmentInputs} from './development-inputs';
|
|
27
29
|
import {openAggregateSources} from './aggregate-inputs';
|
|
28
30
|
import {loadDeliveryPublicTarget} from './config';
|
|
29
31
|
import {inputDigest,readInputBytes} from './input-files';
|
|
@@ -68,7 +70,7 @@ export function expectedDevelopmentAssertions(collection:Pick<DeliveryCollection
|
|
|
68
70
|
export function mergeDevelopmentRegistration(input:{
|
|
69
71
|
registry:unknown;metadata:unknown;base:{registry:ContractInfo;metadata:DeploymentInfo};
|
|
70
72
|
target:string;deploymentId:string;executionId:string;recordPath:string;updatedAt:string;
|
|
71
|
-
changes:readonly (DeploymentChange&{address:string;proxyKind?:'uups'})[];preparations:readonly PreparationEntry[];
|
|
73
|
+
changes:readonly (DeploymentChange&{address:string;proxyKind?:'uups'|'transparent'})[];preparations:readonly PreparationEntry[];
|
|
72
74
|
}):{registry:ContractInfo;metadata:DeploymentInfo} {
|
|
73
75
|
const {base,target}=input,registry=validateContractInfo(input.registry),metadata=validateDeploymentInfo(input.metadata);
|
|
74
76
|
registry[target]??={};metadata.networks[target]??={};
|
|
@@ -78,14 +80,14 @@ export function mergeDevelopmentRegistration(input:{
|
|
|
78
80
|
const previous=resolveDeployment(registry,target,c.instanceId);
|
|
79
81
|
if(previous&&previous.key!==c.contractInfoKey){
|
|
80
82
|
const entry=input.preparations.find(p=>p.stepId===c.stepId),request=entry?.prepared.request;
|
|
81
|
-
if(c.kind!=='upgrade'
|
|
83
|
+
if(c.kind!=='upgrade'||!['uups','transparent'].includes(c.proxyKind??'')||entry?.change.kind!=='upgrade'||!request||!(request.kind==='uups-upgrade'||request.kind==='transparent-upgrade')||entry.prepared.plan.kind!==request.kind
|
|
82
84
|
||entry.change.instanceId!==c.instanceId||entry.change.contractInfoKey!==c.contractInfoKey||entry.change.actualContract!==c.actualContract
|
|
83
85
|
||previous.address.toLowerCase()!==request.proxy.toLowerCase()||previous.address.toLowerCase()!==c.address.toLowerCase()||c.proxyAddress?.toLowerCase()!==request.proxy.toLowerCase())throw Error('Contract candidate key conflict');
|
|
84
86
|
delete registry[target][previous.key];
|
|
85
87
|
}
|
|
86
88
|
if(previous&&previous.address.toLowerCase()!==c.address.toLowerCase()&&c.previousAddress?.toLowerCase()!==previous.address.toLowerCase())throw Error('Contract candidate address conflict');
|
|
87
89
|
registry[target][c.contractInfoKey]=c.address;
|
|
88
|
-
metadata.networks[target][c.instanceId]={contractInfoKey:c.contractInfoKey,address:c.address,actualContract:c.actualContract,...(c.proxyKind?{proxyKind:c.proxyKind,implementationAddress:c.implementationAddress}:{}),source:{kind:'deployment',deploymentId:input.deploymentId,executionId:input.executionId,recordPath:input.recordPath},updatedAt:input.updatedAt};
|
|
90
|
+
metadata.networks[target][c.instanceId]={contractInfoKey:c.contractInfoKey,address:c.address,actualContract:c.actualContract,...(c.proxyKind?{proxyKind:c.proxyKind,implementationAddress:c.implementationAddress,...(c.proxyKind==='transparent'?{proxyAdminAddress:c.proxyAdminAddress,proxyAdminOwner:c.proxyAdminOwner}:{})}:{}),source:{kind:'deployment',deploymentId:input.deploymentId,executionId:input.executionId,recordPath:input.recordPath},updatedAt:input.updatedAt};
|
|
89
91
|
}
|
|
90
92
|
return {registry:validateContractInfo(registry),metadata:validateDeploymentInfo(metadata)};
|
|
91
93
|
}
|
|
@@ -107,7 +109,7 @@ export async function createDevelopmentDelivery(options:DevelopmentDeliveryOptio
|
|
|
107
109
|
same(preparations.map(p=>({stepId:p.stepId,preparationHash:p.preparationHash,transactionIds:p.mappings.map(m=>m.transactionId),derivedKeys:p.derivedKeys})),plan.deploymentBindings,'Sealed preparation bindings');
|
|
108
110
|
if(!plan.transactions.length)throw Error('Development acceptance requires actual transactions');
|
|
109
111
|
const rows=aggregate.inputs.targets;
|
|
110
|
-
const computed=
|
|
112
|
+
const computed=aggregateInputHash(aggregate);
|
|
111
113
|
same(plan.deploymentId,aggregate.inputs.business.deploymentId,'Business deployment identity');same(computed,aggregate.inputsHash,'Aggregate input hash');same(plan.inputsHash,computed,'Plan inputs');same(plan.sourceDefinitionHash,aggregate.sourceDefinitionHash,'Plan source');same(semanticHash(aggregate.inputs.sourceDefinitions),aggregate.sourceDefinitionHash,'Source hash');
|
|
112
114
|
const hre=options.hre,provider={request:(method:string,params:readonly unknown[])=>hre.network.provider.request({method,params:[...params]})};
|
|
113
115
|
if(options.execution.context.finality.kind==='confirmations'&&BigInt(options.execution.context.finality.depth)<BigInt(Math.max(1,target.finality.confirmations)))throw Error('Development finality below configured policy');
|
|
@@ -160,6 +162,7 @@ export async function createDevelopmentDelivery(options:DevelopmentDeliveryOptio
|
|
|
160
162
|
if(preparations.length)return;
|
|
161
163
|
const coverage=pureCoverage(),checker=new BytecodeChecker(hre,{},'evm');
|
|
162
164
|
for(const row of coverage.expected){const code=await hre.ethers.provider.getCode(row.address),local=await checker.getLocalBytecode(row.contract);if(code==='0x'||!checker.compareBytecode(code,local.bytecode,local.abi,local.immutableReferences).matches)throw Error('Existing development code mismatch');}
|
|
165
|
+
for(const alias of coverage.aliases){const m=base.metadata.networks[plan.target]?.[alias];if(m?.proxyKind==='transparent'){if(!m.proxyAdminAddress||!m.proxyAdminOwner)throw Error('Missing frozen existing Transparent admin');await verifyTransparentAdmin({hre:hre as any,proxy:m.address,admin:m.proxyAdminAddress,owner:m.proxyAdminOwner});}}
|
|
163
166
|
for(const m of coverage.manifests){if(byteHash(readInputBytes(root,m.file))!==m.hash)throw Error('Existing UUPS manifest changed');const storage=await provider.request('eth_getStorageAt',[m.proxy,'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc','latest']);if(typeof storage!=='string'||storage.slice(-40).toLowerCase()!==m.implementation.slice(2))throw Error('Existing UUPS implementation changed');}
|
|
164
167
|
}
|
|
165
168
|
await assertArtifacts();await assertExistingCode();
|
|
@@ -183,16 +186,16 @@ export async function createDevelopmentDelivery(options:DevelopmentDeliveryOptio
|
|
|
183
186
|
for(const deployment of p.deployments){
|
|
184
187
|
if(deployment.initCodeHash&&'factory' in r&&getCreate2Address(r.factory,deployment.salt,deployment.initCodeHash).toLowerCase()!==deployment.address.toLowerCase())throw Error('Prepared CREATE2 address mismatch');
|
|
185
188
|
const code=await hre.ethers.provider.getCode(deployment.address);if(code==='0x')throw Error('Prepared deployment has no actual code');
|
|
186
|
-
const name=deployment.role==='proxy'?'@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol:ERC1967Proxy':p.contract;
|
|
189
|
+
const name=deployment.role==='proxy'?(p.proxyKind==='transparent'?TRANSPARENT_PROXY_ARTIFACT:'@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol:ERC1967Proxy'):p.contract;
|
|
187
190
|
const local=await checker.getLocalBytecode(name),comparison=checker.compareBytecode(code,local.bytecode,local.abi,local.immutableReferences);if(!comparison.matches)throw Error(`Prepared deployed code mismatch: ${name}`);
|
|
188
191
|
}
|
|
189
192
|
const deployed=p.deployments.find(d=>d.role==='proxy')?.address??('proxy' in r?r.proxy:p.deployments.find(d=>d.role==='contract')?.address);
|
|
190
193
|
if(!deployed)throw Error('Prepared deployment address missing');
|
|
191
|
-
const change={...entry.change,address:deployed,...(p.proxyKind
|
|
194
|
+
const change={...entry.change,address:deployed,...(p.proxyKind?{proxyAddress:deployed,proxyKind:p.proxyKind}:{})};changes.set(change.instanceId,change);
|
|
192
195
|
}
|
|
193
|
-
for(const change of changes.values())if(change.proxyKind==='uups'){
|
|
196
|
+
for(const change of changes.values())if(change.proxyKind==='uups'||change.proxyKind==='transparent'){
|
|
194
197
|
const entry=[...preparations].reverse().find(p=>p.change.instanceId===change.instanceId)!;
|
|
195
|
-
const candidate=await prepareEvmManifestCandidate({root,scheme:'evm',network:plan.target,target:plan.target,proxy:change.address,contract:entry.prepared.plan.contract,constructorArgs:change.constructorArgs??[],skipTxHash:true,hre,compile:false});
|
|
198
|
+
const candidate=await prepareEvmManifestCandidate({root,scheme:'evm',network:plan.target,target:plan.target,proxy:change.address,proxyKind:change.proxyKind,proxyAdminAddress:change.proxyAdminAddress,proxyAdminOwner:change.proxyAdminOwner,contract:entry.prepared.plan.contract,constructorArgs:change.constructorArgs??[],skipTxHash:true,hre,compile:false});
|
|
196
199
|
const expectedImplementation=entry.prepared.plan.deployments.find(d=>d.role==='implementation')?.address??change.implementationAddress;
|
|
197
200
|
if(!expectedImplementation||candidate.implementation.toLowerCase()!==expectedImplementation.toLowerCase())throw Error('Final UUPS implementation mismatch');
|
|
198
201
|
same(candidate.artifact,entry.prepared.plan.artifact,'UUPS artifact');change.implementationAddress=candidate.implementation;candidates.push(candidate);
|
|
@@ -225,7 +228,7 @@ export async function createDevelopmentDelivery(options:DevelopmentDeliveryOptio
|
|
|
225
228
|
}
|
|
226
229
|
files.push(...manifests.values());return commitDevelopmentFilesLocked({root,executionId:plan.executionId,identity,files},options.onFileCommitted);
|
|
227
230
|
});
|
|
228
|
-
for(const candidate of state.candidates){const manifest=parseTargetManifest(fs.readFileSync(candidate.paths.file)),entry=manifest.impls[candidate.versionKey];if(!entry||![entry.address,...(entry.allAddresses??[])].some(a=>a.toLowerCase()===candidate.implementation)||hashStorageLayout(entry.layout)!==candidate.layoutHash||!manifest.proxies.some(p=>p.address.toLowerCase()===candidate.proxy&&p.kind===
|
|
231
|
+
for(const candidate of state.candidates){const manifest=parseTargetManifest(fs.readFileSync(candidate.paths.file)),entry=manifest.impls[candidate.versionKey];if(!entry||![entry.address,...(entry.allAddresses??[])].some(a=>a.toLowerCase()===candidate.implementation)||hashStorageLayout(entry.layout)!==candidate.layoutHash||!manifest.proxies.some(p=>p.address.toLowerCase()===candidate.proxy&&p.kind===candidate.proxyKind))throw Error('Committed target manifest mismatch');}
|
|
229
232
|
return {journal:result,changes:state.changes,manifests:!preparations.length?pureCoverage().manifests:state.candidates.map(c=>({file:path.relative(root,c.paths.file),hash:byteHash(fs.readFileSync(c.paths.file)),layoutHash:c.layoutHash,proxy:c.proxy,implementation:c.implementation}))};
|
|
230
233
|
}
|
|
231
234
|
return Object.freeze({plan,
|
|
@@ -242,7 +245,7 @@ export async function createDevelopmentDelivery(options:DevelopmentDeliveryOptio
|
|
|
242
245
|
const assertions=await verifyBusiness(),finalized=result.snapshot.transactions.map(t=>t.observation!);
|
|
243
246
|
const materialization=await materialize(finalized,assertions);
|
|
244
247
|
const registry=fileBytes(root,'contractInfo.json')!,metadata=fileBytes(root,'deploymentInfo.json')!,effective:Record<string,Record<string,string>>={[plan.target]:{}},expectedCode:ExpectedDevelopmentCode[]=[];
|
|
245
|
-
for(const c of materialization.changes){effective[plan.target][c.contractInfoKey]=c.address;const alias=c.contractInfoKey.includes(':')?c.contractInfoKey.split(':')[0]:c.contractInfoKey;expectedCode.push({key:alias,contract:c.actualContract,address:c.implementationAddress??c.address});if(c.proxyKind
|
|
248
|
+
for(const c of materialization.changes){effective[plan.target][c.contractInfoKey]=c.address;const alias=c.contractInfoKey.includes(':')?c.contractInfoKey.split(':')[0]:c.contractInfoKey;expectedCode.push({key:alias,contract:c.actualContract,address:c.implementationAddress??c.address});if(c.proxyKind)expectedCode.push({key:`${alias}_Proxy`,contract:c.proxyKind==='transparent'?'TransparentUpgradeableProxy':'ERC1967Proxy',address:c.address});if(c.proxyKind==='transparent')expectedCode.push({key:`${alias}_ProxyAdmin`,contract:'ProxyAdmin',address:c.proxyAdminAddress});}
|
|
246
249
|
if(!preparations.length){const coverage=pureCoverage();Object.assign(effective,coverage.effective);expectedCode.push(...coverage.expected);}
|
|
247
250
|
const acceptancePath=`.gate-delivery/development/${plan.executionId}.acceptance.json`,priorAcceptance=fileBytes(root,acceptancePath);
|
|
248
251
|
if(priorAcceptance!==null){
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Environment labels are evidence; actual source, dependency and compiler inputs remain binding. */
|
|
2
|
+
import type { RuntimeEvidence } from './configuration-runtime';
|
|
3
|
+
import { parseCustomInputClosure, type CustomInputClosure } from './closure-format';
|
|
4
|
+
import { semanticHash } from '../workflow/lib/canonicalData';
|
|
5
|
+
import type { DeliveryAggregate } from './aggregate';
|
|
6
|
+
|
|
7
|
+
export function runtimeExecutionInput(runtime: RuntimeEvidence): Omit<RuntimeEvidence, 'node'> {
|
|
8
|
+
const { node: _environment, ...inputs } = runtime;
|
|
9
|
+
return inputs;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function compilerExecutionInput<T extends { node: string; platform: string; arch: string; abi: string }>(toolchain: T): Omit<T, 'node' | 'platform' | 'arch' | 'abi'> {
|
|
13
|
+
const { node: _node, platform: _platform, arch: _arch, abi: _abi, ...inputs } = toolchain;
|
|
14
|
+
return inputs;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Only resolver version and its derived exact hash are omitted; all actual edges/emitted bytes stay. */
|
|
18
|
+
export function customExecutionInput(raw: CustomInputClosure | null): unknown {
|
|
19
|
+
if (raw === null) return null;
|
|
20
|
+
const { closureHash: _exactHash, resolver, ...inputs } = parseCustomInputClosure(raw);
|
|
21
|
+
const { version: _nodeVersion, ...policy } = resolver;
|
|
22
|
+
return { ...inputs, resolver: policy };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Collection, dev execution and historical validation share the same preimage. */
|
|
26
|
+
export function aggregateInputHash(a: Pick<DeliveryAggregate, 'inputs'>): string {
|
|
27
|
+
return semanticHash({ version: 1, business: a.inputs.business, sourceDefinitions: a.inputs.sourceDefinitions,
|
|
28
|
+
runtime: runtimeExecutionInput(a.inputs.runtime), files: a.inputs.files,
|
|
29
|
+
targets: a.inputs.targets.map(row => ({ target: row.target, configuration: row.configuration.configurationRuntimeHash,
|
|
30
|
+
sourceConfiguration: row.sourceConfiguration.configurationRuntimeHash, compiler: row.compiler.stableDigest,
|
|
31
|
+
custom: customExecutionInput(row.custom) })) });
|
|
32
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Pure-call coverage from the accepted registration and owned compiler witness. No deployments are invented. */
|
|
2
|
-
import {artifactIdentity,parseTargetManifest,hashStorageLayout} from 'gate-evm-tools/workflow';
|
|
2
|
+
import {TRANSPARENT_PROXY_ARTIFACT,PROXY_ADMIN_ARTIFACT,transparentAdminAddress,artifactIdentity,parseTargetManifest,hashStorageLayout} from 'gate-evm-tools/workflow';
|
|
3
3
|
import {canonicalJson,semanticHash} from '../workflow/lib/canonicalData';
|
|
4
4
|
import {validateContractInfo} from '../workflow/lib/deployments';
|
|
5
5
|
import {validateDeploymentInfo} from '../workflow/lib/deploymentInfo';
|
|
@@ -26,10 +26,11 @@ export function existingCodeCoverage(input:{target:string;addresses:string[];reg
|
|
|
26
26
|
const [key]=matches[0],alias=key.split(':')[0],m=metadata[alias],contract=m?.actualContract??key.split(':').at(-1)!;
|
|
27
27
|
if(m&&(m.contractInfoKey!==key||m.address.toLowerCase()!==address))throw Error('Existing code metadata registration mismatch');
|
|
28
28
|
aliases.push(alias);effective[input.target][key]=address;
|
|
29
|
-
if(m?.proxyKind&&m.proxyKind!=='uups')throw Error('Unsupported existing proxy kind');
|
|
30
|
-
if(m?.proxyKind==='uups'){
|
|
31
|
-
const implementation=m.implementationAddress!.toLowerCase();add(alias,contract,implementation);add(`${alias}_Proxy`,'ERC1967Proxy',address);
|
|
32
|
-
|
|
29
|
+
if(m?.proxyKind&&m.proxyKind!=='uups'&&m.proxyKind!=='transparent')throw Error('Unsupported existing proxy kind');
|
|
30
|
+
if(m?.proxyKind==='uups'||m?.proxyKind==='transparent'){
|
|
31
|
+
const implementation=m.implementationAddress!.toLowerCase();add(alias,contract,implementation);add(`${alias}_Proxy`,m.proxyKind==='transparent'?TRANSPARENT_PROXY_ARTIFACT:'ERC1967Proxy',address);
|
|
32
|
+
if(m.proxyKind==='transparent'){if(m.proxyAdminAddress?.toLowerCase()!==transparentAdminAddress(address)||!m.proxyAdminOwner)throw Error('Missing existing Transparent admin identity');add(`${alias}_ProxyAdmin`,PROXY_ADMIN_ARTIFACT,m.proxyAdminAddress);}
|
|
33
|
+
const owned=input.manifests.flatMap(file=>{const manifest=parseTargetManifest(Buffer.from(file.contents));const proxies=manifest.proxies.filter(p=>p.address.toLowerCase()===address),impls=Object.entries(manifest.impls).filter(([,v])=>v&&[v.address,...(v.allAddresses??[])].some(a=>a.toLowerCase()===implementation));return proxies.length===1&&proxies[0].kind===m.proxyKind&&impls.length===1?[{file:file.path,hash:byteHash(file.contents),layoutHash:hashStorageLayout(impls[0][1]!.layout),proxy:address,implementation}]:[];});
|
|
33
34
|
if(owned.length!==1)throw Error('Missing unique sealed existing UUPS manifest');manifests.push(owned[0]);
|
|
34
35
|
}else add(alias,contract,address);
|
|
35
36
|
}
|
|
@@ -39,6 +40,6 @@ export function existingCodeCoverage(input:{target:string;addresses:string[];reg
|
|
|
39
40
|
/** Called only by the owned worker while the actual compiler artifact reader is installed. */
|
|
40
41
|
export async function collectExistingCodeArtifacts(input:{target:string;addresses:string[];registry:unknown;metadata:unknown;readArtifact(name:string):Promise<unknown>}){
|
|
41
42
|
const registry=validateContractInfo(input.registry)[input.target]??{},metadata=validateDeploymentInfo(input.metadata).networks[input.target]??{},names=new Set<string>();
|
|
42
|
-
for(const address of new Set(input.addresses.map(a=>a.toLowerCase()))){const entries=Object.entries(registry).filter(([,a])=>a.toLowerCase()===address);if(entries.length!==1)throw Error('Existing code target requires unique frozen registration');const [key]=entries[0],m=metadata[key.split(':')[0]];names.add(m?.actualContract??key.split(':').at(-1)!);if(m?.proxyKind==='uups')names.add('@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol:ERC1967Proxy');}
|
|
43
|
+
for(const address of new Set(input.addresses.map(a=>a.toLowerCase()))){const entries=Object.entries(registry).filter(([,a])=>a.toLowerCase()===address);if(entries.length!==1)throw Error('Existing code target requires unique frozen registration');const [key]=entries[0],m=metadata[key.split(':')[0]];names.add(m?.actualContract??key.split(':').at(-1)!);if(m?.proxyKind==='uups'||m?.proxyKind==='transparent'){names.add(m.proxyKind==='transparent'?TRANSPARENT_PROXY_ARTIFACT:'@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol:ERC1967Proxy');if(m.proxyKind==='transparent')names.add(PROXY_ADMIN_ARTIFACT);}}
|
|
43
44
|
for(const name of names)await input.readArtifact(name);
|
|
44
45
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Verify retained producer bytes in memory. Historical code is never extracted or executed. */
|
|
2
|
+
import {createHash} from 'node:crypto';
|
|
3
|
+
import {gunzipSync} from 'node:zlib';
|
|
4
|
+
import {byteHash} from './materialization';
|
|
5
|
+
import {object,portablePath} from './protocol';
|
|
6
|
+
import {canonicalJson} from '../workflow/lib/canonicalData';
|
|
7
|
+
import type {DeliveryAggregate} from './aggregate';
|
|
8
|
+
|
|
9
|
+
export function readHistoricalPackage(archive:Buffer,archiveFile:string,aggregate:DeliveryAggregate):ReadonlyMap<string,Buffer> {
|
|
10
|
+
if(archiveFile!==`.gate-delivery/packages/${byteHash(archive)}.json.gz`)throw Error('Historical archive content address mismatch');
|
|
11
|
+
const snapshot=object(JSON.parse(gunzipSync(archive,{maxOutputLength:128*1024*1024}).toString()),'historical package snapshot',['schema','version','runtimeIdentity','observedPackage','files']);
|
|
12
|
+
if(snapshot.schema!=='gate-delivery-package-snapshot'||snapshot.version!==2||!Array.isArray(snapshot.files))throw Error('Invalid historical package snapshot archive');
|
|
13
|
+
const original=aggregate.inputs.runtime.package;
|
|
14
|
+
const same=(a:unknown,b:unknown,label:string)=>{if(canonicalJson(a)!==canonicalJson(b))throw Error(`${label} mismatch`);};
|
|
15
|
+
same(snapshot.observedPackage,original,'Historical snapshot provenance');
|
|
16
|
+
same(snapshot.runtimeIdentity,aggregate.inputs.runtime.declared,'Historical snapshot runtime');
|
|
17
|
+
const files=new Map<string,Buffer>();
|
|
18
|
+
for(const raw of snapshot.files){
|
|
19
|
+
const file=object(raw,'historical package member',['path','digest','base64']),relative=portablePath(file.path,'historical package member');
|
|
20
|
+
if(relative.split('/').includes('node_modules')||files.has(relative)||typeof file.base64!=='string')throw Error('Invalid historical package member');
|
|
21
|
+
const bytes=Buffer.from(file.base64,'base64');
|
|
22
|
+
if(bytes.toString('base64')!==file.base64||byteHash(bytes)!==file.digest)throw Error('Historical snapshot archive bytes mismatch');
|
|
23
|
+
files.set(relative,bytes);
|
|
24
|
+
}
|
|
25
|
+
const payload=[...files].map(([path,bytes])=>({path,digest:byteHash(bytes)}));
|
|
26
|
+
same(payload,original.payload,'Historical archive complete payload');
|
|
27
|
+
same(byteHash(payload.map(f=>`${f.path}\0${f.digest}\n`).join('')),original.payloadDigest,'Historical payload digest');
|
|
28
|
+
// Match the producer's sorted directory traversal, including file/directory prefix ordering.
|
|
29
|
+
const children=new Map<string,Set<string>>();
|
|
30
|
+
for(const relative of files.keys()){
|
|
31
|
+
const parts=relative.split('/');let directory='';
|
|
32
|
+
for(let i=0;i<parts.length;i++){
|
|
33
|
+
if(files.has(directory))throw Error('Historical package file/directory collision');
|
|
34
|
+
if(!children.has(directory))children.set(directory,new Set());
|
|
35
|
+
children.get(directory)!.add(parts[i]);directory=directory?`${directory}/${parts[i]}`:parts[i];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const digest=createHash('sha256');
|
|
39
|
+
const visit=(directory:string)=>{
|
|
40
|
+
const names=children.get(directory);if(!names)throw Error('Historical executable directory missing');
|
|
41
|
+
for(const name of [...names].sort()){
|
|
42
|
+
const relative=`${directory}/${name}`,bytes=files.get(relative);
|
|
43
|
+
if(bytes){if(/\.(?:c?js)$/.test(relative)){digest.update(relative);digest.update(bytes);}}
|
|
44
|
+
else visit(relative);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
for(const directory of ['dist-runtime','dist','dist-cjs','bin'])visit(directory);
|
|
48
|
+
same(digest.digest('hex'),aggregate.inputs.runtime.declared.digest,'Historical executable runtime digest');
|
|
49
|
+
return files;
|
|
50
|
+
}
|
|
@@ -11,6 +11,8 @@ import { resolveDeliveryParameters, consumeDeliveryParameters, type ResolvedDeli
|
|
|
11
11
|
import { validateDeploySaveAs, type WorkflowStep } from '../workflow/lib/parameters';
|
|
12
12
|
import { canonicalJson, deepFreeze, semanticHash, exactObject, nonempty } from '../workflow/lib/canonicalData';
|
|
13
13
|
import type { DeepReadonly } from './source';
|
|
14
|
+
import { parseDeploymentRequirementRef } from './task-identity';
|
|
15
|
+
import { resolveParameterTargets } from './parameter-defaults';
|
|
14
16
|
|
|
15
17
|
export interface DeliveryInputOptions {
|
|
16
18
|
workspaceRoot: string;
|
|
@@ -83,20 +85,29 @@ export function openDeliveryInputs(raw: DeliveryInputOptions): DeliveryInputHand
|
|
|
83
85
|
files.push({ path: relative, digest: inputDigest(bytes) });
|
|
84
86
|
return parseInputDocument(bytes, format);
|
|
85
87
|
};
|
|
86
|
-
const task =
|
|
88
|
+
const task = object(read('task.yaml', 'yaml'), 'task.yaml', ['version', 'id', 'developmentNetwork', 'releaseTargetNetworks', 'requirementRef', 'productionRequirementRef', 'referenceDeploymentId']);
|
|
87
89
|
if (task.version !== 1 || task.id !== deploymentId) throw new Error('Task version/deployment identity mismatch');
|
|
90
|
+
same(parseDeploymentRequirementRef(task.requirementRef), scope.requirementRef, 'Task requirementRef');
|
|
91
|
+
same(task.referenceDeploymentId ?? null, scope.referenceDeploymentId ?? null, 'Task referenceDeploymentId');
|
|
88
92
|
same(task.developmentNetwork, scope.developmentNetwork, 'Task developmentNetwork');
|
|
89
|
-
|
|
93
|
+
const release = task.releaseTargetNetworks as string[];
|
|
94
|
+
if (!Array.isArray(release) || new Set(release).size !== release.length) throw Error('Invalid task releaseTargetNetworks');
|
|
95
|
+
for (const network of release) if(typeof network !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(network) || ['__proto__','prototype','constructor'].includes(network)) throw Error('Invalid release target');
|
|
96
|
+
if (input.request.environment === 'dev') {
|
|
97
|
+
if (!(scope.releaseTargetNetworks as string[]).every(network => release.includes(network))) throw Error('Task removed a development-declared release target');
|
|
98
|
+
} else {
|
|
99
|
+
same(release, scope.releaseTargetNetworks, 'Task releaseTargetNetworks');
|
|
100
|
+
same(task.productionRequirementRef === undefined ? null : parseDeploymentRequirementRef(task.productionRequirementRef), scope.productionRequirementRef ?? null, 'Task productionRequirementRef');
|
|
101
|
+
}
|
|
90
102
|
const manifest = object(read('manifest.yaml', 'yaml'), 'manifest.yaml', ['id', 'description', 'steps']);
|
|
91
103
|
if (manifest.id !== deploymentId) throw new Error('Manifest deployment identity mismatch');
|
|
92
104
|
if (manifest.description !== undefined) nonempty(manifest.description, 'manifest description');
|
|
93
105
|
const steps = parseSteps(manifest.steps);
|
|
94
106
|
const schema = object(read('parameters.schema.json', 'json'), 'parameters schema');
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
const definitions = object(provenance.producers, 'producers'), observations = object(provenance.targets, 'provenance targets');
|
|
107
|
+
const dev = scope.developmentNetwork as string;
|
|
108
|
+
const all = [dev, ...release];
|
|
109
|
+
const { params, observations, definitions } = resolveParameterTargets(
|
|
110
|
+
read('parameters.yaml', 'yaml'), read('parameters.provenance.json', 'json'), all);
|
|
100
111
|
for (const [id, raw] of Object.entries(definitions)) {
|
|
101
112
|
safeId(id, 'producer ID');
|
|
102
113
|
const definition = exactObject(raw, ['version', 'module'], 'producer');
|
|
@@ -104,10 +115,6 @@ export function openDeliveryInputs(raw: DeliveryInputOptions): DeliveryInputHand
|
|
|
104
115
|
const module = portablePath(definition.module, 'producer module');
|
|
105
116
|
if (!module.startsWith(`${businessRoot}/`)) throw new Error('Producer module is outside deployment business root');
|
|
106
117
|
}
|
|
107
|
-
const dev = scope.developmentNetwork as string, release = scope.releaseTargetNetworks as string[];
|
|
108
|
-
const all = [dev, ...release];
|
|
109
|
-
for (const name of [...Object.keys(params), ...Object.keys(observations)]) if (!all.includes(name)) throw new Error('Unknown parameter target');
|
|
110
|
-
for (const value of Object.values(observations)) exactObject(value, ['manual', 'computed'], 'target provenance');
|
|
111
118
|
const selected = input.selection === 'development' ? [dev] : input.selection === 'release' ? release : all;
|
|
112
119
|
// Both the digest and every public projection use these exact buffers. Reopening
|
|
113
120
|
// for projection would permit an A→B→A edit to bind B's values to A's digest.
|
|
@@ -14,7 +14,7 @@ import type { DerivedBindings } from './bindings';
|
|
|
14
14
|
import { matchesSameForkInitial, sameForkBaselineDigest } from './same-fork-baseline';
|
|
15
15
|
export interface PreparationEntry extends PreparationInput { requestHash: string; gateToolPlanHash: string; mappings: PreparationMapping[]; preparationHash: string }
|
|
16
16
|
export interface JournalIdentity { target: string; chainId: string; expectedStepIds: readonly string[] }
|
|
17
|
-
const addressKeys = ['address', 'proxyAddress', 'implementationAddress', 'preparedImplementationAddress', 'placeholderImplementationAddress', 'factory', 'multicall', 'expectedAddress', 'predictedAddress', 'previousAddress'];
|
|
17
|
+
const addressKeys = ['address', 'proxyAddress', 'proxyAdminAddress', 'proxyAdminOwner', 'implementationAddress', 'preparedImplementationAddress', 'placeholderImplementationAddress', 'factory', 'multicall', 'expectedAddress', 'predictedAddress', 'previousAddress'];
|
|
18
18
|
const stringKeys = ['stepId', 'instanceId', 'actualContract', 'contractInfoKey', 'strategy', 'proxyKind', 'implementationSalt', 'salt', 'redeployReason'];
|
|
19
19
|
// Prepared changes cannot carry runtime reuse observations, receipts, candidate lifecycle, sync or success markers.
|
|
20
20
|
const changeKeys = [...addressKeys, ...stringKeys, 'kind', 'constructorArgs', 'leadingZeroBytes', 'gateTool'];
|
|
@@ -42,10 +42,10 @@ function normalizeInput(raw: unknown, identity: JournalIdentity): PreparationInp
|
|
|
42
42
|
if (!requestPath.endsWith(`/${stepId}-request.json`) || !planPath.endsWith(`/${stepId}-plan.json`)) throw new Error('Preparation step/ref mismatch');
|
|
43
43
|
if (request.scheme !== 'evm' || plan.scheme !== 'evm') throw new Error('Delivery preparation must use EVM');
|
|
44
44
|
if (plan.requestHash !== hashPreparationRequest(request) || plan.kind !== request.kind || plan.network !== request.network || plan.target !== request.target || plan.contract !== request.contract || plan.target !== identity.target || plan.chainId !== identity.chainId) throw new Error('Preparation request/plan identity mismatch');
|
|
45
|
-
if (change.stepId !== stepId || (change.kind === 'upgrade') !== (plan.kind === 'uups-upgrade') || change.actualContract !== plan.artifact.contractName || (plan.contract.includes(':') && plan.contract !== `${plan.artifact.sourceName}:${plan.artifact.contractName}`)) throw new Error('Preparation change/step/artifact identity mismatch');
|
|
45
|
+
if (change.stepId !== stepId || (change.kind === 'upgrade') !== (plan.kind === 'uups-upgrade' || plan.kind === 'transparent-upgrade') || change.actualContract !== plan.artifact.contractName || (plan.contract.includes(':') && plan.contract !== `${plan.artifact.sourceName}:${plan.artifact.contractName}`)) throw new Error('Preparation change/step/artifact identity mismatch');
|
|
46
46
|
if (canonicalJson(plan.transactions.map(t => t.purpose)) !== canonicalJson(expectedTransactionPurposes(plan))) throw new Error('Preparation transaction purposes/order mismatch');
|
|
47
47
|
if (!sameAddress(change.factory, request.factory) || change.contractInfoKey !== contractInfoKey(change.instanceId, change.actualContract)) throw new Error('Prepared factory/registry identity mismatch');
|
|
48
|
-
if (change.strategy !== (plan.kind === 'uups-upgrade' ? 'gate-tool' : 'create2')) throw new Error('Invalid prepared strategy');
|
|
48
|
+
if (change.strategy !== ((plan.kind === 'uups-upgrade' || plan.kind === 'transparent-upgrade') ? 'gate-tool' : 'create2')) throw new Error('Invalid prepared strategy');
|
|
49
49
|
const role = (name: string) => { const matches = plan.deployments.filter(d => d.role === name); if (matches.length !== 1) throw new Error(`Missing/duplicate preparation ${name}`); return matches[0]; };
|
|
50
50
|
const optionalAddress = (key: keyof DeploymentChange, expected: string) => {
|
|
51
51
|
if (Object.hasOwn(change, key) && !sameAddress(change[key], expected)) throw new Error(`Prepared ${key} does not match its canonical address`);
|
|
@@ -61,17 +61,21 @@ function normalizeInput(raw: unknown, identity: JournalIdentity): PreparationInp
|
|
|
61
61
|
if (Object.hasOwn(change, 'constructorArgs') && canonicalJson(change.constructorArgs) !== canonicalJson(request.constructorArgs)) throw new Error('Prepared constructorArgs differ from canonical request');
|
|
62
62
|
if (request.kind === 'create2-deploy') {
|
|
63
63
|
const deployment = role('contract');
|
|
64
|
-
rejectFields('proxyAddress', 'implementationAddress', 'preparedImplementationAddress', 'placeholderImplementationAddress', 'implementationSalt');
|
|
64
|
+
rejectFields('proxyAddress', 'implementationAddress', 'preparedImplementationAddress', 'placeholderImplementationAddress', 'implementationSalt', 'proxyAdminAddress', 'proxyAdminOwner');
|
|
65
65
|
if (change.proxyKind !== undefined || deployment.salt !== request.salt || change.salt !== request.salt || !sameAddress(change.predictedAddress, deployment.address)) throw new Error('Prepared CREATE2 request/deployment mismatch');
|
|
66
66
|
optionalAddress('address', deployment.address);
|
|
67
67
|
deploymentPolicy(request.addressPolicy, deployment.address);
|
|
68
68
|
} else {
|
|
69
69
|
const implementation = role('implementation');
|
|
70
|
-
if (change.proxyKind !==
|
|
70
|
+
if (change.proxyKind !== request.proxyKind || !sameAddress(change.implementationAddress, implementation.address) || implementation.salt !== request.implementationSalt) throw new Error('Prepared implementation mismatch');
|
|
71
71
|
optionalAddress('preparedImplementationAddress', implementation.address);
|
|
72
|
-
if
|
|
72
|
+
if(request.proxyKind==='transparent'){
|
|
73
|
+
if(!sameAddress(change.proxyAdminAddress,plan.proxyAdmin?.address)||!sameAddress(change.proxyAdminOwner,request.proxyAdminOwner)||!sameAddress(plan.proxyAdmin?.owner,request.proxyAdminOwner))throw Error('Prepared Transparent ProxyAdmin mismatch');
|
|
74
|
+
}else rejectFields('proxyAdminAddress','proxyAdminOwner');
|
|
75
|
+
if (request.kind === 'uups-placeholder-deploy' || request.kind === 'transparent-deploy') {
|
|
73
76
|
const proxy = role('proxy');
|
|
74
|
-
if (proxy.salt !== request.proxySalt || change.salt !== request.proxySalt || !sameAddress(change.predictedAddress, proxy.address) ||
|
|
77
|
+
if (proxy.salt !== request.proxySalt || change.salt !== request.proxySalt || !sameAddress(change.predictedAddress, proxy.address) || (request.kind==='uups-placeholder-deploy'&&!sameAddress(change.placeholderImplementationAddress, request.placeholderImplementation))) throw new Error('Prepared proxy mismatch');
|
|
78
|
+
if(request.kind==='transparent-deploy')rejectFields('placeholderImplementationAddress');
|
|
75
79
|
optionalAddress('address', proxy.address);
|
|
76
80
|
optionalAddress('proxyAddress', proxy.address);
|
|
77
81
|
deploymentPolicy(request.proxyAddressPolicy, proxy.address);
|
|
@@ -132,9 +136,9 @@ export function parsePreparationJournal(raw: unknown, transactions: readonly Rel
|
|
|
132
136
|
if (canonicalJson(entries.map(e => e.stepId)) !== canonicalJson(identity.expectedStepIds)) throw new Error('missing, extra or duplicate preparation steps');
|
|
133
137
|
for (const [index, entry] of entries.entries()) {
|
|
134
138
|
const {request,plan} = entry.prepared;
|
|
135
|
-
if (request.kind === 'uups-upgrade' && plan.validation.kind === 'uups-upgrade' && plan.validation.sameForkBaselineDigest !== undefined) {
|
|
139
|
+
if ((request.kind === 'uups-upgrade' || request.kind === 'transparent-upgrade') && (plan.validation.kind === 'uups-upgrade' || plan.validation.kind === 'transparent-upgrade') && plan.validation.sameForkBaselineDigest !== undefined) {
|
|
136
140
|
const validation = plan.validation;
|
|
137
|
-
const prior = entries.slice(0,index).filter(e => matchesSameForkInitial(e.prepared,request.target,plan.chainId,request.proxy,validation.currentImplementation));
|
|
141
|
+
const prior = entries.slice(0,index).filter(e => matchesSameForkInitial(e.prepared,request.target,plan.chainId,request.proxy,validation.currentImplementation,request.proxyKind));
|
|
138
142
|
if (prior.length !== 1 || sameForkBaselineDigest(prior[0].prepared,validation.oldLayoutHash) !== validation.sameForkBaselineDigest) throw new Error('Same-fork upgrade requires its exact prior initial preparation');
|
|
139
143
|
}
|
|
140
144
|
}
|
|
@@ -189,7 +193,7 @@ export class PreparationJournal implements PreparationSink {
|
|
|
189
193
|
assertUpgradeBaseline(proxy: string): boolean {
|
|
190
194
|
const matches = this.inputs.filter(i => matchesSameForkInitial(i.prepared,this.identity.target,this.identity.chainId,proxy));
|
|
191
195
|
if (!matches.length) return false;
|
|
192
|
-
if (matches.length !== 1 || !this.qualifiedSameFork?.(matches[0].prepared)) throw new Error('Same-fork
|
|
196
|
+
if (matches.length !== 1 || !this.qualifiedSameFork?.(matches[0].prepared)) throw new Error('Same-fork proxy upgrade requires a complete validated immutable OZ layout baseline; unsupported without that evidence');
|
|
193
197
|
return true;
|
|
194
198
|
}
|
|
195
199
|
seal(transactions: readonly ReleaseExecutionTransaction[], derived: DerivedBindings): PreparationEntry[] {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** Expand task-owned defaults before schema/provenance validation. Never a production override port. */
|
|
2
|
+
import { ownPath, pointerPart } from './data';
|
|
3
|
+
import { object } from './protocol';
|
|
4
|
+
import { canonicalJson, exactObject } from '../workflow/lib/canonicalData';
|
|
5
|
+
|
|
6
|
+
type Observations = { manual: Record<string, unknown>; computed: Record<string, unknown> };
|
|
7
|
+
const record = (value: unknown): value is Record<string, unknown> =>
|
|
8
|
+
value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
9
|
+
function observations(raw: unknown): Observations {
|
|
10
|
+
const value = exactObject(raw, ['manual', 'computed'], 'target provenance');
|
|
11
|
+
return { manual: object(value.manual, 'manual observations'), computed: object(value.computed, 'computed observations') };
|
|
12
|
+
}
|
|
13
|
+
function leaves(value: unknown, path = '', result = new Map<string, unknown>()): Map<string, unknown> {
|
|
14
|
+
const entries = value !== null && typeof value === 'object' ? Object.entries(value) : [];
|
|
15
|
+
if (!entries.length) result.set(path, value);
|
|
16
|
+
else for (const [key, child] of entries) leaves(child, `${path}/${pointerPart(key)}`, result);
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
function merge(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown> {
|
|
20
|
+
return Object.fromEntries([...new Set([...Object.keys(base), ...Object.keys(override)])].map(key => [key,
|
|
21
|
+
!Object.hasOwn(override, key) ? base[key]
|
|
22
|
+
: Object.hasOwn(base, key) && record(base[key]) && record(override[key]) ? merge(base[key], override[key])
|
|
23
|
+
: override[key],
|
|
24
|
+
]));
|
|
25
|
+
}
|
|
26
|
+
/** Only leaves actually inherited from defaults may inherit default observations. Arrays replace as a unit. */
|
|
27
|
+
function inheritedLeaves(base: unknown, override: unknown, path = '', result = new Set<string>()): Set<string> {
|
|
28
|
+
if (!record(base) || !record(override)) return result;
|
|
29
|
+
for (const [key, value] of Object.entries(base)) {
|
|
30
|
+
const child = `${path}/${pointerPart(key)}`;
|
|
31
|
+
if (!Object.hasOwn(override, key)) for (const pointer of leaves(value, child).keys()) result.add(pointer);
|
|
32
|
+
else if (record(value) && record(override[key])) inheritedLeaves(value, override[key], child, result);
|
|
33
|
+
}
|
|
34
|
+
// The empty root/container is itself a schema leaf.
|
|
35
|
+
if (!Object.keys(base).length && !Object.keys(override).length) result.add(path);
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function resolveParameterTargets(rawValues: unknown, rawProvenance: unknown, networks: readonly string[]): {
|
|
40
|
+
params: Record<string, unknown>; observations: Record<string, Observations>; definitions: Record<string, unknown>;
|
|
41
|
+
} {
|
|
42
|
+
const values = object(rawValues, 'parameters.yaml', ['defaults', 'params']);
|
|
43
|
+
const params = object(ownPath(values, ['params'], 'target parameters'), 'target parameters');
|
|
44
|
+
const provenance = object(rawProvenance, 'parameters provenance', ['version', 'producers', 'defaults', 'targets']);
|
|
45
|
+
if (provenance.version !== 1) throw new Error('Unsupported provenance version');
|
|
46
|
+
const definitions = object(provenance.producers, 'producers'), targets = object(provenance.targets, 'provenance targets');
|
|
47
|
+
for (const name of [...Object.keys(params), ...Object.keys(targets)])
|
|
48
|
+
if (!networks.includes(name)) throw new Error('Unknown parameter target');
|
|
49
|
+
const observed = Object.fromEntries(Object.entries(targets).map(([name, value]) => [name, observations(value)]));
|
|
50
|
+
if (!Object.hasOwn(values, 'defaults')) {
|
|
51
|
+
if (Object.hasOwn(provenance, 'defaults')) throw new Error('Default provenance requires parameter defaults');
|
|
52
|
+
return { params, observations: observed, definitions };
|
|
53
|
+
}
|
|
54
|
+
const defaults = object(values.defaults, 'parameter defaults');
|
|
55
|
+
const shared = Object.hasOwn(provenance, 'defaults') ? observations(provenance.defaults) : { manual: {}, computed: {} };
|
|
56
|
+
const defaultLeaves = leaves(defaults);
|
|
57
|
+
for (const kind of ['manual', 'computed'] as const) for (const [path, raw] of Object.entries(shared[kind])) {
|
|
58
|
+
if (!defaultLeaves.has(path)) throw new Error(`Unused default parameter observation at ${path}`);
|
|
59
|
+
const observation = exactObject(raw, kind === 'manual' ? ['source', 'value'] : ['producer', 'version', 'inputs', 'output'], 'default observation');
|
|
60
|
+
if (canonicalJson(observation[kind === 'manual' ? 'value' : 'output']) !== canonicalJson(defaultLeaves.get(path)))
|
|
61
|
+
throw new Error(`Default observation mismatch at ${path}`);
|
|
62
|
+
}
|
|
63
|
+
const effective = Object.fromEntries(networks.map(name => {
|
|
64
|
+
const override = Object.hasOwn(params, name) ? object(params[name], 'network parameter overrides') : {};
|
|
65
|
+
return [name, merge(defaults, override)];
|
|
66
|
+
}));
|
|
67
|
+
const effectiveObservations = Object.fromEntries(networks.map(name => {
|
|
68
|
+
const override = Object.hasOwn(params, name) ? params[name] : {};
|
|
69
|
+
const inherited = inheritedLeaves(defaults, override), local = Object.hasOwn(observed, name) ? observed[name] : { manual: {}, computed: {} };
|
|
70
|
+
return [name, Object.fromEntries((['manual', 'computed'] as const).map(kind => [kind, {
|
|
71
|
+
...Object.fromEntries(Object.entries(shared[kind]).filter(([path]) => inherited.has(path))), ...local[kind],
|
|
72
|
+
}])) as Observations];
|
|
73
|
+
}));
|
|
74
|
+
return { params: effective, observations: effectiveObservations, definitions };
|
|
75
|
+
}
|
|
@@ -324,6 +324,24 @@ export function resolveDeliveryParameters(
|
|
|
324
324
|
const body = { version: 1 as const, ...input, provenance };
|
|
325
325
|
return deepFreeze({ ...body, identity: semanticHash(body) });
|
|
326
326
|
}
|
|
327
|
+
/** Read-only migration check, never a producer receipt or production authority. */
|
|
328
|
+
export function validateHistoricalParameterExtension(previous: ResolvedDeliveryParameters, current: ParameterInputs): ResolvedDeliveryParameters {
|
|
329
|
+
consumeDeliveryParameters(previous, previous.identity);
|
|
330
|
+
const input = strictData({schema: current.schema, target: current.target, values: current.values,
|
|
331
|
+
sources: current.sources ?? {}, manual: current.manual ?? {}, computed: current.computed ?? {}});
|
|
332
|
+
if (input.target !== previous.target) throw Error('Historical parameter target changed');
|
|
333
|
+
// A historical consumer cannot rerun or grant authority to a newly introduced producer.
|
|
334
|
+
for (const [pointer, observation] of Object.entries(input.computed)) {
|
|
335
|
+
if (canonicalJson(observation) !== canonicalJson(previous.computed[pointer] ?? null)) throw Error('Historical computed parameter changed; new development acceptance required');
|
|
336
|
+
}
|
|
337
|
+
const provenance = attest(input);
|
|
338
|
+
for (const leaf of previous.provenance) {
|
|
339
|
+
const next = provenance.find(candidate => candidate.path === leaf.path);
|
|
340
|
+
if (!next || canonicalJson(next) !== canonicalJson(leaf)) throw Error(`Historical parameter value/provenance changed: ${leaf.path}`);
|
|
341
|
+
}
|
|
342
|
+
const body = {version: 1 as const, ...input, provenance};
|
|
343
|
+
return deepFreeze({...body, identity: semanticHash(body)});
|
|
344
|
+
}
|
|
327
345
|
/** Consume a previously trusted identity; no fill-in, override, or producer invocation. */
|
|
328
346
|
export function consumeDeliveryParameters(
|
|
329
347
|
raw: ResolvedDeliveryParameters,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { yieldCompilerIo } from './compiler-io';
|
|
2
|
+
import { createCalldataInvocation, reviewTransaction, parseCalldataReviews, type CalldataTransactionReview } from './calldata-review';
|
|
3
|
+
import { preparationCalldataReviews } from './calldata-preparations';
|
|
2
4
|
import {assertOwnedTargetWorker} from './aggregate-bootstrap';
|
|
3
5
|
import type { ReferenceObservation } from "./references";
|
|
4
6
|
import { createDeliveryValidation } from "./validation";
|
|
@@ -67,6 +69,7 @@ export interface DeliveryRuntimeEvidence {
|
|
|
67
69
|
}
|
|
68
70
|
export interface DeliveryCollection {
|
|
69
71
|
plan: DeliveryPlan;
|
|
72
|
+
calldata: CalldataTransactionReview[];
|
|
70
73
|
preparations: PreparationEntry[];
|
|
71
74
|
verification: VerificationObservation[];
|
|
72
75
|
runtimeEvidence: DeliveryRuntimeEvidence;
|
|
@@ -95,14 +98,14 @@ export interface DeliveryPlanningOptions {
|
|
|
95
98
|
identities: PlanningIdentityPort;
|
|
96
99
|
steps: readonly WorkflowStep[];
|
|
97
100
|
derivationSeed: string;
|
|
98
|
-
expected?: Pick<DeliveryCollection, "plan" | "preparations" | "verification"
|
|
101
|
+
expected?: Pick<DeliveryCollection, "plan" | "preparations" | "verification"> & Partial<Pick<DeliveryCollection, 'calldata'>>;
|
|
99
102
|
completedTransactions?: CompletedExecutionTransaction[];
|
|
100
103
|
customSession?: CustomPlanningInputSession;
|
|
101
104
|
}
|
|
102
105
|
export interface StagedDeliveryCollection {
|
|
103
106
|
readonly payload: Omit<DeliveryPlanBody,'inputsHash'>;
|
|
104
107
|
readonly invariantHash:string;
|
|
105
|
-
readonly snapshot: {payload:Omit<DeliveryPlanBody,'inputsHash'>;preparations:PreparationEntry[];verification:VerificationObservation[];runtimeEvidence:Omit<DeliveryRuntimeEvidence,'forkAudit'>&{forkAudit:Omit<ForkAuditEvidence,'planHash'|'evidenceHash'>}};
|
|
108
|
+
readonly snapshot: {payload:Omit<DeliveryPlanBody,'inputsHash'>;calldata:CalldataTransactionReview[];preparations:PreparationEntry[];verification:VerificationObservation[];runtimeEvidence:Omit<DeliveryRuntimeEvidence,'forkAudit'>&{forkAudit:Omit<ForkAuditEvidence,'planHash'|'evidenceHash'>}};
|
|
106
109
|
assertUnchanged(): Promise<void>;
|
|
107
110
|
bind(inputsHash:string): DeliveryCollection;
|
|
108
111
|
close():void;
|
|
@@ -224,6 +227,7 @@ async function collectOwned(input:DeliveryPlanningOptions,staging:boolean):Promi
|
|
|
224
227
|
});
|
|
225
228
|
const verification = new VerificationJournal([]);
|
|
226
229
|
const pendingObservations: DeliveryRuntimeEvidence["pendingUupsReuse"] = [];
|
|
230
|
+
const callReviews = new Map<string, CalldataTransactionReview>();
|
|
227
231
|
const operationIds = new DeliveryOperationIdentities();
|
|
228
232
|
const references: DeliveryRuntimeEvidence["references"] = [];
|
|
229
233
|
const customBindings: CustomEvidence[] = [],
|
|
@@ -249,6 +253,10 @@ async function collectOwned(input:DeliveryPlanningOptions,staging:boolean):Promi
|
|
|
249
253
|
bindings,
|
|
250
254
|
preparations,
|
|
251
255
|
verification,
|
|
256
|
+
calldataReview: { record(tx, intent) {
|
|
257
|
+
if (callReviews.has(tx.transactionId)) throw Error('Duplicate calldata ABI intent');
|
|
258
|
+
callReviews.set(tx.transactionId, reviewTransaction(tx, createCalldataInvocation({ ...intent, data: tx.data })));
|
|
259
|
+
} },
|
|
252
260
|
executionPlanner: planner,
|
|
253
261
|
transactionIdentity: operationIds,
|
|
254
262
|
transactionObserver: audit,
|
|
@@ -326,6 +334,12 @@ async function collectOwned(input:DeliveryPlanningOptions,staging:boolean):Promi
|
|
|
326
334
|
derivedBindings = bindings.seal();
|
|
327
335
|
const prepared = preparations.seal(transactions, derivedBindings),
|
|
328
336
|
observations = verification.seal();
|
|
337
|
+
for (const review of await preparationCalldataReviews(prepared, transactions, context.runtime.hre.artifacts)) {
|
|
338
|
+
if (callReviews.has(review.transactionId)) throw Error('Duplicate calldata preparation intent');
|
|
339
|
+
callReviews.set(review.transactionId, review);
|
|
340
|
+
}
|
|
341
|
+
const calldata = parseCalldataReviews(transactions.map(tx => callReviews.get(tx.transactionId)), transactions);
|
|
342
|
+
if (input.expected?.calldata && canonicalJson(parseCalldataReviews(input.expected.calldata, transactions)) !== canonicalJson(calldata)) throw Error('Replayed calldata ABI intent differs');
|
|
329
343
|
const {inputsHash:knownHash,...base}=header;
|
|
330
344
|
const payload = deepFreeze({
|
|
331
345
|
...base,
|
|
@@ -359,7 +373,7 @@ async function collectOwned(input:DeliveryPlanningOptions,staging:boolean):Promi
|
|
|
359
373
|
throw new Error("Replayed verification evidence differs");
|
|
360
374
|
session?.closeReads();
|
|
361
375
|
const interval=await audit.capture({transactions,completedTransactions:input.completedTransactions??[]});
|
|
362
|
-
const stagedSnapshot=deepFreeze({payload,preparations:prepared,verification:observations,runtimeEvidence:{pendingUupsReuse:pendingObservations,forkAudit:interval.snapshot,references}});
|
|
376
|
+
const stagedSnapshot=deepFreeze({payload,calldata,preparations:prepared,verification:observations,runtimeEvidence:{pendingUupsReuse:pendingObservations,forkAudit:interval.snapshot,references}});
|
|
363
377
|
const invariantHash=semanticHash(stagedSnapshot);
|
|
364
378
|
let state:'staged'|'bound'|'failed'|'closed'='staged';
|
|
365
379
|
const open=()=>{if(state!=='staged')throw new Error('Staged collection closed or already bound');};
|
|
@@ -368,7 +382,7 @@ async function collectOwned(input:DeliveryPlanningOptions,staging:boolean):Promi
|
|
|
368
382
|
const plan=sealDeliveryPlan({...payload,inputsHash});
|
|
369
383
|
if(expected&&plan.planHash!==expected.planHash)throw new Error('Replayed delivery plan differs from expected plan');
|
|
370
384
|
const forkAudit=interval.bind(plan.planHash);
|
|
371
|
-
const result=deepFreeze(canonicalData({plan,preparations:prepared,verification:observations,
|
|
385
|
+
const result=deepFreeze(canonicalData({plan,calldata,preparations:prepared,verification:observations,
|
|
372
386
|
runtimeEvidence:{pendingUupsReuse:pendingObservations,forkAudit,references}}) as unknown as DeliveryCollection);
|
|
373
387
|
if(collectionInvariantHash(result)!==invariantHash)throw new Error('Binding changed staged collection payload');
|
|
374
388
|
state='bound';return result;
|
|
@@ -393,5 +407,5 @@ async function collectOwned(input:DeliveryPlanningOptions,staging:boolean):Promi
|
|
|
393
407
|
export function collectionInvariantHash(collection:DeliveryCollection):string {
|
|
394
408
|
const {inputsHash:_,planHash:__,...payload}=collection.plan;
|
|
395
409
|
const {planHash:___,evidenceHash:____,...forkAudit}=collection.runtimeEvidence.forkAudit;
|
|
396
|
-
return semanticHash({payload,preparations:collection.preparations,verification:collection.verification,runtimeEvidence:{...collection.runtimeEvidence,forkAudit}});
|
|
410
|
+
return semanticHash({payload,calldata:collection.calldata,preparations:collection.preparations,verification:collection.verification,runtimeEvidence:{...collection.runtimeEvidence,forkAudit}});
|
|
397
411
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Interface, concat, getCreate2Address } from "ethers";
|
|
3
3
|
import {
|
|
4
4
|
artifactIdentity,
|
|
5
|
+
TRANSPARENT_PROXY_ARTIFACT, PROXY_ADMIN_ARTIFACT, PROXY_ADMIN_INTERFACE, transparentAdminAddress,
|
|
5
6
|
type GateToolArtifact,
|
|
6
7
|
type GateToolPreparationPlanV1,
|
|
7
8
|
type GateToolPreparationRequestV1,
|
|
@@ -173,10 +174,24 @@ export async function validatePreparationSemantics(
|
|
|
173
174
|
artifact,
|
|
174
175
|
request.constructorArgs,
|
|
175
176
|
request.implementationSalt,
|
|
176
|
-
request.kind === "uups-upgrade"
|
|
177
|
+
(request.kind === "uups-upgrade" || request.kind === "transparent-upgrade")
|
|
177
178
|
? request.implementationAddressPolicy
|
|
178
179
|
: undefined,
|
|
179
180
|
);
|
|
181
|
+
if(request.kind === 'transparent-deploy' || request.kind === 'transparent-upgrade'){
|
|
182
|
+
if(request.call)validateAbiArguments(artifact.abi,request.call.method,request.call.args);
|
|
183
|
+
const data=request.call?new Interface(artifact.abi).encodeFunctionData(request.call.method,request.call.args):'0x';
|
|
184
|
+
await read(PROXY_ADMIN_ARTIFACT);
|
|
185
|
+
const proxy=request.kind==='transparent-deploy'
|
|
186
|
+
?role('proxy',deliveryArtifact(await read(TRANSPARENT_PROXY_ARTIFACT)),[implementation,request.proxyAdminOwner,data],request.proxySalt,request.proxyAddressPolicy)
|
|
187
|
+
:request.proxy.toLowerCase();
|
|
188
|
+
const admin=transparentAdminAddress(proxy);
|
|
189
|
+
if(plan.proxyKind!=='transparent'||plan.proxyAdmin?.address!==admin||plan.proxyAdmin?.owner!==request.proxyAdminOwner.toLowerCase()
|
|
190
|
+
||(request.kind==='transparent-upgrade'&&request.proxyAdmin.toLowerCase()!==admin))throw Error('Actual Transparent admin identity differs from preparation');
|
|
191
|
+
if(request.kind==='transparent-upgrade')transactions.push({id:'upgrade-proxy',purpose:'upgrade-proxy',to:admin,value:'0',operation:0,
|
|
192
|
+
data:PROXY_ADMIN_INTERFACE.encodeFunctionData('upgradeAndCall',[proxy,implementation,data]).toLowerCase()});
|
|
193
|
+
if(request.kind==='transparent-deploy'&&deployments.find(d=>d.role==='proxy')?.status==='reused'&&deployments.some(d=>d.status==='required'))throw Error('Invalid actual Transparent reuse roles');
|
|
194
|
+
} else {
|
|
180
195
|
const proxy =
|
|
181
196
|
request.kind === "uups-placeholder-deploy"
|
|
182
197
|
? role(
|
|
@@ -215,6 +230,7 @@ export async function validatePreparationSemantics(
|
|
|
215
230
|
});
|
|
216
231
|
else if (deployments.some((d) => d.status === "required"))
|
|
217
232
|
throw new Error("Invalid actual UUPS reuse roles");
|
|
233
|
+
}
|
|
218
234
|
}
|
|
219
235
|
if (
|
|
220
236
|
canonicalJson(deployments) !== canonicalJson(plan.deployments) ||
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Remove signing authority, retaining independent model/RPC API configuration. */
|
|
2
2
|
export function productionChildEnvironment(input:NodeJS.ProcessEnv):NodeJS.ProcessEnv {
|
|
3
3
|
const out={...input};
|
|
4
|
-
|
|
4
|
+
// Also discard the retired alias if it remains in a caller's environment; it is never a signer input.
|
|
5
|
+
for(const key of Object.keys(out))if(/(^|_)(PRIVATE_KEY|WALLET_CREDENTIAL|MNEMONIC|SEED_PHRASE)$/.test(key)||key==='CUSTODY_API_BASE_URL'||/^GATEFLOW_(OPERATOR_TOKEN|PRODUCTION_|MAC_SECURE_SIGNER_)/.test(key)||/^GATE_(CUSTODY|SIGNER)_/.test(key))delete out[key];
|
|
5
6
|
return out;
|
|
6
7
|
}
|