diff --git a/.ban-nondeterminism-allowlist b/.ban-nondeterminism-allowlist index f4a001a6..bf26dbfa 100644 --- a/.ban-nondeterminism-allowlist +++ b/.ban-nondeterminism-allowlist @@ -13,6 +13,7 @@ std-fs crates/warp-core/src/wsc/view.rs native WSC file-open helper pending extr std-fs crates/warp-core/src/causal_wal_tests.rs WAL filesystem fixture I/O only. std-fs crates/warp-core/tests/causal_wal_hardening_tests.rs WAL filesystem fixture I/O only. std-fs crates/warp-core/tests/external_consumer_contract_fixture_tests.rs installed-contract restart WAL fixture I/O only. +std-fs crates/warp-core/tests/executable_operation_pipeline_tests.rs executable-operation restart WAL fixture I/O only. std-fs crates/warp-core/tests/provider_contract_admission_tests.rs provider invocation restart WAL fixture I/O only. std-fs crates/warp-core/tests/trusted_runtime_host_loop_tests.rs trusted runtime filesystem WAL fixture I/O only. std-env crates/warp-core/tests/wsc_store_tests.rs WSC filesystem fixture temp directory selection only. diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fec9b4a..29d70c52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,29 @@ ### Added +- `TrustedRuntimeHost` now has the first hook-free executable-operation runtime + slice. A runtime owner can admit exact canonical + `ExecutableOperationPackageV1` bytes under a separate package policy, install + their data-only `EchoOperationProgramV1`, independently admit an exact-basis + invocation under caller authority and delegated budget, evaluate privately, + and either commit one parent-visible patch or return typed noncommit evidence. + Only committed operation consequences enter the operation-tick WAL. The initial + generic program performs an anchored typed-node alpha-attachment + compare-and-set; it contains no application matcher, executor, footprint + callback, or prebuilt mutation plan. Package, installation, invocation, + evaluation, actual-footprint, budget, patch, result, basis, and terminal + identities are bound into a typed receipt. The parent patch and singleton + tick evidence name the admitted installation rather than promoting the + subordinate program digest into rule authority. Application-basis + corroboration is bounded by the delegated read budget. A runtime-control + installation record and distinct execution-kernel commit records retain the + full admitted installation and committed state delta under exact frame and + frontier shapes so a fresh host can re-admit and reconstruct them without + callbacks. A program digest alone cannot install, invoke, or authorize an + operation. This slice does not yet include Edict + compiler emission, a structurally separate target verifier, Jedit's rope + lawpack, `ReplaceRange`, scheduler batch composition, or an independently + implemented semantic oracle. - `TrustedRuntimeHost` can now admit a previously witnessed mutation for an installed Edict provider package with `admit_provider_contract_submission_v1(...)`. The shared installed-contract @@ -49,7 +72,7 @@ invoke callbacks, schedule or execute work, emit receipts, or grant runtime authority. The real-host witness extends the exact generator dependency closure, so generation evidence and the checked provider occurrence refresh - to `sha256:e0ccd4503c7f5830a1affa1c5a676f866aa0fab976a5ec2a0075c70916a64b69`; + to `sha256:ee870c75ec08c8818b3f80ab6562ae62a5cf741cd709edcee0085d951c5d5a7b`; the primary semantic and Target IR artifacts remain byte-identical. - `TrustedRuntimeHost` can now admit an opaque Edict provider proposal against an independently constructed `ProviderContractAdmissionPolicyV1`. The pure diff --git a/Cargo.lock b/Cargo.lock index 0b046f9e..4f971ff1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2207,6 +2207,7 @@ dependencies = [ "bytes", "echo-cas", "echo-dry-tests", + "echo-edict-canonical", "echo-registry-api", "echo-runtime-schema", "echo-wasm-abi", diff --git a/README.md b/README.md index 3e56415d..5eb36ada 100644 --- a/README.md +++ b/README.md @@ -103,20 +103,26 @@ model. ## How It Works -Echo currently implements two generated-contract paths. The first Edict -mutation closure now spans publication through provider-native Echo execution -and recovery. Wesley packaging and the generated bounded-read corridor remain -separate incomplete paths. +Echo currently implements two callback-shaped compatibility paths plus the +first generic hook-free executable-operation runtime slice. The Edict provider +mutation closure spans publication through provider-native Echo execution and +recovery, but ambient host callbacks still determine its application +semantics. Wesley packaging and the generated bounded-read corridor remain a +separate incomplete path. The Wesley compatibility path emits raw `RewriteRule` builders and generated helpers. Its integration fixture enables the policy-gated `native_rule_bootstrap` feature and registers those rules directly. It does not emit an `InstalledContractPackage` or exercise package verification. -The Edict provider path admits exact semantic-source, contract-pack, and -settings bytes; emits canonical semantic artifacts; runs a deterministic -lowerer and an independent verifier; and publishes a digest-locked provider -package plus a generated Rust helper projection. +Edict accepts exact semantic-source, contract-pack, and settings bytes. Its +compiler pipeline emits canonical semantic artifacts, runs a deterministic +lowerer and a structurally separate verifier path, and publishes a +digest-locked provider package plus a generated Rust helper projection. A +separate verifier path is not, by itself, an independently implemented +verifier. Echo later consumes the resulting proposal through runtime-owned +admission, corroboration, installation, invocation, receipt, and recovery +crossings. The helper performs pure, fail-closed preflight across exact package, Target IR, bundle, profile, schema, codec, obstruction, ABI, helper API, operation, and footprint claims. It @@ -166,6 +172,30 @@ ingress boundary, support provider-native reads, or turn package metadata into runtime authority. Echo separately retains the Wesley compatibility path for host-constructed `InstalledContractPackage` values. +Separately, Echo can now admit and install an exact canonical +`ExecutableOperationPackageV1`, admit an exact-basis invocation under explicit +authority and delegated budget, evaluate its data-only +`EchoOperationProgramV1` privately, and either commit one patch or return typed +noncommit evidence. Only committed operation consequences enter the +operation-tick WAL. The initial generic program performs an anchored typed-node +alpha-attachment compare-and-set. Its receipt binds the admitted package, +operation, subordinate program, invocation, complete evaluation basis, +authority, declared and actual footprints, budgets, patch, result, and terminal +outcome. Runtime-control installation and committed execution-kernel records +permit callback-free fresh-host recovery. Program bytes explicitly bind the +interpreter and intrinsic profiles, while the parent patch and singleton tick +evidence bind the admitted installation. A program digest cannot confer +operation identity, invocability, or authority. + +That generic runtime witness is not yet the Jim/Jedit vertical. No real Edict +compiler output, Jedit rope lawpack, or `ReplaceRange` operation uses it, and it +does not yet claim structurally separate target verification, scheduler batch +composition, or independently implemented semantic conformance. It also +temporarily reuses `TrustedRuntimeHost`'s joint `native_rule_bootstrap` and +`trusted_runtime` feature gate. The program itself has no native hooks, but the +host surface must be decoupled from the legacy bootstrap feature before a +product can remove that compatibility feature. + The following sequence is the existing Wesley bootstrap fixture: ```mermaid @@ -186,21 +216,49 @@ sequenceDiagram Echo-->>Fixture: OpticReading or typed obstruction ``` -The first Edict mutation branch of the package-shaped flow is now executable; -Wesley packaging and generated bounded reads are not yet complete: +These corridors are not one pipeline with optional source nouns. + +The current Wesley compatibility path is: + +```text +GraphQL source +-> generated RewriteRule builders and helpers +-> trusted-host direct native rule registration +-> canonical intent +-> Echo admission, scheduling, and receipt +``` + +The current Edict provider-v1 compatibility path is: ```text -admitted Wesley or Edict source --> verified mutation Target IR or lawful read semantics --> generated handlers, observers, codecs, footprints, and package metadata +Edict source +-> canonical Core meaning and verified Echo Target IR +-> digest-locked provider package and generated helper -> opaque provider proposal with explicit host binding -> trusted-host exact proposal-claim admission -> exact package corroboration and provider-native installation -> exact EINT-kind and installed-operation admission --> scheduler-owned execution +-> scheduler-owned callback execution -> receipts and WAL recovery bound to package, operation, Target IR, and rule ``` +The current executable-operation runtime slice is: + +```text +canonical ExecutableOperationPackageV1 bytes +-> Echo-owned package and invocation admission +-> installed data-only EchoOperationProgramV1 +-> bounded private Echo evaluation +-> exact-basis singleton commit +-> typed receipt, WAL retention, and callback-free recovery +``` + +The first two paths are callback-shaped compatibility infrastructure. The +third proves Echo-owned execution of admitted data-only meaning, but no real +Edict compiler output or Jedit operation uses it yet. The next convergence +crossing must bind a real Jedit-owned Edict operation and lawpack to that +executable-operation package without reintroducing a native implementation. + ## Contracts And Boundaries Echo core is intentionally generic. Application nouns belong in authored @@ -218,9 +276,15 @@ contracts and generated adapters, not in the runtime kernel. application SDK. - Echo's package registry and scheduler path is implemented independently of both compiler publication paths. -- A trusted Echo host must verify compiler-emitted material, bind the - host-supplied executor or observer, and install it through the generic - package boundary. A compiler must not create a second execution engine. +- Provider v1 binds host-supplied executors as explicitly transitional + compatibility evidence; matching identities do not prove callback + semantics. +- For newly authored executable operations, a trusted Echo runtime verifies + and installs admitted package material, then Echo interprets the bound + data-only program. The host supplies no application matcher, executor, or + footprint callback. +- A compiler must not create a second execution engine, and a host must not + substitute ambient application behavior for admitted executable meaning. See [Generated Rule Authorship](docs/topics/GeneratedRules.md) for the exact current/target boundary, including the separate Wesley compatibility and Edict @@ -238,8 +302,8 @@ type Mutation { - **Runtime-owned time**: application code cannot tick Echo or choose scheduler boundaries. -- **Deterministic execution**: ticks, admission, handler dispatch, and - settlement are scheduler-owned. +- **Deterministic runtime judgments**: Echo owns admission, scheduling, tick + formation, rule selection, settlement, and evidence construction. - **Evidence-first observations**: readings carry basis, observer, witness, budget, rights, and residual posture. - **Replayable history**: submissions, receipts, witnesses, and retained @@ -261,9 +325,18 @@ canonical evidence before the scheduler can act on it: work; - the scheduler drains eligible work in deterministic order under explicit conflict and footprint rules; -- handlers run only during scheduler-owned ticks; +- admitted data-only operation programs are interpreted by Echo; legacy + compatibility callbacks run only during scheduler-owned ticks but do not, by + callback binding alone, prove deterministic application consequences; - every committed tick emits receipt evidence that can be replayed and checked. +Echo deterministically controls its runtime judgments and evidence. End-to-end +deterministic consequences additionally require every executed semantic +implementation to satisfy its declared execution contract. The +executable-operation corridor makes that requirement structural by deriving +the consequence from admitted data-only program bytes; the callback-shaped +compatibility corridors do not yet provide the same proof. + `dispatch_intent(...)` is ingress. It is not "run this now." A host may run Echo on a fixed wall-clock cadence or in an until-idle loop, but that cadence is trusted runtime policy. The semantic tick is a logical scheduler-owned diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json index af69c579..b57a41cc 100644 --- a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json @@ -1 +1 @@ -{"apiVersion":"wesley.generation-provenance-manifest/v1","contractVersions":{"generatorAbi":"wesley.extension-generator/v1","inputSchema":"wesley.extension-generation-input/v1","provenanceSchema":"wesley.generation-provenance-manifest/v1"},"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac","version":"0.1.0"},"settingsDigest":"sha256:0f708e76898de6fdb8186352e81d0f5c445adf94bb6c7de9204952d9fe913d4a","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file +{"apiVersion":"wesley.generation-provenance-manifest/v1","contractVersions":{"generatorAbi":"wesley.extension-generator/v1","inputSchema":"wesley.extension-generation-input/v1","provenanceSchema":"wesley.generation-provenance-manifest/v1"},"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a","version":"0.1.0"},"settingsDigest":"sha256:0f708e76898de6fdb8186352e81d0f5c445adf94bb6c7de9204952d9fe913d4a","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/review.provider-generation.json b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/review.provider-generation.json index 12997f41..bdbbe353 100644 --- a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/review.provider-generation.json +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/generated/evidence/review.provider-generation.json @@ -1 +1 @@ -{"apiVersion":"wesley.generation-review/v1","authoritative":false,"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac","version":"0.1.0"},"projectionRoles":["authority-facts.echo-dpo","authority-facts.echo-lawpack","generated-artifact-profile.echo-dpo-registration","lawpack.echo-dpo","schema.echo-provider-artifacts","target-profile.echo-dpo"],"provenanceManifestDigest":"sha256:ce67a27c13a0f830f99abe4cabaf519b519c3fb5904d198dd9550d1b349d276e","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file +{"apiVersion":"wesley.generation-review/v1","authoritative":false,"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a","version":"0.1.0"},"projectionRoles":["authority-facts.echo-dpo","authority-facts.echo-lawpack","generated-artifact-profile.echo-dpo-registration","lawpack.echo-dpo","schema.echo-provider-artifacts","target-profile.echo-dpo"],"provenanceManifestDigest":"sha256:ddb18917c074ed929938ee4e231ff2b9382fb0acf132196e57f35a67fe7c1993","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/provider-manifest.echo.json b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/provider-manifest.echo.json index a6098a92..a6e6d320 100644 --- a/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/provider-manifest.echo.json +++ b/crates/echo-wesley-gen/assets/v1/edict-provider/package/v1/provider-manifest.echo.json @@ -3,7 +3,7 @@ "providerAbi": "edict:target-provider@1.0.0", "provider": { "coordinate": "echo.edict-provider@1", - "digest": "sha256:e0ccd4503c7f5830a1affa1c5a676f866aa0fab976a5ec2a0075c70916a64b69" + "digest": "sha256:ee870c75ec08c8818b3f80ab6562ae62a5cf741cd709edcee0085d951c5d5a7b" }, "artifacts": [ { @@ -21,7 +21,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -40,7 +40,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -59,7 +59,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -78,7 +78,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -102,7 +102,7 @@ "artifactKind": "generationProvenance", "resource": { "coordinate": "echo.edict-provider-generation-provenance@1", - "digest": "sha256:2c6b23e85be5f3c36447135bf8c3fb9b24cdef2426f0e5d84889f6cc11f45228" + "digest": "sha256:509131f36dab0243077b70bb4f66c3d9a3eab0e9c6501b26f53d199f75483c56" }, "source": { "kind": "generated", @@ -112,7 +112,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -121,7 +121,7 @@ "artifactKind": "reviewArtifact", "resource": { "coordinate": "echo.edict-provider-generation-review@1", - "digest": "sha256:9748d55ab83e43e64fb37e48682a4ce4c104ed1dce969089335d53bb11a321ae" + "digest": "sha256:7db063768bdb03f8ee0e2a754c15ac0d25f0cd864d8351381e9fc40c6d6be10b" }, "source": { "kind": "generated", @@ -131,7 +131,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -150,7 +150,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -169,7 +169,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, diff --git a/crates/echo-wesley-gen/assets/v1/repository/Cargo.lock.source b/crates/echo-wesley-gen/assets/v1/repository/Cargo.lock.source index 0b046f9e..4f971ff1 100644 --- a/crates/echo-wesley-gen/assets/v1/repository/Cargo.lock.source +++ b/crates/echo-wesley-gen/assets/v1/repository/Cargo.lock.source @@ -2207,6 +2207,7 @@ dependencies = [ "bytes", "echo-cas", "echo-dry-tests", + "echo-edict-canonical", "echo-registry-api", "echo-runtime-schema", "echo-wasm-abi", diff --git a/crates/echo-wesley-gen/tests/provider_package.rs b/crates/echo-wesley-gen/tests/provider_package.rs index 1c266e40..6dae83cf 100644 --- a/crates/echo-wesley-gen/tests/provider_package.rs +++ b/crates/echo-wesley-gen/tests/provider_package.rs @@ -65,7 +65,7 @@ const SEMANTIC_DIGEST: &str = const RELEASE_DIGEST: &str = "sha256:c39449495281b51f978468d08c21e93bcfa423176063b41675da61e4674b0066"; const PACKAGE_ARTIFACT_SHA256: &str = - "e0ccd4503c7f5830a1affa1c5a676f866aa0fab976a5ec2a0075c70916a64b69"; + "ee870c75ec08c8818b3f80ab6562ae62a5cf741cd709edcee0085d951c5d5a7b"; const OTHER_PACKAGE_ARTIFACT_SHA256: &str = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; diff --git a/crates/echo-wesley-gen/tests/provider_package_corpus.rs b/crates/echo-wesley-gen/tests/provider_package_corpus.rs index 4aec09f9..a3c47d9f 100644 --- a/crates/echo-wesley-gen/tests/provider_package_corpus.rs +++ b/crates/echo-wesley-gen/tests/provider_package_corpus.rs @@ -11,9 +11,9 @@ use std::sync::atomic::{AtomicU64, Ordering}; use sha2::{Digest as _, Sha256}; const EXPECTED_PROVIDER_DIGEST: &str = - "sha256:e0ccd4503c7f5830a1affa1c5a676f866aa0fab976a5ec2a0075c70916a64b69"; + "sha256:ee870c75ec08c8818b3f80ab6562ae62a5cf741cd709edcee0085d951c5d5a7b"; const EXPECTED_MANIFEST_RAW_SHA256: &str = - "fe5d70f5581247c29569924679a5fb3fdb46e9b81f252b42488aecda0c61b9fb"; + "0c026e0cf9f04a2be3765c37a657866d573047966210b04e824911586cc21693"; const PACKAGE_PATHS: [&str; 25] = [ "components/lowerer.echo-dpo.component.wasm", diff --git a/crates/warp-core/Cargo.toml b/crates/warp-core/Cargo.toml index 31ccb637..7504ab5b 100644 --- a/crates/warp-core/Cargo.toml +++ b/crates/warp-core/Cargo.toml @@ -23,6 +23,7 @@ rustc-hash = "2.1.1" echo-wasm-abi = { workspace = true } echo-runtime-schema = { workspace = true } echo-registry-api = { workspace = true } +echo-edict-canonical = { workspace = true } warp-math = { workspace = true } [dev-dependencies] @@ -92,6 +93,10 @@ required-features = ["native_rule_bootstrap", "host_test"] name = "provider_contract_admission_tests" required-features = ["native_rule_bootstrap", "trusted_runtime"] +[[test]] +name = "executable_operation_pipeline_tests" +required-features = ["native_rule_bootstrap", "trusted_runtime"] + [build-dependencies] blake3 = "1.0" diff --git a/crates/warp-core/src/causal_wal.rs b/crates/warp-core/src/causal_wal.rs index 9f709e55..b6399c3c 100644 --- a/crates/warp-core/src/causal_wal.rs +++ b/crates/warp-core/src/causal_wal.rs @@ -305,6 +305,8 @@ pub enum WalAppendAuthority { RuntimeControl, /// Echo admission-kernel authority. AdmissionKernel, + /// Echo executable-operation interpreter and commit authority. + ExecutionKernel, /// Recovery authority. Recovery, } @@ -326,6 +328,10 @@ pub enum WalTransactionKind { TopologyIntent, /// Echo-owned causal-anchor admission. CausalAnchorAdmission, + /// Runtime-owner installation of admitted executable operation meaning. + ExecutableOperationInstallation, + /// Execution-kernel-owned commit of one executable operation consequence. + ExecutableOperationTick, } impl WalTransactionKind { @@ -340,6 +346,8 @@ impl WalTransactionKind { Self::MaterializationOutbox => 5, Self::TopologyIntent => 6, Self::CausalAnchorAdmission => 7, + Self::ExecutableOperationInstallation => 8, + Self::ExecutableOperationTick => 9, } } @@ -353,8 +361,11 @@ impl WalTransactionKind { Self::SchedulerTick | Self::MaterializationOutbox | Self::TopologyIntent => { WalAppendAuthority::TrustedScheduler } - Self::RuntimePosture => WalAppendAuthority::RuntimeControl, + Self::RuntimePosture | Self::ExecutableOperationInstallation => { + WalAppendAuthority::RuntimeControl + } Self::CausalAnchorAdmission => WalAppendAuthority::AdmissionKernel, + Self::ExecutableOperationTick => WalAppendAuthority::ExecutionKernel, Self::Checkpoint => WalAppendAuthority::Recovery, } } @@ -368,6 +379,8 @@ impl WalTransactionKind { 5 => Ok(Self::MaterializationOutbox), 6 => Ok(Self::TopologyIntent), 7 => Ok(Self::CausalAnchorAdmission), + 8 => Ok(Self::ExecutableOperationInstallation), + 9 => Ok(Self::ExecutableOperationTick), _ => Err(WalDecodeError::UnknownEnumCode { enum_name: "WalTransactionKind", code, @@ -427,6 +440,12 @@ pub enum WalRecordKind { CausalAnchorFactRecorded, /// Echo admission kernel recorded the matching causal-anchor receipt. CausalAnchorAdmissionReceiptRecorded, + /// Runtime owner retained one exact admitted executable operation package. + ExecutableOperationPackageInstalled, + /// Echo execution kernel retained one typed executable operation outcome. + ExecutableOperationExecutionRecorded, + /// Echo execution kernel retained the outcome's replayable state delta. + ExecutableOperationStateDeltaRecorded, } impl WalRecordKind { @@ -457,6 +476,9 @@ impl WalRecordKind { Self::TopologySuffixImportRecorded => "TopologySuffixImportRecorded", Self::CausalAnchorFactRecorded => "CausalAnchorFactRecorded", Self::CausalAnchorAdmissionReceiptRecorded => "CausalAnchorAdmissionReceiptRecorded", + Self::ExecutableOperationPackageInstalled => "ExecutableOperationPackageInstalled", + Self::ExecutableOperationExecutionRecorded => "ExecutableOperationExecutionRecorded", + Self::ExecutableOperationStateDeltaRecorded => "ExecutableOperationStateDeltaRecorded", } } @@ -484,6 +506,9 @@ impl WalRecordKind { Self::SchedulerFaultQuarantined | Self::TrustedRuntimeControlRecorded => { WalAppendAuthority::RuntimeControl } + Self::ExecutableOperationPackageInstalled => WalAppendAuthority::RuntimeControl, + Self::ExecutableOperationExecutionRecorded + | Self::ExecutableOperationStateDeltaRecorded => WalAppendAuthority::ExecutionKernel, Self::CausalAnchorFactRecorded | Self::CausalAnchorAdmissionReceiptRecorded => { WalAppendAuthority::AdmissionKernel } @@ -530,6 +555,9 @@ impl WalRecordKind { Self::SubmissionEnvelopeRetained => 22, Self::CausalAnchorFactRecorded => 23, Self::CausalAnchorAdmissionReceiptRecorded => 24, + Self::ExecutableOperationPackageInstalled => 25, + Self::ExecutableOperationExecutionRecorded => 26, + Self::ExecutableOperationStateDeltaRecorded => 27, } } @@ -559,6 +587,9 @@ impl WalRecordKind { 22 => Ok(Self::SubmissionEnvelopeRetained), 23 => Ok(Self::CausalAnchorFactRecorded), 24 => Ok(Self::CausalAnchorAdmissionReceiptRecorded), + 25 => Ok(Self::ExecutableOperationPackageInstalled), + 26 => Ok(Self::ExecutableOperationExecutionRecorded), + 27 => Ok(Self::ExecutableOperationStateDeltaRecorded), _ => Err(WalDecodeError::UnknownEnumCode { enum_name: "WalRecordKind", code, @@ -686,6 +717,10 @@ pub enum AffectedFrontierKind { TopologyIndex, /// Causal-anchor admission index frontier. CausalAnchorIndex, + /// Installed executable-operation catalog frontier. + ExecutableOperationCatalog, + /// Typed executable-operation receipt frontier. + ExecutableOperationReceiptIndex, } impl AffectedFrontierKind { @@ -701,6 +736,8 @@ impl AffectedFrontierKind { Self::CheckpointIndex => 6, Self::TopologyIndex => 7, Self::CausalAnchorIndex => 8, + Self::ExecutableOperationCatalog => 9, + Self::ExecutableOperationReceiptIndex => 10, } } @@ -7753,6 +7790,46 @@ pub fn build_replayable_tick_transaction( builder.commit(affected_frontiers) } +/// Builds one runtime-owner installation transaction for exact executable meaning. +#[cfg(all(feature = "native_rule_bootstrap", feature = "trusted_runtime"))] +pub(crate) fn build_executable_operation_installation_transaction( + mut builder: WalTransactionBuilder, + retained_installation_bytes: Vec, + affected_frontiers: Vec, +) -> Result { + builder.push_record( + WalRecordKind::ExecutableOperationPackageInstalled, + retained_installation_bytes, + )?; + builder.commit(affected_frontiers) +} + +/// Builds one execution-kernel-owned executable-operation commit transaction. +/// +/// This transaction is separate from the legacy scheduler-tick record shape. +/// Its execution-kernel-owned state-delta record remains the replayable +/// provenance carrier; the operation record carries the additional typed +/// executable-semantics receipt. +#[cfg(all(feature = "native_rule_bootstrap", feature = "trusted_runtime"))] +pub(crate) fn build_executable_operation_tick_transaction( + mut builder: WalTransactionBuilder, + retained_execution_bytes: Vec, + retained_state_delta_bytes: Vec, + affected_frontiers: Vec, +) -> Result { + WalRuntimeStateDeltaRecord::from_payload_bytes(&retained_state_delta_bytes) + .map_err(|_| WalBuildError::RuntimeStateDeltaInvalid)?; + builder.push_record( + WalRecordKind::ExecutableOperationExecutionRecorded, + retained_execution_bytes, + )?; + builder.push_record( + WalRecordKind::ExecutableOperationStateDeltaRecorded, + retained_state_delta_bytes, + )?; + builder.commit(affected_frontiers) +} + fn push_tick_receipt_records( builder: &mut WalTransactionBuilder, receipt: TickReceiptRecord, @@ -8804,6 +8881,18 @@ pub enum WalValidationError { /// Causal-anchor admission does not contain exactly one fact followed by one receipt. #[error("WAL causal-anchor admission frame shape is invalid")] CausalAnchorAdmissionFrameShapeMismatch, + /// Executable-operation installation does not contain exactly one package record. + #[error("WAL executable-operation installation frame shape is invalid")] + ExecutableOperationInstallationFrameShapeMismatch, + /// Executable-operation commit does not contain one receipt followed by one state delta. + #[error("WAL executable-operation tick frame shape is invalid")] + ExecutableOperationTickFrameShapeMismatch, + /// Executable-operation installation does not advance exactly its catalog frontier. + #[error("WAL executable-operation installation frontier shape is invalid")] + ExecutableOperationInstallationFrontierShapeMismatch, + /// Executable-operation commit does not advance its receipt and runtime frontiers in order. + #[error("WAL executable-operation tick frontier shape is invalid")] + ExecutableOperationTickFrontierShapeMismatch, /// Transaction contains no frames. #[error("WAL transaction contains no frames")] EmptyTransaction, @@ -9299,6 +9388,19 @@ fn validate_transaction_semantics( { return Err(WalValidationError::CausalAnchorAdmissionFrameShapeMismatch); } + if transaction_kind == WalTransactionKind::ExecutableOperationInstallation + && (frames.len() != 1 + || frames[0].header.record_kind != WalRecordKind::ExecutableOperationPackageInstalled) + { + return Err(WalValidationError::ExecutableOperationInstallationFrameShapeMismatch); + } + if transaction_kind == WalTransactionKind::ExecutableOperationTick + && (frames.len() != 2 + || frames[0].header.record_kind != WalRecordKind::ExecutableOperationExecutionRecorded + || frames[1].header.record_kind != WalRecordKind::ExecutableOperationStateDeltaRecorded) + { + return Err(WalValidationError::ExecutableOperationTickFrameShapeMismatch); + } Ok(()) } @@ -9306,6 +9408,19 @@ fn validate_transaction_frontiers( frontiers: &[AffectedFrontier], transaction_kind: WalTransactionKind, ) -> Result<(), WalValidationError> { + if transaction_kind == WalTransactionKind::ExecutableOperationInstallation + && (frontiers.len() != 1 + || frontiers[0].kind != AffectedFrontierKind::ExecutableOperationCatalog) + { + return Err(WalValidationError::ExecutableOperationInstallationFrontierShapeMismatch); + } + if transaction_kind == WalTransactionKind::ExecutableOperationTick + && (frontiers.len() != 2 + || frontiers[0].kind != AffectedFrontierKind::ExecutableOperationReceiptIndex + || frontiers[1].kind != AffectedFrontierKind::RuntimeState) + { + return Err(WalValidationError::ExecutableOperationTickFrontierShapeMismatch); + } for frontier in frontiers { if !frontier_kind_allowed_for_transaction(transaction_kind, frontier.kind) { return Err(WalValidationError::FrontierTransitionKindMismatch); @@ -9343,6 +9458,15 @@ fn frontier_kind_allowed_for_transaction( WalTransactionKind::CausalAnchorAdmission => { matches!(frontier_kind, AffectedFrontierKind::CausalAnchorIndex) } + WalTransactionKind::ExecutableOperationInstallation => matches!( + frontier_kind, + AffectedFrontierKind::ExecutableOperationCatalog + ), + WalTransactionKind::ExecutableOperationTick => matches!( + frontier_kind, + AffectedFrontierKind::RuntimeState + | AffectedFrontierKind::ExecutableOperationReceiptIndex + ), } } diff --git a/crates/warp-core/src/echo_operation.rs b/crates/warp-core/src/echo_operation.rs new file mode 100644 index 00000000..830946ae --- /dev/null +++ b/crates/warp-core/src/echo_operation.rs @@ -0,0 +1,4566 @@ +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +//! Echo-owned executable operation packages and bounded evaluation. +//! +//! This module is intentionally operation-oriented rather than a second +//! provider revision. Provider v1 binds ambient native callbacks and remains a +//! compatibility corridor. An executable operation package instead carries the +//! complete data-only program interpreted here by Echo. +//! +//! The first earned program profile is deliberately small: an invocation +//! anchors one typed node and compares the digest of its typed alpha attachment +//! before replacing that attachment. It has one unique match, a closed +//! attachment algebra, explicit resource bounds, and no callback, function +//! pointer, matcher, executor, or application-specific intrinsic. + +use std::{collections::BTreeMap, sync::Arc}; + +use blake3::Hasher; +use bytes::Bytes; +use echo_edict_canonical::{ + decode_canonical_cbor_v1, encode_canonical_cbor_v1, CanonicalValueError, + CanonicalValueErrorKind, CanonicalValueV1, +}; +use thiserror::Error; + +use crate::{ + attachment::{AtomPayload, AttachmentKey, AttachmentValue}, + clock::{GlobalTick, WorldlineTick}, + footprint::{Footprint, WarpScopedPortKey}, + head::WriterHeadKey, + ident::{EdgeKey, Hash, NodeKey, TypeId}, + receipt::{TickReceipt, TickReceiptDisposition, TickReceiptEntry}, + snapshot::{compute_commit_hash_v2, Snapshot}, + tick_patch::{SlotId, TickCommitStatus, TickPatchError, WarpOp, WarpTickPatchV1}, + tx::TxId, + worldline_state::WorldlineState, +}; + +const PACKAGE_SCHEMA: &str = "echo.operation-package/v1"; +const PROGRAM_SCHEMA: &str = "echo.operation-program/v1"; +const INVOCATION_SCHEMA: &str = "echo.operation-invocation/v1"; +const PROGRAM_KIND: &str = "anchored-node-attachment-compare-and-set/v1"; +const FOOTPRINT_CONTRACT: &str = "anchored-node-alpha-exact/v1"; +const INPUT_SCHEMA: &str = "echo.operation.input.anchored-node-alpha-cas/v1"; +const RESULT_SCHEMA: &str = "echo.operation.result.anchored-node-alpha-cas/v1"; +const OBSTRUCTION_SCHEMA: &str = "echo.operation.obstruction.anchored-node-alpha-cas/v1"; +const RESULT_INTERPRETATION: &str = + "echo.operation.result-interpretation.anchored-node-alpha-cas/v1"; +const OBSTRUCTION_INTERPRETATION: &str = + "echo.operation.obstruction-interpretation.anchored-node-alpha-cas/v1"; +const BASIS_SCHEMA: &str = "echo.operation.evaluation-basis/v1"; +const APPLICATION_BASIS_SCHEMA: &str = "echo.operation.basis.anchored-node-alpha/v1"; +const TARGET_PROFILE: &str = "echo.operation-target.anchored-node-alpha-cas/v1"; +const INTERPRETER_PROFILE: &str = "echo.operation-interpreter/v1"; +const INTRINSIC_PROFILE: &str = "echo.operation-attachment-algebra/v1"; +const PACKAGE_ID_DOMAIN: &[u8] = b"echo:operation-package:v1\0"; +const PROGRAM_ID_DOMAIN: &[u8] = b"echo:operation-program:v1\0"; +const INVOCATION_ID_DOMAIN: &[u8] = b"echo:operation-invocation:v1\0"; +const INVOCATION_BYTES_DIGEST_DOMAIN: &[u8] = b"echo:operation-invocation-bytes:v1\0"; +const BASIS_ID_DOMAIN: &[u8] = b"echo:operation-evaluation-basis:v1\0"; +const PACKAGE_ADMISSION_ID_DOMAIN: &[u8] = b"echo:operation-package-admission:v1\0"; +const INSTALLATION_ID_DOMAIN: &[u8] = b"echo:operation-installation:v1\0"; +const INVOCATION_ADMISSION_ID_DOMAIN: &[u8] = b"echo:operation-invocation-admission:v1\0"; +const PRIVATE_EVALUATION_ID_DOMAIN: &[u8] = b"echo:operation-private-evaluation:v1\0"; +const PREPARATION_ID_DOMAIN: &[u8] = b"echo:operation-preparation:v1\0"; +const RESULT_ID_DOMAIN: &[u8] = b"echo:operation-result:v1\0"; +const OBSTRUCTION_ID_DOMAIN: &[u8] = b"echo:operation-obstruction:v1\0"; +const TERMINAL_OUTCOME_ID_DOMAIN: &[u8] = b"echo:operation-terminal-outcome:v1\0"; +const ATOM_VALUE_DOMAIN: &[u8] = b"echo:operation-atom-value:v1\0"; +const APPLICATION_BASIS_VALUE_DOMAIN: &[u8] = b"echo:operation-anchored-node-alpha-basis:v1\0"; +const FOOTPRINT_DIGEST_DOMAIN: &[u8] = b"echo:operation-footprint:v1\0"; +const RECEIPT_DIGEST_DOMAIN: &[u8] = b"echo:operation-receipt:v1\0"; +const COMPOSITION_DIGEST_DOMAIN: &[u8] = b"echo:operation-singleton-composition:v1\0"; +const PLAN_DIGEST_DOMAIN: &[u8] = b"echo:operation-plan:v1\0"; +const REWRITES_DIGEST_DOMAIN: &[u8] = b"echo:operation-rewrites:v1\0"; +const GENESIS_COMMIT_DOMAIN: &[u8] = b"echo:operation-genesis-commit:v1\0"; + +/// Process-local capability proving that admission, evaluation, and commit are +/// owned by the same Echo runtime instance. +#[derive(Clone, Debug)] +pub(crate) struct EchoOperationEvaluationAuthorityV1(Arc<()>); + +impl EchoOperationEvaluationAuthorityV1 { + pub(crate) fn new() -> Self { + Self(Arc::new(())) + } + + fn same_owner(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} + +/// Stable content identity of exact executable-operation package bytes. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EchoOperationPackageIdV1(Hash); + +impl EchoOperationPackageIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Stable content identity of exact Echo operation-program bytes. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EchoOperationProgramIdV1(Hash); + +impl EchoOperationProgramIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Stable content identity of one canonical invocation. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EchoOperationInvocationIdV1(Hash); + +impl EchoOperationInvocationIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Stable content identity of one exact Echo evaluation basis. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EchoOperationEvaluationBasisIdV1(Hash); + +impl EchoOperationEvaluationBasisIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Stable identity of Echo's package-admission evidence. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EchoOperationPackageAdmissionIdV1(Hash); + +impl EchoOperationPackageAdmissionIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Stable identity of Echo-owned installed executable meaning. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InstalledEchoOperationIdV1(Hash); + +impl InstalledEchoOperationIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Stable identity of Echo's invocation-admission evidence. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EchoOperationInvocationAdmissionIdV1(Hash); + +impl EchoOperationInvocationAdmissionIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Stable identity of one bounded private evaluation. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EchoOperationPrivateEvaluationIdV1(Hash); + +impl EchoOperationPrivateEvaluationIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Stable identity of one complete committable preparation. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PreparedEchoOperationIdV1(Hash); + +impl PreparedEchoOperationIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Stable identity of the typed result produced by private evaluation. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EchoOperationResultIdV1(Hash); + +impl EchoOperationResultIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Stable identity of one typed no-patch evaluation obstruction. +#[repr(transparent)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EchoOperationObstructionIdV1(Hash); + +impl EchoOperationObstructionIdV1 { + /// Returns the raw digest bytes. + #[must_use] + pub const fn as_hash(self) -> Hash { + self.0 + } +} + +/// Computes the identity of exact canonical package bytes. +/// +/// This digest is substitution evidence only. It does not confer an operation +/// coordinate, installation, invocability, or authority. +#[must_use] +pub fn echo_operation_package_id_v1(bytes: &[u8]) -> EchoOperationPackageIdV1 { + EchoOperationPackageIdV1(domain_hash(PACKAGE_ID_DOMAIN, bytes)) +} + +/// Computes the digest of one typed attachment atom. +#[must_use] +pub fn echo_operation_atom_value_digest_v1(type_id: TypeId, bytes: &[u8]) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(ATOM_VALUE_DOMAIN); + hasher.update(type_id.as_bytes()); + hasher.update(&(bytes.len() as u64).to_le_bytes()); + hasher.update(bytes); + hasher.finalize().into() +} + +/// Returns the first program profile's canonical application-basis proposition. +#[must_use] +pub fn echo_operation_anchored_node_application_basis_v1( + node: NodeKey, + attachment_type: TypeId, + bytes: &[u8], +) -> EchoOperationApplicationBasisV1 { + let atom_digest = echo_operation_atom_value_digest_v1(attachment_type, bytes); + let mut hasher = Hasher::new(); + hasher.update(APPLICATION_BASIS_VALUE_DOMAIN); + hasher.update(node.warp_id.as_bytes()); + hasher.update(node.local_id.as_bytes()); + hasher.update(&atom_digest); + EchoOperationApplicationBasisV1::new( + profile_digest(APPLICATION_BASIS_SCHEMA), + hasher.finalize().into(), + ) +} + +/// Returns the exact target-profile identity implemented by the v1 evaluator. +#[must_use] +pub fn echo_operation_target_profile_identity_v1() -> Hash { + profile_digest(TARGET_PROFILE) +} + +/// A three-axis resource budget for one bounded operation evaluation. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct EchoOperationBudgetV1 { + steps: u64, + read_bytes: u64, + write_bytes: u64, +} + +impl EchoOperationBudgetV1 { + /// Creates one explicit budget. + #[must_use] + pub const fn new(steps: u64, read_bytes: u64, write_bytes: u64) -> Self { + Self { + steps, + read_bytes, + write_bytes, + } + } + + /// Returns the step allowance or consumption. + #[must_use] + pub const fn steps(self) -> u64 { + self.steps + } + + /// Returns the read-byte allowance or consumption. + #[must_use] + pub const fn read_bytes(self) -> u64 { + self.read_bytes + } + + /// Returns the write-byte allowance or consumption. + #[must_use] + pub const fn write_bytes(self) -> u64 { + self.write_bytes + } + + fn is_nonzero(self) -> bool { + self.steps != 0 + } + + fn fits_within(self, ceiling: Self) -> bool { + self.steps <= ceiling.steps + && self.read_bytes <= ceiling.read_bytes + && self.write_bytes <= ceiling.write_bytes + } + + fn to_value(self) -> CanonicalValueV1 { + map_value([ + ("read_bytes", uint_value(self.read_bytes)), + ("steps", uint_value(self.steps)), + ("write_bytes", uint_value(self.write_bytes)), + ]) + } + + fn from_value(value: CanonicalValueV1) -> Result { + let mut fields = exact_text_map(value, &["read_bytes", "steps", "write_bytes"])?; + Ok(Self { + steps: take_u64(&mut fields, "steps")?, + read_bytes: take_u64(&mut fields, "read_bytes")?, + write_bytes: take_u64(&mut fields, "write_bytes")?, + }) + } +} + +#[derive(Clone, Copy, Debug)] +struct EchoOperationBudgetMeterV1 { + delegated: EchoOperationBudgetV1, + consumed: EchoOperationBudgetV1, +} + +impl EchoOperationBudgetMeterV1 { + const fn new(delegated: EchoOperationBudgetV1) -> Self { + Self { + delegated, + consumed: EchoOperationBudgetV1::new(0, 0, 0), + } + } + + fn charge(&mut self, steps: u64, read_bytes: u64, write_bytes: u64) -> bool { + let Some(next_steps) = self.consumed.steps.checked_add(steps) else { + return false; + }; + let Some(next_read_bytes) = self.consumed.read_bytes.checked_add(read_bytes) else { + return false; + }; + let Some(next_write_bytes) = self.consumed.write_bytes.checked_add(write_bytes) else { + return false; + }; + let next = EchoOperationBudgetV1::new(next_steps, next_read_bytes, next_write_bytes); + if !next.fits_within(self.delegated) { + return false; + } + self.consumed = next; + true + } + + const fn consumed(self) -> EchoOperationBudgetV1 { + self.consumed + } +} + +/// The declared footprint-derivation contract implemented by the first program profile. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum EchoOperationFootprintContractV1 { + /// Read one anchored node and alpha attachment; write that same attachment. + AnchoredNodeAlphaExact, +} + +impl EchoOperationFootprintContractV1 { + const fn coordinate(self) -> &'static str { + match self { + Self::AnchoredNodeAlphaExact => FOOTPRINT_CONTRACT, + } + } +} + +/// Data-only executable meaning interpreted by Echo. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum EchoOperationProgramV1 { + /// Compare and replace one anchored typed node attachment. + AnchoredNodeAttachmentCompareAndSet { + /// Required skeleton node type. + required_node_type: TypeId, + /// Required alpha attachment atom type. + required_attachment_type: TypeId, + /// Maximum replacement byte count accepted by the program. + max_replacement_bytes: u64, + }, +} + +impl EchoOperationProgramV1 { + /// Creates the first bounded, unique-match operation program. + #[must_use] + pub const fn anchored_node_attachment_compare_and_set( + required_node_type: TypeId, + required_attachment_type: TypeId, + max_replacement_bytes: u64, + ) -> Self { + Self::AnchoredNodeAttachmentCompareAndSet { + required_node_type, + required_attachment_type, + max_replacement_bytes, + } + } + + /// Returns the exact program identity. + pub fn identity(&self) -> Result { + Ok(EchoOperationProgramIdV1(domain_hash( + PROGRAM_ID_DOMAIN, + &self.to_canonical_bytes()?, + ))) + } + + /// Encodes this program using Edict's canonical CBOR profile. + pub fn to_canonical_bytes(&self) -> Result, EchoOperationArtifactErrorV1> { + self.validate_supported_profile()?; + encode_canonical_cbor_v1(&self.to_value()).map_err(canonical_error) + } + + fn footprint_contract(&self) -> EchoOperationFootprintContractV1 { + match self { + Self::AnchoredNodeAttachmentCompareAndSet { .. } => { + EchoOperationFootprintContractV1::AnchoredNodeAlphaExact + } + } + } + + const fn minimum_budget(&self) -> EchoOperationBudgetV1 { + match self { + Self::AnchoredNodeAttachmentCompareAndSet { .. } => { + EchoOperationBudgetV1::new(4, 64, 32) + } + } + } + + fn validate_supported_profile(&self) -> Result<(), EchoOperationArtifactErrorV1> { + match self { + Self::AnchoredNodeAttachmentCompareAndSet { + max_replacement_bytes, + .. + } if *max_replacement_bytes == 0 => Err(artifact_error( + EchoOperationArtifactErrorKindV1::UnsupportedProgram, + "program replacement bound must be nonzero", + )), + Self::AnchoredNodeAttachmentCompareAndSet { .. } => Ok(()), + } + } + + fn to_value(&self) -> CanonicalValueV1 { + match self { + Self::AnchoredNodeAttachmentCompareAndSet { + required_node_type, + required_attachment_type, + max_replacement_bytes, + } => map_value([ + ( + "interpreter_profile_identity", + hash_value(profile_digest(INTERPRETER_PROFILE)), + ), + ( + "intrinsic_profile_identity", + hash_value(profile_digest(INTRINSIC_PROFILE)), + ), + ("kind", text_value(PROGRAM_KIND)), + ("max_replacement_bytes", uint_value(*max_replacement_bytes)), + ( + "required_attachment_type", + hash_value(required_attachment_type.0), + ), + ("required_node_type", hash_value(required_node_type.0)), + ("schema", text_value(PROGRAM_SCHEMA)), + ]), + } + } + + fn from_canonical_bytes(bytes: &[u8]) -> Result { + let value = decode_canonical_cbor_v1(bytes).map_err(canonical_error)?; + let mut fields = exact_text_map( + value, + &[ + "interpreter_profile_identity", + "intrinsic_profile_identity", + "kind", + "max_replacement_bytes", + "required_attachment_type", + "required_node_type", + "schema", + ], + )?; + require_text(&mut fields, "schema", PROGRAM_SCHEMA)?; + require_text(&mut fields, "kind", PROGRAM_KIND)?; + let interpreter_profile_identity = take_hash(&mut fields, "interpreter_profile_identity")?; + let intrinsic_profile_identity = take_hash(&mut fields, "intrinsic_profile_identity")?; + if interpreter_profile_identity != profile_digest(INTERPRETER_PROFILE) + || intrinsic_profile_identity != profile_digest(INTRINSIC_PROFILE) + { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::UnsupportedTargetProfile, + "operation program names an unsupported interpreter or intrinsic profile", + )); + } + let required_node_type = TypeId(take_hash(&mut fields, "required_node_type")?); + let required_attachment_type = TypeId(take_hash(&mut fields, "required_attachment_type")?); + let max_replacement_bytes = take_u64(&mut fields, "max_replacement_bytes")?; + let program = Self::anchored_node_attachment_compare_and_set( + required_node_type, + required_attachment_type, + max_replacement_bytes, + ); + program.validate_supported_profile()?; + Ok(program) + } +} + +/// Application- and Edict-owned identities that close one executable meaning. +/// +/// Echo treats these as opaque substitution evidence. It does not infer source, +/// Core, Target IR, application schema, or lawpack identity from program bytes. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EchoOperationSemanticClosureV1 { + edict_source_identity: Hash, + canonical_meaning_identity: Hash, + core_identity: Hash, + target_ir_identity: Hash, + application_schema_coordinate: String, + application_schema_identity: Hash, + lawpack_coordinate: String, + lawpack_identity: Hash, +} + +impl EchoOperationSemanticClosureV1 { + /// Creates the exact upstream semantic closure bound by one package. + #[allow(clippy::too_many_arguments)] + #[must_use] + pub fn new( + edict_source_identity: Hash, + canonical_meaning_identity: Hash, + core_identity: Hash, + target_ir_identity: Hash, + application_schema_coordinate: impl Into, + application_schema_identity: Hash, + lawpack_coordinate: impl Into, + lawpack_identity: Hash, + ) -> Self { + Self { + edict_source_identity, + canonical_meaning_identity, + core_identity, + target_ir_identity, + application_schema_coordinate: application_schema_coordinate.into(), + application_schema_identity, + lawpack_coordinate: lawpack_coordinate.into(), + lawpack_identity, + } + } + + /// Returns the canonical Edict meaning identity. + #[must_use] + pub const fn canonical_meaning_identity(&self) -> Hash { + self.canonical_meaning_identity + } + + /// Returns the application-owned lawpack identity. + #[must_use] + pub const fn lawpack_identity(&self) -> Hash { + self.lawpack_identity + } + + fn validate(&self) -> Result<(), EchoOperationArtifactErrorV1> { + if self.application_schema_coordinate.is_empty() || self.lawpack_coordinate.is_empty() { + return Err(invalid_structure( + "application schema and lawpack coordinates must not be empty", + )); + } + Ok(()) + } + + fn to_value(&self) -> CanonicalValueV1 { + map_value([ + ( + "application_schema_coordinate", + text_value(&self.application_schema_coordinate), + ), + ( + "application_schema_identity", + hash_value(self.application_schema_identity), + ), + ( + "canonical_meaning_identity", + hash_value(self.canonical_meaning_identity), + ), + ("core_identity", hash_value(self.core_identity)), + ( + "edict_source_identity", + hash_value(self.edict_source_identity), + ), + ("lawpack_coordinate", text_value(&self.lawpack_coordinate)), + ("lawpack_identity", hash_value(self.lawpack_identity)), + ("target_ir_identity", hash_value(self.target_ir_identity)), + ]) + } + + fn from_value(value: CanonicalValueV1) -> Result { + let mut fields = exact_text_map( + value, + &[ + "application_schema_coordinate", + "application_schema_identity", + "canonical_meaning_identity", + "core_identity", + "edict_source_identity", + "lawpack_coordinate", + "lawpack_identity", + "target_ir_identity", + ], + )?; + let closure = Self { + edict_source_identity: take_hash(&mut fields, "edict_source_identity")?, + canonical_meaning_identity: take_hash(&mut fields, "canonical_meaning_identity")?, + core_identity: take_hash(&mut fields, "core_identity")?, + target_ir_identity: take_hash(&mut fields, "target_ir_identity")?, + application_schema_coordinate: take_text(&mut fields, "application_schema_coordinate")?, + application_schema_identity: take_hash(&mut fields, "application_schema_identity")?, + lawpack_coordinate: take_text(&mut fields, "lawpack_coordinate")?, + lawpack_identity: take_hash(&mut fields, "lawpack_identity")?, + }; + closure.validate()?; + Ok(closure) + } +} + +/// Exact executable-operation publication material and provenance. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ExecutableOperationPackageV1 { + operation_coordinate: String, + semantic_closure: EchoOperationSemanticClosureV1, + target_profile_identity: Hash, + interpreter_profile_identity: Hash, + intrinsic_profile_identity: Hash, + authority_profile_identity: Hash, + application_basis_schema_identity: Hash, + input_schema_identity: Hash, + result_schema_identity: Hash, + obstruction_schema_identity: Hash, + result_interpretation_identity: Hash, + obstruction_interpretation_identity: Hash, + evaluation_basis_schema_identity: Hash, + footprint_contract_identity: Hash, + budget_ceiling: EchoOperationBudgetV1, + program: EchoOperationProgramV1, +} + +impl ExecutableOperationPackageV1 { + /// Creates publication material for the supported bounded operation profile. + #[must_use] + pub fn new( + operation_coordinate: impl Into, + semantic_closure: EchoOperationSemanticClosureV1, + target_profile_identity: Hash, + authority_profile_identity: Hash, + budget_ceiling: EchoOperationBudgetV1, + program: EchoOperationProgramV1, + ) -> Self { + Self { + operation_coordinate: operation_coordinate.into(), + semantic_closure, + target_profile_identity, + interpreter_profile_identity: profile_digest(INTERPRETER_PROFILE), + intrinsic_profile_identity: profile_digest(INTRINSIC_PROFILE), + authority_profile_identity, + input_schema_identity: profile_digest(INPUT_SCHEMA), + result_schema_identity: profile_digest(RESULT_SCHEMA), + obstruction_schema_identity: profile_digest(OBSTRUCTION_SCHEMA), + result_interpretation_identity: profile_digest(RESULT_INTERPRETATION), + obstruction_interpretation_identity: profile_digest(OBSTRUCTION_INTERPRETATION), + application_basis_schema_identity: profile_digest(APPLICATION_BASIS_SCHEMA), + evaluation_basis_schema_identity: profile_digest(BASIS_SCHEMA), + footprint_contract_identity: profile_digest(program.footprint_contract().coordinate()), + budget_ceiling, + program, + } + } + + /// Returns the public operation coordinate. + #[must_use] + pub fn operation_coordinate(&self) -> &str { + &self.operation_coordinate + } + + /// Returns the Edict semantic identity bound by this package. + #[must_use] + pub const fn semantic_identity(&self) -> Hash { + self.semantic_closure.canonical_meaning_identity + } + + /// Returns the application-owned lawpack identity bound by this package. + #[must_use] + pub const fn lawpack_identity(&self) -> Hash { + self.semantic_closure.lawpack_identity + } + + /// Returns the Echo target-profile identity bound by this package. + #[must_use] + pub const fn target_profile_identity(&self) -> Hash { + self.target_profile_identity + } + + /// Returns the required authority-profile identity. + #[must_use] + pub const fn authority_profile_identity(&self) -> Hash { + self.authority_profile_identity + } + + /// Returns the package budget ceiling. + #[must_use] + pub const fn budget_ceiling(&self) -> EchoOperationBudgetV1 { + self.budget_ceiling + } + + /// Returns the executable program. + #[must_use] + pub const fn program(&self) -> &EchoOperationProgramV1 { + &self.program + } + + /// Encodes exact package bytes using Edict's canonical CBOR profile. + pub fn to_canonical_bytes(&self) -> Result, EchoOperationArtifactErrorV1> { + if self.operation_coordinate.is_empty() { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::EmptyOperationCoordinate, + "operation coordinate must not be empty", + )); + } + if !self.budget_ceiling.is_nonzero() { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::InvalidBudget, + "package step budget must be nonzero", + )); + } + if !self + .program + .minimum_budget() + .fits_within(self.budget_ceiling) + { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::InvalidBudget, + "package budget cannot complete the program's smallest lawful evaluation", + )); + } + self.semantic_closure.validate()?; + let program_bytes = self.program.to_canonical_bytes()?; + let value = map_value([ + ( + "application_basis_schema_identity", + hash_value(self.application_basis_schema_identity), + ), + ( + "authority_profile_identity", + hash_value(self.authority_profile_identity), + ), + ("budget_ceiling", self.budget_ceiling.to_value()), + ( + "evaluation_basis_schema_identity", + hash_value(self.evaluation_basis_schema_identity), + ), + ( + "footprint_contract_identity", + hash_value(self.footprint_contract_identity), + ), + ( + "interpreter_profile_identity", + hash_value(self.interpreter_profile_identity), + ), + ( + "input_schema_identity", + hash_value(self.input_schema_identity), + ), + ( + "intrinsic_profile_identity", + hash_value(self.intrinsic_profile_identity), + ), + ( + "obstruction_schema_identity", + hash_value(self.obstruction_schema_identity), + ), + ( + "obstruction_interpretation_identity", + hash_value(self.obstruction_interpretation_identity), + ), + ( + "operation_coordinate", + text_value(&self.operation_coordinate), + ), + ("program", CanonicalValueV1::Bytes(program_bytes)), + ( + "result_schema_identity", + hash_value(self.result_schema_identity), + ), + ( + "result_interpretation_identity", + hash_value(self.result_interpretation_identity), + ), + ("schema", text_value(PACKAGE_SCHEMA)), + ("semantic_closure", self.semantic_closure.to_value()), + ( + "target_profile_identity", + hash_value(self.target_profile_identity), + ), + ]); + encode_canonical_cbor_v1(&value).map_err(canonical_error) + } + + fn from_canonical_bytes(bytes: &[u8]) -> Result { + let value = decode_canonical_cbor_v1(bytes).map_err(canonical_error)?; + let mut fields = exact_text_map( + value, + &[ + "authority_profile_identity", + "application_basis_schema_identity", + "budget_ceiling", + "evaluation_basis_schema_identity", + "footprint_contract_identity", + "interpreter_profile_identity", + "input_schema_identity", + "intrinsic_profile_identity", + "obstruction_schema_identity", + "obstruction_interpretation_identity", + "operation_coordinate", + "program", + "result_schema_identity", + "result_interpretation_identity", + "schema", + "semantic_closure", + "target_profile_identity", + ], + )?; + require_text(&mut fields, "schema", PACKAGE_SCHEMA)?; + let operation_coordinate = take_text(&mut fields, "operation_coordinate")?; + if operation_coordinate.is_empty() { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::EmptyOperationCoordinate, + "operation coordinate must not be empty", + )); + } + let program_bytes = take_bytes(&mut fields, "program")?; + let program = EchoOperationProgramV1::from_canonical_bytes(&program_bytes)?; + let package = Self { + operation_coordinate, + semantic_closure: EchoOperationSemanticClosureV1::from_value(take_field( + &mut fields, + "semantic_closure", + )?)?, + target_profile_identity: take_hash(&mut fields, "target_profile_identity")?, + interpreter_profile_identity: take_hash(&mut fields, "interpreter_profile_identity")?, + intrinsic_profile_identity: take_hash(&mut fields, "intrinsic_profile_identity")?, + authority_profile_identity: take_hash(&mut fields, "authority_profile_identity")?, + input_schema_identity: take_hash(&mut fields, "input_schema_identity")?, + result_schema_identity: take_hash(&mut fields, "result_schema_identity")?, + obstruction_schema_identity: take_hash(&mut fields, "obstruction_schema_identity")?, + result_interpretation_identity: take_hash( + &mut fields, + "result_interpretation_identity", + )?, + obstruction_interpretation_identity: take_hash( + &mut fields, + "obstruction_interpretation_identity", + )?, + application_basis_schema_identity: take_hash( + &mut fields, + "application_basis_schema_identity", + )?, + evaluation_basis_schema_identity: take_hash( + &mut fields, + "evaluation_basis_schema_identity", + )?, + footprint_contract_identity: take_hash(&mut fields, "footprint_contract_identity")?, + budget_ceiling: EchoOperationBudgetV1::from_value(take_field( + &mut fields, + "budget_ceiling", + )?)?, + program, + }; + package.self_validate_supported_profile()?; + if package.to_canonical_bytes()? != bytes { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::NonCanonical, + "package did not reproduce the exact admitted bytes", + )); + } + Ok(package) + } + + fn self_validate_supported_profile(&self) -> Result<(), EchoOperationArtifactErrorV1> { + if !self.budget_ceiling.is_nonzero() { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::InvalidBudget, + "package step budget must be nonzero", + )); + } + let expected = [ + ( + "input schema", + self.input_schema_identity, + profile_digest(INPUT_SCHEMA), + ), + ( + "result schema", + self.result_schema_identity, + profile_digest(RESULT_SCHEMA), + ), + ( + "obstruction schema", + self.obstruction_schema_identity, + profile_digest(OBSTRUCTION_SCHEMA), + ), + ( + "result interpretation", + self.result_interpretation_identity, + profile_digest(RESULT_INTERPRETATION), + ), + ( + "obstruction interpretation", + self.obstruction_interpretation_identity, + profile_digest(OBSTRUCTION_INTERPRETATION), + ), + ( + "application basis schema", + self.application_basis_schema_identity, + profile_digest(APPLICATION_BASIS_SCHEMA), + ), + ( + "evaluation basis schema", + self.evaluation_basis_schema_identity, + profile_digest(BASIS_SCHEMA), + ), + ( + "footprint contract", + self.footprint_contract_identity, + profile_digest(self.program.footprint_contract().coordinate()), + ), + ]; + if let Some((label, _, _)) = expected + .iter() + .find(|(_, actual, required)| actual != required) + { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::UnsupportedSchema, + format!("unsupported {label} identity"), + )); + } + if self.target_profile_identity != profile_digest(TARGET_PROFILE) { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::UnsupportedTargetProfile, + "unsupported Echo target profile identity", + )); + } + if self.interpreter_profile_identity != profile_digest(INTERPRETER_PROFILE) + || self.intrinsic_profile_identity != profile_digest(INTRINSIC_PROFILE) + { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::UnsupportedTargetProfile, + "unsupported interpreter or intrinsic profile identity", + )); + } + Ok(()) + } +} + +/// Stable package decode/self-validation failure categories. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum EchoOperationArtifactErrorKindV1 { + /// Canonical bytes were malformed or outside the accepted value model. + MalformedCanonicalBytes, + /// The decoded value did not have the exact supported structure. + InvalidStructure, + /// Exact canonical bytes were not supplied. + NonCanonical, + /// The public operation coordinate was empty. + EmptyOperationCoordinate, + /// The target profile is not implemented by this Echo runtime. + UnsupportedTargetProfile, + /// A schema or footprint profile is not implemented by this runtime. + UnsupportedSchema, + /// The program kind or its values are unsupported. + UnsupportedProgram, + /// The budget is malformed or cannot execute any step. + InvalidBudget, +} + +/// One structured artifact failure. +#[derive(Clone, Debug, Error, PartialEq, Eq)] +#[error("{kind:?}: {detail}")] +pub struct EchoOperationArtifactErrorV1 { + kind: EchoOperationArtifactErrorKindV1, + detail: String, +} + +impl EchoOperationArtifactErrorV1 { + /// Returns the stable failure category. + #[must_use] + pub const fn kind(&self) -> EchoOperationArtifactErrorKindV1 { + self.kind + } + + /// Returns deterministic diagnostic detail. + #[must_use] + pub fn detail(&self) -> &str { + &self.detail + } +} + +/// Independently pinned policy for the package-admission crossing. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EchoOperationAdmissionPolicyV1 { + expected_package_id: EchoOperationPackageIdV1, + expected_operation_coordinate: String, + expected_authority_profile_identity: Hash, + maximum_budget: EchoOperationBudgetV1, +} + +impl EchoOperationAdmissionPolicyV1 { + /// Pins the exact package, public operation, authority profile, and budget ceiling. + #[must_use] + pub fn exact( + expected_package_id: EchoOperationPackageIdV1, + expected_operation_coordinate: impl Into, + expected_authority_profile_identity: Hash, + maximum_budget: EchoOperationBudgetV1, + ) -> Self { + Self { + expected_package_id, + expected_operation_coordinate: expected_operation_coordinate.into(), + expected_authority_profile_identity, + maximum_budget, + } + } + + fn identity(&self) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(b"echo:operation-admission-policy:v1\0"); + hasher.update(&self.expected_package_id.as_hash()); + hash_len_bytes(&mut hasher, self.expected_operation_coordinate.as_bytes()); + hasher.update(&self.expected_authority_profile_identity); + hash_budget(&mut hasher, self.maximum_budget); + hasher.finalize().into() + } + + fn to_value(&self) -> CanonicalValueV1 { + map_value([ + ( + "expected_authority_profile_identity", + hash_value(self.expected_authority_profile_identity), + ), + ( + "expected_operation_coordinate", + text_value(&self.expected_operation_coordinate), + ), + ( + "expected_package_id", + hash_value(self.expected_package_id.as_hash()), + ), + ("maximum_budget", self.maximum_budget.to_value()), + ]) + } + + fn from_value(value: CanonicalValueV1) -> Result { + let mut fields = exact_text_map( + value, + &[ + "expected_authority_profile_identity", + "expected_operation_coordinate", + "expected_package_id", + "maximum_budget", + ], + )?; + let policy = Self { + expected_package_id: EchoOperationPackageIdV1(take_hash( + &mut fields, + "expected_package_id", + )?), + expected_operation_coordinate: take_text(&mut fields, "expected_operation_coordinate")?, + expected_authority_profile_identity: take_hash( + &mut fields, + "expected_authority_profile_identity", + )?, + maximum_budget: EchoOperationBudgetV1::from_value(take_field( + &mut fields, + "maximum_budget", + )?)?, + }; + if policy.expected_operation_coordinate.is_empty() || !policy.maximum_budget.is_nonzero() { + return Err(invalid_structure( + "package-admission policy coordinate and budget must be nonempty", + )); + } + Ok(policy) + } +} + +/// Stable package-admission refusal categories. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum EchoOperationAdmissionErrorKindV1 { + /// Exact package bytes were malformed or unsupported. + ArtifactInvalid, + /// Exact package bytes did not match the independently pinned package id. + PackageIdentityMismatch, + /// The public operation coordinate differed from policy. + OperationCoordinateMismatch, + /// The authority profile differed from policy. + AuthorityProfileMismatch, + /// The package budget exceeded policy. + BudgetExceedsPolicy, +} + +/// One structured package-admission refusal. +#[derive(Clone, Debug, Error, PartialEq, Eq)] +#[error("{kind:?}: {detail}")] +pub struct EchoOperationAdmissionErrorV1 { + kind: EchoOperationAdmissionErrorKindV1, + detail: String, + artifact: Option, +} + +impl EchoOperationAdmissionErrorV1 { + /// Returns the stable refusal category. + #[must_use] + pub const fn kind(&self) -> EchoOperationAdmissionErrorKindV1 { + self.kind + } + + /// Returns the artifact failure, when admission failed during decoding. + #[must_use] + pub const fn artifact(&self) -> Option<&EchoOperationArtifactErrorV1> { + self.artifact.as_ref() + } +} + +/// Opaque evidence that Echo admitted exact package bytes under separate policy. +#[derive(Clone, Debug)] +pub struct AdmittedExecutableOperationPackageV1 { + package: ExecutableOperationPackageV1, + canonical_package_bytes: Vec, + package_id: EchoOperationPackageIdV1, + admission_policy_id: Hash, + admission_id: EchoOperationPackageAdmissionIdV1, + admission_policy: EchoOperationAdmissionPolicyV1, +} + +/// Installed executable meaning. Installation does not itself authorize an invocation. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct InstalledEchoOperationV1 { + installed_operation_id: InstalledEchoOperationIdV1, + package_id: EchoOperationPackageIdV1, + package_admission_id: EchoOperationPackageAdmissionIdV1, + operation_coordinate: String, + semantic_identity: Hash, + lawpack_identity: Hash, + target_profile_identity: Hash, + interpreter_profile_identity: Hash, + intrinsic_profile_identity: Hash, + authority_profile_identity: Hash, + application_basis_schema_identity: Hash, + budget_ceiling: EchoOperationBudgetV1, + program_id: EchoOperationProgramIdV1, + program: EchoOperationProgramV1, + canonical_package_bytes: Vec, + admission_policy_id: Hash, + admission_policy: EchoOperationAdmissionPolicyV1, +} + +impl InstalledEchoOperationV1 { + /// Returns Echo's installed-operation identity. + #[must_use] + pub const fn installed_operation_id(&self) -> InstalledEchoOperationIdV1 { + self.installed_operation_id + } + + /// Returns the public operation coordinate admitted by Echo. + #[must_use] + pub fn operation_coordinate(&self) -> &str { + &self.operation_coordinate + } + + /// Returns the exact package identity. + #[must_use] + pub const fn package_id(&self) -> EchoOperationPackageIdV1 { + self.package_id + } + + /// Returns the package-admission evidence identity consumed by installation. + #[must_use] + pub const fn package_admission_id(&self) -> EchoOperationPackageAdmissionIdV1 { + self.package_admission_id + } + + /// Returns the subordinate executable-program identity. + #[must_use] + pub const fn program_id(&self) -> EchoOperationProgramIdV1 { + self.program_id + } + + /// Returns the semantic identity bound by the admitted package. + #[must_use] + pub const fn semantic_identity(&self) -> Hash { + self.semantic_identity + } + + /// Returns the lawpack identity bound by the admitted package. + #[must_use] + pub const fn lawpack_identity(&self) -> Hash { + self.lawpack_identity + } + + /// Returns the exact retained package bytes. + #[must_use] + pub fn canonical_package_bytes(&self) -> &[u8] { + &self.canonical_package_bytes + } +} + +/// Stable installation refusal categories. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum EchoOperationInstallationErrorKindV1 { + /// The package id is already installed with different exact evidence. + PackageIdentityConflict, + /// The public operation coordinate is already bound to another package. + OperationCoordinateConflict, +} + +/// One structured installation refusal. +#[derive(Clone, Debug, Error, PartialEq, Eq)] +#[error("{kind:?}: {detail}")] +pub struct EchoOperationInstallationErrorV1 { + kind: EchoOperationInstallationErrorKindV1, + detail: String, +} + +impl EchoOperationInstallationErrorV1 { + /// Returns the stable refusal category. + #[must_use] + pub const fn kind(&self) -> EchoOperationInstallationErrorKindV1 { + self.kind + } +} + +pub(crate) fn admit_package_v1( + policy: &EchoOperationAdmissionPolicyV1, + canonical_package_bytes: Vec, +) -> Result { + let package_id = echo_operation_package_id_v1(&canonical_package_bytes); + if package_id != policy.expected_package_id { + return Err(admission_error( + EchoOperationAdmissionErrorKindV1::PackageIdentityMismatch, + "exact package bytes differ from the independently pinned package id", + )); + } + let package = ExecutableOperationPackageV1::from_canonical_bytes(&canonical_package_bytes) + .map_err(|artifact| EchoOperationAdmissionErrorV1 { + kind: EchoOperationAdmissionErrorKindV1::ArtifactInvalid, + detail: artifact.to_string(), + artifact: Some(artifact), + })?; + if package.operation_coordinate != policy.expected_operation_coordinate { + return Err(admission_error( + EchoOperationAdmissionErrorKindV1::OperationCoordinateMismatch, + "package operation coordinate differs from admission policy", + )); + } + if package.authority_profile_identity != policy.expected_authority_profile_identity { + return Err(admission_error( + EchoOperationAdmissionErrorKindV1::AuthorityProfileMismatch, + "package authority profile differs from admission policy", + )); + } + if !package.budget_ceiling.fits_within(policy.maximum_budget) { + return Err(admission_error( + EchoOperationAdmissionErrorKindV1::BudgetExceedsPolicy, + "package budget ceiling exceeds admission policy", + )); + } + let admission_policy_id = policy.identity(); + Ok(AdmittedExecutableOperationPackageV1 { + package, + canonical_package_bytes, + package_id, + admission_policy_id, + admission_id: package_admission_id(package_id, admission_policy_id), + admission_policy: policy.clone(), + }) +} + +pub(crate) fn installed_from_admitted( + admitted: AdmittedExecutableOperationPackageV1, +) -> Result { + let package = admitted.package; + let mut installed = InstalledEchoOperationV1 { + installed_operation_id: InstalledEchoOperationIdV1([0; 32]), + package_id: admitted.package_id, + package_admission_id: admitted.admission_id, + operation_coordinate: package.operation_coordinate, + semantic_identity: package.semantic_closure.canonical_meaning_identity, + lawpack_identity: package.semantic_closure.lawpack_identity, + target_profile_identity: package.target_profile_identity, + interpreter_profile_identity: package.interpreter_profile_identity, + intrinsic_profile_identity: package.intrinsic_profile_identity, + authority_profile_identity: package.authority_profile_identity, + application_basis_schema_identity: package.application_basis_schema_identity, + budget_ceiling: package.budget_ceiling, + program_id: package.program.identity()?, + program: package.program, + canonical_package_bytes: admitted.canonical_package_bytes, + admission_policy_id: admitted.admission_policy_id, + admission_policy: admitted.admission_policy, + }; + installed.installed_operation_id = installed_operation_id(&installed); + Ok(installed) +} + +pub(crate) fn retain_installation_v1( + installed: &InstalledEchoOperationV1, +) -> Result, EchoOperationArtifactErrorV1> { + encode_canonical_cbor_v1(&map_value([ + ("admission_policy", installed.admission_policy.to_value()), + ( + "admission_policy_id", + hash_value(installed.admission_policy_id), + ), + ( + "installed_operation_id", + hash_value(installed.installed_operation_id.as_hash()), + ), + ( + "package_bytes", + CanonicalValueV1::Bytes(installed.canonical_package_bytes.clone()), + ), + ( + "package_admission_id", + hash_value(installed.package_admission_id.as_hash()), + ), + ("package_id", hash_value(installed.package_id.as_hash())), + ("schema", text_value("echo.operation-installation/v1")), + ])) + .map_err(canonical_error) +} + +pub(crate) fn recover_installation_v1( + bytes: &[u8], +) -> Result { + let value = decode_canonical_cbor_v1(bytes).map_err(canonical_error)?; + let mut fields = exact_text_map( + value, + &[ + "admission_policy_id", + "admission_policy", + "installed_operation_id", + "package_bytes", + "package_admission_id", + "package_id", + "schema", + ], + )?; + require_text(&mut fields, "schema", "echo.operation-installation/v1")?; + let package_id = EchoOperationPackageIdV1(take_hash(&mut fields, "package_id")?); + let canonical_package_bytes = take_bytes(&mut fields, "package_bytes")?; + if echo_operation_package_id_v1(&canonical_package_bytes) != package_id { + return Err(invalid_structure( + "retained installation package identity does not match exact bytes", + )); + } + let admission_policy = + EchoOperationAdmissionPolicyV1::from_value(take_field(&mut fields, "admission_policy")?)?; + let admission_policy_id = take_hash(&mut fields, "admission_policy_id")?; + if admission_policy.identity() != admission_policy_id { + return Err(invalid_structure( + "retained package-admission policy identity mismatch", + )); + } + let package_admission_id = package_admission_id(package_id, admission_policy_id); + let retained_package_admission_id = + EchoOperationPackageAdmissionIdV1(take_hash(&mut fields, "package_admission_id")?); + if retained_package_admission_id != package_admission_id { + return Err(invalid_structure( + "retained package-admission identity does not match package and policy", + )); + } + let admitted = + admit_package_v1(&admission_policy, canonical_package_bytes).map_err(|error| { + invalid_structure(format!( + "retained executable-operation package no longer admits: {error}" + )) + })?; + if admitted.package_id != package_id || admitted.admission_id != package_admission_id { + return Err(invalid_structure( + "retained package-admission evidence does not match re-admission", + )); + } + let installed = installed_from_admitted(admitted)?; + let retained_installed_operation_id = + InstalledEchoOperationIdV1(take_hash(&mut fields, "installed_operation_id")?); + if retained_installed_operation_id != installed.installed_operation_id { + return Err(invalid_structure( + "retained installed-operation identity does not match exact installation", + )); + } + if retain_installation_v1(&installed)? != bytes { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::NonCanonical, + "installation did not reproduce the exact retained bytes", + )); + } + Ok(installed) +} + +pub(crate) fn install_recovered_v1( + packages: &mut BTreeMap, + operations: &mut BTreeMap, + installed: InstalledEchoOperationV1, +) -> Result { + if let Some(existing) = packages.get(&installed.package_id) { + if existing == &installed { + return Ok(existing.clone()); + } + return Err(installation_error( + EchoOperationInstallationErrorKindV1::PackageIdentityConflict, + "recovered package conflicts with installed exact evidence", + )); + } + if operations + .get(&installed.operation_coordinate) + .is_some_and(|existing| *existing != installed.package_id) + { + return Err(installation_error( + EchoOperationInstallationErrorKindV1::OperationCoordinateConflict, + "recovered operation coordinate conflicts with installed package", + )); + } + operations.insert(installed.operation_coordinate.clone(), installed.package_id); + packages.insert(installed.package_id, installed.clone()); + Ok(installed) +} + +/// Application-owned basis proposition carried inside the exact Echo basis. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct EchoOperationApplicationBasisV1 { + schema_identity: Hash, + value_identity: Hash, +} + +impl EchoOperationApplicationBasisV1 { + /// Creates one typed application basis proposition. + #[must_use] + pub const fn new(schema_identity: Hash, value_identity: Hash) -> Self { + Self { + schema_identity, + value_identity, + } + } + + /// Returns its schema identity. + #[must_use] + pub const fn schema_identity(self) -> Hash { + self.schema_identity + } + + /// Returns its value identity. + #[must_use] + pub const fn value_identity(self) -> Hash { + self.value_identity + } +} + +/// Exact parent basis against which Echo evaluated a prepared operation. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct EchoOperationEvaluationBasisV1 { + writer_head: WriterHeadKey, + worldline_tick: WorldlineTick, + commit_global_tick: Option, + state_root: Hash, + commit_id: Hash, + application_basis: EchoOperationApplicationBasisV1, +} + +impl EchoOperationEvaluationBasisV1 { + pub(crate) fn new( + writer_head: WriterHeadKey, + worldline_tick: WorldlineTick, + commit_global_tick: Option, + state_root: Hash, + commit_id: Hash, + application_basis: EchoOperationApplicationBasisV1, + ) -> Self { + Self { + writer_head, + worldline_tick, + commit_global_tick, + state_root, + commit_id, + application_basis, + } + } + + /// Returns the writer head whose frontier is named. + #[must_use] + pub const fn writer_head(self) -> WriterHeadKey { + self.writer_head + } + + /// Returns the exact worldline tick. + #[must_use] + pub const fn worldline_tick(self) -> WorldlineTick { + self.worldline_tick + } + + /// Returns the committing global tick, or `None` for U0. + #[must_use] + pub const fn commit_global_tick(self) -> Option { + self.commit_global_tick + } + + /// Returns the exact state root. + #[must_use] + pub const fn state_root(self) -> Hash { + self.state_root + } + + /// Returns the exact commit identity (including the U0 derived identity). + #[must_use] + pub const fn commit_id(self) -> Hash { + self.commit_id + } + + /// Returns the application-owned basis proposition. + #[must_use] + pub const fn application_basis(self) -> EchoOperationApplicationBasisV1 { + self.application_basis + } + + /// Returns the stable identity of every basis field in the ADR-defined order. + #[must_use] + pub fn identity(self) -> EchoOperationEvaluationBasisIdV1 { + let mut hasher = Hasher::new(); + hasher.update(BASIS_ID_DOMAIN); + hasher.update(self.writer_head.worldline_id.as_bytes()); + hasher.update(self.writer_head.head_id.as_bytes()); + hasher.update(&self.worldline_tick.as_u64().to_le_bytes()); + match self.commit_global_tick { + None => { + hasher.update(&[0]); + } + Some(tick) => { + hasher.update(&[1]); + hasher.update(&tick.as_u64().to_le_bytes()); + } + } + hasher.update(&self.state_root); + hasher.update(&self.commit_id); + hasher.update(&self.application_basis.schema_identity); + hasher.update(&self.application_basis.value_identity); + EchoOperationEvaluationBasisIdV1(hasher.finalize().into()) + } + + fn to_value(self) -> CanonicalValueV1 { + map_value([ + ( + "application_basis_schema_identity", + hash_value(self.application_basis.schema_identity), + ), + ( + "application_basis_value_identity", + hash_value(self.application_basis.value_identity), + ), + ("commit_id", hash_value(self.commit_id)), + ( + "commit_global_tick", + self.commit_global_tick + .map_or(CanonicalValueV1::Null, |tick| uint_value(tick.as_u64())), + ), + ("head_id", hash_value(*self.writer_head.head_id.as_bytes())), + ("state_root", hash_value(self.state_root)), + ( + "worldline_id", + hash_value(*self.writer_head.worldline_id.as_bytes()), + ), + ("worldline_tick", uint_value(self.worldline_tick.as_u64())), + ]) + } + + fn from_value(value: CanonicalValueV1) -> Result { + let mut fields = exact_text_map( + value, + &[ + "application_basis_schema_identity", + "application_basis_value_identity", + "commit_global_tick", + "commit_id", + "head_id", + "state_root", + "worldline_id", + "worldline_tick", + ], + )?; + let commit_global_tick = match take_field(&mut fields, "commit_global_tick")? { + CanonicalValueV1::Null => None, + CanonicalValueV1::Integer(value) => Some(GlobalTick::from_raw(i128_to_u64(value)?)), + _ => return Err(invalid_structure("commit_global_tick must be null or uint")), + }; + Ok(Self { + writer_head: WriterHeadKey { + worldline_id: crate::WorldlineId::from_bytes(take_hash( + &mut fields, + "worldline_id", + )?), + head_id: crate::HeadId::from_bytes(take_hash(&mut fields, "head_id")?), + }, + worldline_tick: WorldlineTick::from_raw(take_u64(&mut fields, "worldline_tick")?), + commit_global_tick, + state_root: take_hash(&mut fields, "state_root")?, + commit_id: take_hash(&mut fields, "commit_id")?, + application_basis: EchoOperationApplicationBasisV1::new( + take_hash(&mut fields, "application_basis_schema_identity")?, + take_hash(&mut fields, "application_basis_value_identity")?, + ), + }) + } +} + +/// Canonical basis-bearing invocation emitted by a generated client/helper. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EchoOperationInvocationV1 { + package_id: EchoOperationPackageIdV1, + operation_coordinate: String, + evaluation_basis: EchoOperationEvaluationBasisV1, + authority_grant_identity: Hash, + delegated_budget: EchoOperationBudgetV1, + node: NodeKey, + expected_value_digest: Hash, + replacement_bytes: Vec, +} + +impl EchoOperationInvocationV1 { + /// Creates an invocation for the first supported operation program. + #[allow(clippy::too_many_arguments)] + #[must_use] + pub fn anchored_node_attachment_compare_and_set( + package_id: EchoOperationPackageIdV1, + operation_coordinate: impl Into, + evaluation_basis: EchoOperationEvaluationBasisV1, + authority_grant_identity: Hash, + delegated_budget: EchoOperationBudgetV1, + node: NodeKey, + expected_value_digest: Hash, + replacement_bytes: Vec, + ) -> Self { + Self { + package_id, + operation_coordinate: operation_coordinate.into(), + evaluation_basis, + authority_grant_identity, + delegated_budget, + node, + expected_value_digest, + replacement_bytes, + } + } + + /// Returns the canonical invocation identity. + pub fn identity(&self) -> Result { + Ok(EchoOperationInvocationIdV1(domain_hash( + INVOCATION_ID_DOMAIN, + &self.to_canonical_bytes()?, + ))) + } + + /// Encodes exact invocation bytes using Edict's canonical CBOR profile. + pub fn to_canonical_bytes(&self) -> Result, EchoOperationArtifactErrorV1> { + if self.operation_coordinate.is_empty() { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::EmptyOperationCoordinate, + "invocation operation coordinate must not be empty", + )); + } + if !self.delegated_budget.is_nonzero() { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::InvalidBudget, + "invocation delegated step budget must be nonzero", + )); + } + let value = map_value([ + ( + "authority_grant_identity", + hash_value(self.authority_grant_identity), + ), + ("delegated_budget", self.delegated_budget.to_value()), + ("evaluation_basis", self.evaluation_basis.to_value()), + ( + "expected_value_digest", + hash_value(self.expected_value_digest), + ), + ("node_id", hash_value(self.node.local_id.0)), + ( + "operation_coordinate", + text_value(&self.operation_coordinate), + ), + ("package_id", hash_value(self.package_id.as_hash())), + ( + "replacement_bytes", + CanonicalValueV1::Bytes(self.replacement_bytes.clone()), + ), + ("schema", text_value(INVOCATION_SCHEMA)), + ("warp_id", hash_value(self.node.warp_id.0)), + ]); + encode_canonical_cbor_v1(&value).map_err(canonical_error) + } + + fn from_canonical_bytes(bytes: &[u8]) -> Result { + let value = decode_canonical_cbor_v1(bytes).map_err(canonical_error)?; + let mut fields = exact_text_map( + value, + &[ + "authority_grant_identity", + "delegated_budget", + "evaluation_basis", + "expected_value_digest", + "node_id", + "operation_coordinate", + "package_id", + "replacement_bytes", + "schema", + "warp_id", + ], + )?; + require_text(&mut fields, "schema", INVOCATION_SCHEMA)?; + let invocation = Self { + package_id: EchoOperationPackageIdV1(take_hash(&mut fields, "package_id")?), + operation_coordinate: take_text(&mut fields, "operation_coordinate")?, + evaluation_basis: EchoOperationEvaluationBasisV1::from_value(take_field( + &mut fields, + "evaluation_basis", + )?)?, + authority_grant_identity: take_hash(&mut fields, "authority_grant_identity")?, + delegated_budget: EchoOperationBudgetV1::from_value(take_field( + &mut fields, + "delegated_budget", + )?)?, + node: NodeKey { + warp_id: crate::WarpId(take_hash(&mut fields, "warp_id")?), + local_id: crate::NodeId(take_hash(&mut fields, "node_id")?), + }, + expected_value_digest: take_hash(&mut fields, "expected_value_digest")?, + replacement_bytes: take_bytes(&mut fields, "replacement_bytes")?, + }; + if invocation.operation_coordinate.is_empty() || !invocation.delegated_budget.is_nonzero() { + return Err(invalid_structure( + "invocation coordinate and delegated step budget must be nonempty", + )); + } + if invocation.to_canonical_bytes()? != bytes { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::NonCanonical, + "invocation did not reproduce the exact admitted bytes", + )); + } + Ok(invocation) + } +} + +/// Runtime-owner invocation policy, separate from authored invocation bytes. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct EchoOperationInvocationAdmissionPolicyV1 { + authority_profile_identity: Hash, + authority_grant_identity: Hash, + maximum_delegated_budget: EchoOperationBudgetV1, +} + +impl EchoOperationInvocationAdmissionPolicyV1 { + /// Creates one independently supplied invocation policy. + #[must_use] + pub const fn new( + authority_profile_identity: Hash, + authority_grant_identity: Hash, + maximum_delegated_budget: EchoOperationBudgetV1, + ) -> Self { + Self { + authority_profile_identity, + authority_grant_identity, + maximum_delegated_budget, + } + } + + fn identity(self) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(b"echo:operation-invocation-admission-policy:v1\0"); + hasher.update(&self.authority_profile_identity); + hasher.update(&self.authority_grant_identity); + hash_budget(&mut hasher, self.maximum_delegated_budget); + hasher.finalize().into() + } +} + +/// Stable invocation-admission refusal categories. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum EchoOperationInvocationAdmissionErrorKindV1 { + /// Invocation bytes were malformed or noncanonical. + MalformedInvocation, + /// No admitted executable operation package is installed under the claimed id. + OperationUnavailable, + /// The invocation's public operation coordinate disagrees with the package. + OperationCoordinateMismatch, + /// The runtime-owned authority profile disagrees with the package. + AuthorityProfileMismatch, + /// The invocation's authority grant was not admitted by runtime policy. + AuthorityGrantMismatch, + /// Delegated budget exceeded the installed package or runtime policy. + BudgetExceeded, + /// The invocation named a basis other than the current exact parent basis. + BasisMismatch, +} + +/// One structured invocation-admission refusal. +#[derive(Clone, Debug, Error, PartialEq, Eq)] +#[error("{kind:?}: {detail}")] +pub struct EchoOperationInvocationAdmissionErrorV1 { + kind: EchoOperationInvocationAdmissionErrorKindV1, + detail: String, +} + +impl EchoOperationInvocationAdmissionErrorV1 { + /// Returns the stable refusal category. + #[must_use] + pub const fn kind(&self) -> EchoOperationInvocationAdmissionErrorKindV1 { + self.kind + } +} + +/// Opaque evidence that Echo admitted an installed, basis-bearing invocation. +#[derive(Clone, Debug)] +pub struct AdmittedEchoOperationInvocationV1 { + invocation: EchoOperationInvocationV1, + invocation_id: EchoOperationInvocationIdV1, + canonical_invocation_bytes: Vec, + admission_policy_id: Hash, + admission_id: EchoOperationInvocationAdmissionIdV1, + installed_operation_id: InstalledEchoOperationIdV1, + evaluation_authority: EchoOperationEvaluationAuthorityV1, + admission_policy: EchoOperationInvocationAdmissionPolicyV1, +} + +impl AdmittedEchoOperationInvocationV1 { + pub(crate) const fn package_id(&self) -> EchoOperationPackageIdV1 { + self.invocation.package_id + } + + pub(crate) const fn evaluation_basis(&self) -> EchoOperationEvaluationBasisV1 { + self.invocation.evaluation_basis + } +} + +pub(crate) fn admit_invocation_v1( + installed: Option<&InstalledEchoOperationV1>, + policy: EchoOperationInvocationAdmissionPolicyV1, + canonical_invocation_bytes: &[u8], + current_basis: EchoOperationEvaluationBasisV1, + current_state: &WorldlineState, + evaluation_authority: EchoOperationEvaluationAuthorityV1, +) -> Result { + let invocation = EchoOperationInvocationV1::from_canonical_bytes(canonical_invocation_bytes) + .map_err(|error| { + invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::MalformedInvocation, + error.to_string(), + ) + })?; + let installed = installed.ok_or_else(|| { + invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::OperationUnavailable, + "claimed executable operation package is not installed", + ) + })?; + if invocation.operation_coordinate != installed.operation_coordinate { + return Err(invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::OperationCoordinateMismatch, + "invocation operation coordinate differs from installed package", + )); + } + if policy.authority_profile_identity != installed.authority_profile_identity { + return Err(invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::AuthorityProfileMismatch, + "runtime authority profile differs from installed package", + )); + } + if invocation.authority_grant_identity != policy.authority_grant_identity { + return Err(invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::AuthorityGrantMismatch, + "invocation authority grant was not admitted by runtime policy", + )); + } + if !invocation + .delegated_budget + .fits_within(installed.budget_ceiling) + || !invocation + .delegated_budget + .fits_within(policy.maximum_delegated_budget) + || !installed + .program + .minimum_budget() + .fits_within(invocation.delegated_budget) + { + return Err(invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::BudgetExceeded, + "delegated budget is below the program minimum or exceeds an admitted ceiling", + )); + } + if invocation.evaluation_basis != current_basis { + return Err(invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::BasisMismatch, + "invocation does not name the current exact parent basis", + )); + } + if invocation + .evaluation_basis + .application_basis + .schema_identity + != installed.application_basis_schema_identity + { + return Err(invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::BasisMismatch, + "invocation application-basis schema differs from installed package", + )); + } + let current_application_basis = + current_application_basis(installed, &invocation, current_state)?; + if invocation.evaluation_basis.application_basis != current_application_basis { + return Err(invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::BasisMismatch, + "invocation application basis differs from Echo's current graph proposition", + )); + } + let invocation_id = EchoOperationInvocationIdV1(domain_hash( + INVOCATION_ID_DOMAIN, + canonical_invocation_bytes, + )); + let admission_policy_id = policy.identity(); + let installed_operation_id = installed.installed_operation_id; + Ok(AdmittedEchoOperationInvocationV1 { + invocation, + invocation_id, + canonical_invocation_bytes: canonical_invocation_bytes.to_vec(), + admission_policy_id, + admission_id: invocation_admission_id( + installed_operation_id, + invocation_id, + admission_policy_id, + current_basis.identity(), + ), + installed_operation_id, + evaluation_authority, + admission_policy: policy, + }) +} + +fn current_application_basis( + installed: &InstalledEchoOperationV1, + invocation: &EchoOperationInvocationV1, + state: &WorldlineState, +) -> Result { + let basis_mismatch = |detail| { + invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::BasisMismatch, + detail, + ) + }; + match installed.program { + EchoOperationProgramV1::AnchoredNodeAttachmentCompareAndSet { .. } => { + let store = state + .store(&invocation.node.warp_id) + .ok_or_else(|| basis_mismatch("application-basis warp is unavailable"))?; + store + .node(&invocation.node.local_id) + .ok_or_else(|| basis_mismatch("application-basis node is unavailable"))?; + let attachment = store + .node_attachment(&invocation.node.local_id) + .ok_or_else(|| basis_mismatch("application-basis attachment is unavailable"))?; + let AttachmentValue::Atom(atom) = attachment else { + return Err(basis_mismatch( + "application-basis attachment is not a canonical atom", + )); + }; + let atom_len = u64::try_from(atom.bytes.len()).map_err(|_| { + invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::BudgetExceeded, + "application-basis attachment length exceeds the v1 budget domain", + ) + })?; + let required_read_bytes = 64_u64.checked_add(atom_len).ok_or_else(|| { + invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::BudgetExceeded, + "application-basis read requirement overflowed", + ) + })?; + if required_read_bytes > invocation.delegated_budget.read_bytes { + return Err(invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::BudgetExceeded, + "application-basis corroboration exceeds the delegated read budget", + )); + } + Ok(echo_operation_anchored_node_application_basis_v1( + invocation.node, + atom.type_id, + &atom.bytes, + )) + } + } +} + +pub(crate) fn decode_invocation_route_v1( + canonical_invocation_bytes: &[u8], +) -> Result< + (EchoOperationPackageIdV1, EchoOperationEvaluationBasisV1), + EchoOperationInvocationAdmissionErrorV1, +> { + let invocation = EchoOperationInvocationV1::from_canonical_bytes(canonical_invocation_bytes) + .map_err(|error| { + invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::MalformedInvocation, + error.to_string(), + ) + })?; + Ok((invocation.package_id, invocation.evaluation_basis)) +} + +pub(crate) fn invocation_runtime_error( + detail: impl Into, +) -> EchoOperationInvocationAdmissionErrorV1 { + invocation_admission_error( + EchoOperationInvocationAdmissionErrorKindV1::BasisMismatch, + detail, + ) +} + +pub(crate) fn runtime_basis_obstruction( + admitted: AdmittedEchoOperationInvocationV1, +) -> EchoOperationPreparationV1 { + EchoOperationPreparationV1::Obstructed(EchoOperationObstructionV1 { + kind: EchoOperationObstructionKindV1::BasisChanged, + package_id: admitted.invocation.package_id, + installed_operation_id: admitted.installed_operation_id, + invocation_admission_id: admitted.admission_id, + invocation_id: admitted.invocation_id, + evaluation_basis_id: admitted.invocation.evaluation_basis.identity(), + }) +} + +/// Stable private-evaluation obstruction categories. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum EchoOperationObstructionKindV1 { + /// The installed operation changed or disappeared after admission. + OperationUnavailable, + /// Admission belongs to another Echo runtime owner. + EvaluationAuthorityMismatch, + /// The parent basis changed before or during preparation. + BasisChanged, + /// The delegated budget could not cover deterministic evaluation. + BudgetExceeded, + /// The anchored node is absent. + NodeMissing, + /// The anchored node has a different skeleton type. + NodeTypeMismatch, + /// The anchored node has no alpha attachment. + AttachmentMissing, + /// The alpha attachment is descended rather than an atom. + AttachmentNotAtom, + /// The alpha atom has a different declared type. + AttachmentTypeMismatch, + /// The current atom digest differs from the invocation precondition. + PreconditionMismatch, + /// Actual resource access exceeded the declared footprint contract. + FootprintViolation, + /// The program rejected a replacement outside its bound. + ReplacementTooLarge, +} + +/// One typed obstruction. Obstruction never carries a parent-visible patch. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EchoOperationObstructionV1 { + kind: EchoOperationObstructionKindV1, + package_id: EchoOperationPackageIdV1, + installed_operation_id: InstalledEchoOperationIdV1, + invocation_admission_id: EchoOperationInvocationAdmissionIdV1, + invocation_id: EchoOperationInvocationIdV1, + evaluation_basis_id: EchoOperationEvaluationBasisIdV1, +} + +impl EchoOperationObstructionV1 { + /// Returns the stable obstruction category. + #[must_use] + pub const fn kind(&self) -> EchoOperationObstructionKindV1 { + self.kind + } + + /// Returns the exact installed operation against which evaluation was attempted. + #[must_use] + pub const fn installed_operation_id(&self) -> InstalledEchoOperationIdV1 { + self.installed_operation_id + } + + /// Returns the exact runtime admission that authorized this evaluation attempt. + #[must_use] + pub const fn invocation_admission_id(&self) -> EchoOperationInvocationAdmissionIdV1 { + self.invocation_admission_id + } + + /// Returns the identity of this typed no-parent-patch obstruction. + #[must_use] + pub fn identity(&self) -> EchoOperationObstructionIdV1 { + let mut hasher = Hasher::new(); + hasher.update(OBSTRUCTION_ID_DOMAIN); + hasher.update(&self.package_id.as_hash()); + hasher.update(&self.installed_operation_id.as_hash()); + hasher.update(&self.invocation_admission_id.as_hash()); + hasher.update(&self.invocation_id.as_hash()); + hasher.update(&self.evaluation_basis_id.as_hash()); + hasher.update(&[obstruction_kind_code(self.kind)]); + EchoOperationObstructionIdV1(hasher.finalize().into()) + } +} + +/// Private evaluation result: exactly one complete preparation or one obstruction. +#[derive(Clone, Debug)] +pub enum EchoOperationPreparationV1 { + /// A complete, privately evaluated patch that may be offered for exact-basis commit. + Prepared(Box), + /// A typed obstruction with no parent-visible patch. + Obstructed(EchoOperationObstructionV1), +} + +/// Complete private evaluation, bound to all substitution and resource evidence. +#[derive(Clone, Debug)] +pub struct PreparedEchoOperationV1 { + installed: InstalledEchoOperationV1, + invocation: EchoOperationInvocationV1, + invocation_id: EchoOperationInvocationIdV1, + canonical_invocation_bytes: Vec, + invocation_admission_policy_id: Hash, + invocation_admission_maximum_budget: EchoOperationBudgetV1, + invocation_admission_id: EchoOperationInvocationAdmissionIdV1, + evaluation_basis: EchoOperationEvaluationBasisV1, + declared_footprint: Footprint, + actual_footprint: Footprint, + declared_footprint_digest: Hash, + actual_footprint_digest: Hash, + consumed_budget: EchoOperationBudgetV1, + patch: WarpTickPatchV1, + result_id: EchoOperationResultIdV1, + private_evaluation_id: EchoOperationPrivateEvaluationIdV1, + preparation_id: PreparedEchoOperationIdV1, + evaluation_authority: EchoOperationEvaluationAuthorityV1, +} + +impl PreparedEchoOperationV1 { + /// Returns the exact basis on which private evaluation occurred. + #[must_use] + pub const fn evaluation_basis(&self) -> &EchoOperationEvaluationBasisV1 { + &self.evaluation_basis + } + + /// Returns the invocation-derived declared footprint. + #[must_use] + pub const fn declared_footprint(&self) -> &Footprint { + &self.declared_footprint + } + + /// Returns the evaluator-recorded actual footprint. + #[must_use] + pub const fn actual_footprint(&self) -> &Footprint { + &self.actual_footprint + } + + /// Returns the complete parent-visible patch produced in private evaluation. + #[must_use] + pub const fn patch(&self) -> &WarpTickPatchV1 { + &self.patch + } + + /// Returns the exact invocation-admission evidence consumed by evaluation. + #[must_use] + pub const fn invocation_admission_id(&self) -> EchoOperationInvocationAdmissionIdV1 { + self.invocation_admission_id + } + + /// Returns the declared-footprint identity bound by evaluation. + #[must_use] + pub const fn declared_footprint_digest(&self) -> Hash { + self.declared_footprint_digest + } + + /// Returns the evaluator-recorded actual-footprint identity. + #[must_use] + pub const fn actual_footprint_digest(&self) -> Hash { + self.actual_footprint_digest + } + + /// Returns the invocation's admitted delegated budget. + #[must_use] + pub const fn delegated_budget(&self) -> EchoOperationBudgetV1 { + self.invocation.delegated_budget + } + + /// Returns the resource budget consumed during private evaluation. + #[must_use] + pub const fn consumed_budget(&self) -> EchoOperationBudgetV1 { + self.consumed_budget + } + + /// Returns the bounded private-evaluation identity. + #[must_use] + pub const fn private_evaluation_id(&self) -> EchoOperationPrivateEvaluationIdV1 { + self.private_evaluation_id + } + + /// Returns this complete committable preparation's identity. + #[must_use] + pub const fn preparation_id(&self) -> PreparedEchoOperationIdV1 { + self.preparation_id + } + + /// Returns the typed result identity produced by evaluation. + #[must_use] + pub const fn result_id(&self) -> EchoOperationResultIdV1 { + self.result_id + } + + pub(crate) const fn package_id(&self) -> EchoOperationPackageIdV1 { + self.installed.package_id + } + + pub(crate) const fn installed_operation_id(&self) -> InstalledEchoOperationIdV1 { + self.installed.installed_operation_id + } + + pub(crate) fn is_owned_by(&self, authority: &EchoOperationEvaluationAuthorityV1) -> bool { + self.evaluation_authority.same_owner(authority) + } +} + +pub(crate) fn prepare_operation_v1( + installed: Option<&InstalledEchoOperationV1>, + admitted: AdmittedEchoOperationInvocationV1, + current_basis: EchoOperationEvaluationBasisV1, + state: &WorldlineState, + policy_id: u32, + evaluation_authority: &EchoOperationEvaluationAuthorityV1, +) -> EchoOperationPreparationV1 { + let package_id = admitted.invocation.package_id; + let invocation_id = admitted.invocation_id; + let obstruction = |kind| { + EchoOperationPreparationV1::Obstructed(EchoOperationObstructionV1 { + kind, + package_id, + installed_operation_id: admitted.installed_operation_id, + invocation_admission_id: admitted.admission_id, + invocation_id, + evaluation_basis_id: admitted.invocation.evaluation_basis.identity(), + }) + }; + let Some(installed) = installed else { + return obstruction(EchoOperationObstructionKindV1::OperationUnavailable); + }; + if installed.package_id != package_id + || installed.operation_coordinate != admitted.invocation.operation_coordinate + || installed.installed_operation_id != admitted.installed_operation_id + { + return obstruction(EchoOperationObstructionKindV1::OperationUnavailable); + } + if !admitted + .evaluation_authority + .same_owner(evaluation_authority) + { + return obstruction(EchoOperationObstructionKindV1::EvaluationAuthorityMismatch); + } + if admitted.invocation.evaluation_basis != current_basis { + return obstruction(EchoOperationObstructionKindV1::BasisChanged); + } + + match installed.program { + EchoOperationProgramV1::AnchoredNodeAttachmentCompareAndSet { + required_node_type, + required_attachment_type, + max_replacement_bytes, + } => { + let Ok(replacement_len) = u64::try_from(admitted.invocation.replacement_bytes.len()) + else { + return obstruction(EchoOperationObstructionKindV1::ReplacementTooLarge); + }; + if replacement_len > max_replacement_bytes { + return obstruction(EchoOperationObstructionKindV1::ReplacementTooLarge); + } + let node = admitted.invocation.node; + let declared_footprint = anchored_node_footprint(node); + let mut actual_footprint = Footprint::default(); + let mut budget_meter = + EchoOperationBudgetMeterV1::new(admitted.invocation.delegated_budget); + let Some(store) = state.store(&node.warp_id) else { + return obstruction(EchoOperationObstructionKindV1::NodeMissing); + }; + if !budget_meter.charge(1, 32, 0) { + return obstruction(EchoOperationObstructionKindV1::BudgetExceeded); + } + record_node_read(&mut actual_footprint, node); + let Some(record) = store.node(&node.local_id) else { + return obstruction(EchoOperationObstructionKindV1::NodeMissing); + }; + if record.ty != required_node_type { + return obstruction(EchoOperationObstructionKindV1::NodeTypeMismatch); + } + let slot = AttachmentKey::node_alpha(node); + if !budget_meter.charge(1, 32, 0) { + return obstruction(EchoOperationObstructionKindV1::BudgetExceeded); + } + actual_footprint.a_read.insert(slot); + let Some(attachment) = store.node_attachment(&node.local_id) else { + return obstruction(EchoOperationObstructionKindV1::AttachmentMissing); + }; + let AttachmentValue::Atom(atom) = attachment else { + return obstruction(EchoOperationObstructionKindV1::AttachmentNotAtom); + }; + if atom.type_id != required_attachment_type { + return obstruction(EchoOperationObstructionKindV1::AttachmentTypeMismatch); + } + let Ok(current_value_len) = u64::try_from(atom.bytes.len()) else { + return obstruction(EchoOperationObstructionKindV1::BudgetExceeded); + }; + if !budget_meter.charge(1, current_value_len, 0) { + return obstruction(EchoOperationObstructionKindV1::BudgetExceeded); + } + if echo_operation_atom_value_digest_v1(atom.type_id, &atom.bytes) + != admitted.invocation.expected_value_digest + { + return obstruction(EchoOperationObstructionKindV1::PreconditionMismatch); + } + let Some(write_bytes) = 32_u64.checked_add(replacement_len) else { + return obstruction(EchoOperationObstructionKindV1::BudgetExceeded); + }; + if !budget_meter.charge(1, 0, write_bytes) { + return obstruction(EchoOperationObstructionKindV1::BudgetExceeded); + } + let consumed_budget = budget_meter.consumed(); + actual_footprint.a_write.insert(slot); + if actual_footprint != declared_footprint { + return obstruction(EchoOperationObstructionKindV1::FootprintViolation); + } + let patch = WarpTickPatchV1::new( + policy_id, + installed.installed_operation_id.as_hash(), + TickCommitStatus::Committed, + vec![SlotId::Node(node), SlotId::Attachment(slot)], + vec![SlotId::Attachment(slot)], + vec![WarpOp::SetAttachment { + key: slot, + value: Some(AttachmentValue::Atom(AtomPayload::new( + required_attachment_type, + Bytes::from(admitted.invocation.replacement_bytes.clone()), + ))), + }], + ); + let declared_footprint_digest = footprint_digest(&declared_footprint); + let actual_footprint_digest = footprint_digest(&actual_footprint); + let replacement_value_digest = echo_operation_atom_value_digest_v1( + required_attachment_type, + &admitted.invocation.replacement_bytes, + ); + let result_id = operation_result_id( + installed, + &admitted.invocation, + admitted.invocation.expected_value_digest, + replacement_value_digest, + patch.digest(), + ); + let private_evaluation_id = private_evaluation_id_from_parts( + installed.installed_operation_id, + installed.program_id, + admitted.admission_id, + admitted.invocation_id, + current_basis.identity(), + declared_footprint_digest, + actual_footprint_digest, + consumed_budget, + patch.digest(), + result_id, + ); + let preparation_id = preparation_id(private_evaluation_id, patch.digest(), result_id); + EchoOperationPreparationV1::Prepared(Box::new(PreparedEchoOperationV1 { + installed: installed.clone(), + invocation: admitted.invocation, + invocation_id, + canonical_invocation_bytes: admitted.canonical_invocation_bytes, + invocation_admission_policy_id: admitted.admission_policy_id, + invocation_admission_maximum_budget: admitted + .admission_policy + .maximum_delegated_budget, + invocation_admission_id: admitted.admission_id, + evaluation_basis: current_basis, + declared_footprint, + actual_footprint, + declared_footprint_digest, + actual_footprint_digest, + consumed_budget, + patch, + result_id, + private_evaluation_id, + preparation_id, + evaluation_authority: admitted.evaluation_authority, + })) + } + } +} + +/// Terminal posture bound by an executable-operation receipt. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum EchoOperationTerminalPostureV1 { + /// The exact prepared patch entered the parent worldline. + Committed, + /// The parent basis changed; no prepared patch entered history. + NotCommittedBasisChanged, + /// The admitted package was unavailable at the commit crossing. + NotCommittedInstallationUnavailable, + /// Private evaluation belongs to another Echo runtime owner. + NotCommittedEvaluationAuthorityMismatch, +} + +/// Typed receipt for the exact executable semantics and terminal outcome. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EchoOperationReceiptV1 { + package_id: EchoOperationPackageIdV1, + package_admission_id: EchoOperationPackageAdmissionIdV1, + installed_operation_id: InstalledEchoOperationIdV1, + operation_coordinate: String, + semantic_identity: Hash, + lawpack_identity: Hash, + target_profile_identity: Hash, + interpreter_profile_identity: Hash, + intrinsic_profile_identity: Hash, + package_admission_policy_id: Hash, + authority_profile_identity: Hash, + authority_grant_identity: Hash, + invocation_admission_policy_id: Hash, + invocation_admission_maximum_budget: EchoOperationBudgetV1, + invocation_admission_id: EchoOperationInvocationAdmissionIdV1, + program_id: EchoOperationProgramIdV1, + invocation_id: EchoOperationInvocationIdV1, + invocation_bytes_digest: Hash, + evaluation_basis: EchoOperationEvaluationBasisV1, + evaluation_basis_id: EchoOperationEvaluationBasisIdV1, + declared_footprint_digest: Hash, + actual_footprint_digest: Hash, + delegated_budget: EchoOperationBudgetV1, + consumed_budget: EchoOperationBudgetV1, + private_evaluation_id: EchoOperationPrivateEvaluationIdV1, + preparation_id: PreparedEchoOperationIdV1, + prepared_patch_digest: Hash, + prepared_result_id: EchoOperationResultIdV1, + committed_patch_digest: Option, + committed_result_id: Option, + state_root_before: Hash, + state_root_after: Hash, + commit_id: Hash, + composition_digest: Option, + tick_receipt_digest: Hash, + commit_global_tick: Option, + worldline_tick_after: WorldlineTick, + terminal_posture: EchoOperationTerminalPostureV1, + terminal_outcome_digest: Hash, + receipt_digest: Hash, +} + +impl EchoOperationReceiptV1 { + /// Returns the terminal disposition. + #[must_use] + pub const fn terminal_posture(&self) -> EchoOperationTerminalPostureV1 { + self.terminal_posture + } + + /// Returns the admitted package identity. + #[must_use] + pub const fn package_id(&self) -> EchoOperationPackageIdV1 { + self.package_id + } + + /// Returns Echo's package-admission evidence identity. + #[must_use] + pub const fn package_admission_id(&self) -> EchoOperationPackageAdmissionIdV1 { + self.package_admission_id + } + + /// Returns Echo's installed-operation identity. + #[must_use] + pub const fn installed_operation_id(&self) -> InstalledEchoOperationIdV1 { + self.installed_operation_id + } + + /// Returns the public Edict operation coordinate. + #[must_use] + pub fn operation_coordinate(&self) -> &str { + &self.operation_coordinate + } + + /// Returns the canonical invocation identity admitted by Echo. + #[must_use] + pub const fn invocation_id(&self) -> EchoOperationInvocationIdV1 { + self.invocation_id + } + + /// Returns the separately domain-bound digest of canonical invocation bytes. + #[must_use] + pub const fn invocation_bytes_digest(&self) -> Hash { + self.invocation_bytes_digest + } + + /// Returns the exact program identity subordinate to the package. + #[must_use] + pub const fn program_id(&self) -> EchoOperationProgramIdV1 { + self.program_id + } + + /// Returns the exact private-evaluation basis identity. + #[must_use] + pub const fn evaluation_basis_id(&self) -> EchoOperationEvaluationBasisIdV1 { + self.evaluation_basis_id + } + + /// Returns the exact complete basis value used during private evaluation. + #[must_use] + pub const fn evaluation_basis(&self) -> EchoOperationEvaluationBasisV1 { + self.evaluation_basis + } + + /// Returns the invocation-derived declared-footprint identity. + #[must_use] + pub const fn declared_footprint_digest(&self) -> Hash { + self.declared_footprint_digest + } + + /// Returns the evaluator-recorded actual-footprint identity. + #[must_use] + pub const fn actual_footprint_digest(&self) -> Hash { + self.actual_footprint_digest + } + + /// Returns the invocation's admitted delegated budget. + #[must_use] + pub const fn delegated_budget(&self) -> EchoOperationBudgetV1 { + self.delegated_budget + } + + /// Returns the resource budget consumed during private evaluation. + #[must_use] + pub const fn consumed_budget(&self) -> EchoOperationBudgetV1 { + self.consumed_budget + } + + /// Returns the invocation-admission evidence consumed by evaluation. + #[must_use] + pub const fn invocation_admission_id(&self) -> EchoOperationInvocationAdmissionIdV1 { + self.invocation_admission_id + } + + /// Returns the bounded private-evaluation identity. + #[must_use] + pub const fn private_evaluation_id(&self) -> EchoOperationPrivateEvaluationIdV1 { + self.private_evaluation_id + } + + /// Returns the complete preparation identity. + #[must_use] + pub const fn preparation_id(&self) -> PreparedEchoOperationIdV1 { + self.preparation_id + } + + /// Returns the typed result produced during private evaluation. + #[must_use] + pub const fn prepared_result_id(&self) -> EchoOperationResultIdV1 { + self.prepared_result_id + } + + /// Returns the typed result only when it entered the committed consequence. + #[must_use] + pub const fn committed_result_id(&self) -> Option { + self.committed_result_id + } + + /// Returns the parent-visible patch digest only for a committed consequence. + #[must_use] + pub const fn committed_patch_digest(&self) -> Option { + self.committed_patch_digest + } + + /// Returns the evaluated candidate patch identity, committed or not. + #[must_use] + pub const fn prepared_patch_digest(&self) -> Hash { + self.prepared_patch_digest + } + + /// Returns singleton composition evidence only for a committed consequence. + #[must_use] + pub const fn composition_digest(&self) -> Option { + self.composition_digest + } + + /// Returns the closed terminal-outcome identity. + #[must_use] + pub const fn terminal_outcome_digest(&self) -> Hash { + self.terminal_outcome_digest + } + + /// Returns the graph-state root at the terminal commit crossing's start. + #[must_use] + pub const fn state_root_before(&self) -> Hash { + self.state_root_before + } + + /// Returns the graph-state root after the terminal crossing. + #[must_use] + pub const fn state_root_after(&self) -> Hash { + self.state_root_after + } + + /// Returns the committed consequence identity, or the zero sentinel when not committed. + #[must_use] + pub const fn commit_id(&self) -> Hash { + self.commit_id + } + + /// Returns the commit's global tick, or `None` when no consequence committed. + #[must_use] + pub const fn commit_global_tick(&self) -> Option { + self.commit_global_tick + } + + /// Returns the worldline frontier tick after the terminal crossing. + #[must_use] + pub const fn worldline_tick_after(&self) -> WorldlineTick { + self.worldline_tick_after + } + + /// Returns the content identity of this typed receipt. + #[must_use] + pub const fn digest(&self) -> Hash { + self.receipt_digest + } + + pub(crate) const fn tick_receipt_digest(&self) -> Hash { + self.tick_receipt_digest + } + + /// Encodes the complete typed receipt as canonical Edict CBOR. + pub fn to_canonical_bytes(&self) -> Result, EchoOperationArtifactErrorV1> { + encode_canonical_cbor_v1(&self.to_value()).map_err(canonical_error) + } + + fn to_value(&self) -> CanonicalValueV1 { + map_value([ + ( + "actual_footprint_digest", + hash_value(self.actual_footprint_digest), + ), + ( + "authority_grant_identity", + hash_value(self.authority_grant_identity), + ), + ( + "authority_profile_identity", + hash_value(self.authority_profile_identity), + ), + ("commit_id", hash_value(self.commit_id)), + ( + "commit_global_tick", + self.commit_global_tick + .map_or(CanonicalValueV1::Null, |tick| uint_value(tick.as_u64())), + ), + ("consumed_budget", self.consumed_budget.to_value()), + ( + "composition_digest", + self.composition_digest + .map_or(CanonicalValueV1::Null, hash_value), + ), + ( + "declared_footprint_digest", + hash_value(self.declared_footprint_digest), + ), + ("delegated_budget", self.delegated_budget.to_value()), + ("evaluation_basis", self.evaluation_basis.to_value()), + ( + "evaluation_basis_id", + hash_value(self.evaluation_basis_id.as_hash()), + ), + ( + "installed_operation_id", + hash_value(self.installed_operation_id.as_hash()), + ), + ( + "interpreter_profile_identity", + hash_value(self.interpreter_profile_identity), + ), + ( + "intrinsic_profile_identity", + hash_value(self.intrinsic_profile_identity), + ), + ( + "invocation_admission_id", + hash_value(self.invocation_admission_id.as_hash()), + ), + ( + "invocation_admission_policy_id", + hash_value(self.invocation_admission_policy_id), + ), + ( + "invocation_admission_maximum_budget", + self.invocation_admission_maximum_budget.to_value(), + ), + ( + "invocation_bytes_digest", + hash_value(self.invocation_bytes_digest), + ), + ("invocation_id", hash_value(self.invocation_id.as_hash())), + ("lawpack_identity", hash_value(self.lawpack_identity)), + ( + "operation_coordinate", + text_value(&self.operation_coordinate), + ), + ( + "package_admission_policy_id", + hash_value(self.package_admission_policy_id), + ), + ( + "package_admission_id", + hash_value(self.package_admission_id.as_hash()), + ), + ("package_id", hash_value(self.package_id.as_hash())), + ( + "prepared_patch_digest", + hash_value(self.prepared_patch_digest), + ), + ( + "prepared_result_id", + hash_value(self.prepared_result_id.as_hash()), + ), + ("preparation_id", hash_value(self.preparation_id.as_hash())), + ( + "private_evaluation_id", + hash_value(self.private_evaluation_id.as_hash()), + ), + ( + "committed_patch_digest", + self.committed_patch_digest + .map_or(CanonicalValueV1::Null, hash_value), + ), + ( + "committed_result_id", + self.committed_result_id + .map_or(CanonicalValueV1::Null, |id| hash_value(id.as_hash())), + ), + ("program_id", hash_value(self.program_id.as_hash())), + ("receipt_digest", hash_value(self.receipt_digest)), + ("schema", text_value("echo.operation-receipt/v1")), + ("semantic_identity", hash_value(self.semantic_identity)), + ("state_root_after", hash_value(self.state_root_after)), + ("state_root_before", hash_value(self.state_root_before)), + ( + "target_profile_identity", + hash_value(self.target_profile_identity), + ), + ( + "terminal_posture", + text_value(match self.terminal_posture { + EchoOperationTerminalPostureV1::Committed => "committed", + EchoOperationTerminalPostureV1::NotCommittedBasisChanged => { + "not-committed:basis-changed" + } + EchoOperationTerminalPostureV1::NotCommittedInstallationUnavailable => { + "not-committed:installation-unavailable" + } + EchoOperationTerminalPostureV1::NotCommittedEvaluationAuthorityMismatch => { + "not-committed:evaluation-authority-mismatch" + } + }), + ), + ( + "terminal_outcome_digest", + hash_value(self.terminal_outcome_digest), + ), + ("tick_receipt_digest", hash_value(self.tick_receipt_digest)), + ( + "worldline_tick_after", + uint_value(self.worldline_tick_after.as_u64()), + ), + ]) + } + + pub(crate) fn from_canonical_bytes(bytes: &[u8]) -> Result { + let value = decode_canonical_cbor_v1(bytes).map_err(canonical_error)?; + let mut fields = exact_text_map( + value, + &[ + "actual_footprint_digest", + "authority_grant_identity", + "authority_profile_identity", + "commit_global_tick", + "commit_id", + "committed_result_id", + "composition_digest", + "consumed_budget", + "declared_footprint_digest", + "delegated_budget", + "evaluation_basis", + "evaluation_basis_id", + "installed_operation_id", + "interpreter_profile_identity", + "intrinsic_profile_identity", + "invocation_admission_id", + "invocation_admission_policy_id", + "invocation_admission_maximum_budget", + "invocation_bytes_digest", + "invocation_id", + "lawpack_identity", + "operation_coordinate", + "package_admission_id", + "package_admission_policy_id", + "package_id", + "committed_patch_digest", + "prepared_patch_digest", + "prepared_result_id", + "preparation_id", + "private_evaluation_id", + "program_id", + "receipt_digest", + "schema", + "semantic_identity", + "state_root_after", + "state_root_before", + "target_profile_identity", + "terminal_posture", + "terminal_outcome_digest", + "tick_receipt_digest", + "worldline_tick_after", + ], + )?; + require_text(&mut fields, "schema", "echo.operation-receipt/v1")?; + let terminal_posture = match take_text(&mut fields, "terminal_posture")?.as_str() { + "committed" => EchoOperationTerminalPostureV1::Committed, + "not-committed:basis-changed" => { + EchoOperationTerminalPostureV1::NotCommittedBasisChanged + } + "not-committed:installation-unavailable" => { + EchoOperationTerminalPostureV1::NotCommittedInstallationUnavailable + } + "not-committed:evaluation-authority-mismatch" => { + EchoOperationTerminalPostureV1::NotCommittedEvaluationAuthorityMismatch + } + _ => return Err(invalid_structure("unknown operation receipt posture")), + }; + let commit_global_tick = match take_field(&mut fields, "commit_global_tick")? { + CanonicalValueV1::Null => None, + CanonicalValueV1::Integer(value) => Some(GlobalTick::from_raw(i128_to_u64(value)?)), + _ => return Err(invalid_structure("commit_global_tick must be null or uint")), + }; + let mut receipt = Self { + package_id: EchoOperationPackageIdV1(take_hash(&mut fields, "package_id")?), + package_admission_id: EchoOperationPackageAdmissionIdV1(take_hash( + &mut fields, + "package_admission_id", + )?), + installed_operation_id: InstalledEchoOperationIdV1(take_hash( + &mut fields, + "installed_operation_id", + )?), + operation_coordinate: take_text(&mut fields, "operation_coordinate")?, + semantic_identity: take_hash(&mut fields, "semantic_identity")?, + lawpack_identity: take_hash(&mut fields, "lawpack_identity")?, + target_profile_identity: take_hash(&mut fields, "target_profile_identity")?, + interpreter_profile_identity: take_hash(&mut fields, "interpreter_profile_identity")?, + intrinsic_profile_identity: take_hash(&mut fields, "intrinsic_profile_identity")?, + package_admission_policy_id: take_hash(&mut fields, "package_admission_policy_id")?, + authority_profile_identity: take_hash(&mut fields, "authority_profile_identity")?, + authority_grant_identity: take_hash(&mut fields, "authority_grant_identity")?, + invocation_admission_policy_id: take_hash( + &mut fields, + "invocation_admission_policy_id", + )?, + invocation_admission_maximum_budget: EchoOperationBudgetV1::from_value(take_field( + &mut fields, + "invocation_admission_maximum_budget", + )?)?, + invocation_admission_id: EchoOperationInvocationAdmissionIdV1(take_hash( + &mut fields, + "invocation_admission_id", + )?), + program_id: EchoOperationProgramIdV1(take_hash(&mut fields, "program_id")?), + invocation_id: EchoOperationInvocationIdV1(take_hash(&mut fields, "invocation_id")?), + invocation_bytes_digest: take_hash(&mut fields, "invocation_bytes_digest")?, + evaluation_basis: EchoOperationEvaluationBasisV1::from_value(take_field( + &mut fields, + "evaluation_basis", + )?)?, + evaluation_basis_id: EchoOperationEvaluationBasisIdV1(take_hash( + &mut fields, + "evaluation_basis_id", + )?), + declared_footprint_digest: take_hash(&mut fields, "declared_footprint_digest")?, + actual_footprint_digest: take_hash(&mut fields, "actual_footprint_digest")?, + delegated_budget: EchoOperationBudgetV1::from_value(take_field( + &mut fields, + "delegated_budget", + )?)?, + consumed_budget: EchoOperationBudgetV1::from_value(take_field( + &mut fields, + "consumed_budget", + )?)?, + private_evaluation_id: EchoOperationPrivateEvaluationIdV1(take_hash( + &mut fields, + "private_evaluation_id", + )?), + preparation_id: PreparedEchoOperationIdV1(take_hash(&mut fields, "preparation_id")?), + prepared_patch_digest: take_hash(&mut fields, "prepared_patch_digest")?, + prepared_result_id: EchoOperationResultIdV1(take_hash( + &mut fields, + "prepared_result_id", + )?), + committed_patch_digest: match take_field(&mut fields, "committed_patch_digest")? { + CanonicalValueV1::Null => None, + CanonicalValueV1::Bytes(bytes) => Some(bytes.try_into().map_err(|_| { + invalid_structure("committed_patch_digest must be exactly 32 bytes") + })?), + _ => { + return Err(invalid_structure( + "committed_patch_digest must be null or 32 bytes", + )) + } + }, + committed_result_id: take_optional_hash(&mut fields, "committed_result_id")? + .map(EchoOperationResultIdV1), + state_root_before: take_hash(&mut fields, "state_root_before")?, + state_root_after: take_hash(&mut fields, "state_root_after")?, + commit_id: take_hash(&mut fields, "commit_id")?, + composition_digest: take_optional_hash(&mut fields, "composition_digest")?, + tick_receipt_digest: take_hash(&mut fields, "tick_receipt_digest")?, + commit_global_tick, + worldline_tick_after: WorldlineTick::from_raw(take_u64( + &mut fields, + "worldline_tick_after", + )?), + terminal_posture, + terminal_outcome_digest: take_hash(&mut fields, "terminal_outcome_digest")?, + receipt_digest: take_hash(&mut fields, "receipt_digest")?, + }; + if receipt.evaluation_basis.identity() != receipt.evaluation_basis_id { + return Err(invalid_structure( + "operation receipt basis identity does not match complete basis", + )); + } + if package_admission_id(receipt.package_id, receipt.package_admission_policy_id) + != receipt.package_admission_id + { + return Err(invalid_structure( + "operation receipt package-admission identity mismatch", + )); + } + if invocation_admission_id( + receipt.installed_operation_id, + receipt.invocation_id, + receipt.invocation_admission_policy_id, + receipt.evaluation_basis_id, + ) != receipt.invocation_admission_id + { + return Err(invalid_structure( + "operation receipt invocation-admission identity mismatch", + )); + } + let retained_invocation_policy = EchoOperationInvocationAdmissionPolicyV1::new( + receipt.authority_profile_identity, + receipt.authority_grant_identity, + receipt.invocation_admission_maximum_budget, + ); + if retained_invocation_policy.identity() != receipt.invocation_admission_policy_id { + return Err(invalid_structure( + "operation receipt invocation-admission policy identity mismatch", + )); + } + if !receipt + .delegated_budget + .fits_within(receipt.invocation_admission_maximum_budget) + || !receipt + .consumed_budget + .fits_within(receipt.delegated_budget) + || !receipt.consumed_budget.is_nonzero() + { + return Err(invalid_structure( + "operation receipt budget evidence is internally inconsistent", + )); + } + if receipt.declared_footprint_digest != receipt.actual_footprint_digest { + return Err(invalid_structure( + "operation receipt actual footprint differs from the v1 exact contract", + )); + } + if private_evaluation_id_from_parts( + receipt.installed_operation_id, + receipt.program_id, + receipt.invocation_admission_id, + receipt.invocation_id, + receipt.evaluation_basis_id, + receipt.declared_footprint_digest, + receipt.actual_footprint_digest, + receipt.consumed_budget, + receipt.prepared_patch_digest, + receipt.prepared_result_id, + ) != receipt.private_evaluation_id + { + return Err(invalid_structure( + "operation receipt private-evaluation identity mismatch", + )); + } + if preparation_id( + receipt.private_evaluation_id, + receipt.prepared_patch_digest, + receipt.prepared_result_id, + ) != receipt.preparation_id + { + return Err(invalid_structure( + "operation receipt preparation identity mismatch", + )); + } + if terminal_outcome_digest(&receipt) != receipt.terminal_outcome_digest { + return Err(invalid_structure( + "operation receipt terminal-outcome identity mismatch", + )); + } + let retained_digest = receipt.receipt_digest; + receipt.receipt_digest = [0; 32]; + let expected_digest = receipt_digest(&receipt); + receipt.receipt_digest = retained_digest; + if retained_digest != expected_digest { + return Err(invalid_structure("operation receipt digest mismatch")); + } + let committed_worldline_tick_after = receipt + .evaluation_basis + .worldline_tick + .as_u64() + .checked_add(1) + .map(WorldlineTick::from_raw); + let expected_composition_digest = singleton_composition_digest_from_parts( + receipt.preparation_id, + receipt.prepared_patch_digest, + receipt.prepared_result_id, + receipt.evaluation_basis_id, + receipt.actual_footprint_digest, + ); + match receipt.terminal_posture { + EchoOperationTerminalPostureV1::Committed + if receipt.commit_global_tick.is_none() + || receipt.commit_id == [0; 32] + || receipt.tick_receipt_digest == [0; 32] + || receipt.committed_patch_digest.is_none() + || receipt.committed_patch_digest != Some(receipt.prepared_patch_digest) + || receipt.committed_result_id != Some(receipt.prepared_result_id) + || receipt.composition_digest != Some(expected_composition_digest) + || receipt.state_root_before != receipt.evaluation_basis.state_root + || committed_worldline_tick_after != Some(receipt.worldline_tick_after) => + { + return Err(invalid_structure( + "committed receipt is missing commit or tick evidence", + )); + } + EchoOperationTerminalPostureV1::NotCommittedBasisChanged + | EchoOperationTerminalPostureV1::NotCommittedInstallationUnavailable + | EchoOperationTerminalPostureV1::NotCommittedEvaluationAuthorityMismatch + if receipt.commit_global_tick.is_some() + || receipt.commit_id != [0; 32] + || receipt.tick_receipt_digest != [0; 32] + || receipt.committed_patch_digest.is_some() + || receipt.committed_result_id.is_some() + || receipt.composition_digest.is_some() + || receipt.state_root_before != receipt.state_root_after => + { + return Err(invalid_structure( + "noncommitted receipt carries committed consequence evidence", + )); + } + _ => {} + } + if receipt.to_canonical_bytes()? != bytes { + return Err(artifact_error( + EchoOperationArtifactErrorKindV1::NonCanonical, + "receipt did not reproduce the exact retained bytes", + )); + } + Ok(receipt) + } +} + +/// Terminal operation evidence, deliberately separate from generic [`TickReceipt`]. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct EchoOperationExecutionEvidenceV1 { + receipt: EchoOperationReceiptV1, + snapshot: Option, + tick_receipt: Option, + patch: Option, +} + +impl EchoOperationExecutionEvidenceV1 { + /// Returns the typed operation receipt. + #[must_use] + pub const fn receipt(&self) -> &EchoOperationReceiptV1 { + &self.receipt + } + + /// Returns the committed snapshot, if the exact-basis crossing succeeded. + #[must_use] + pub const fn snapshot(&self) -> Option<&Snapshot> { + self.snapshot.as_ref() + } + + /// Returns Echo's singleton commit receipt when committed. + #[must_use] + pub const fn tick_receipt(&self) -> Option<&TickReceipt> { + self.tick_receipt.as_ref() + } + + /// Returns the parent-visible committed patch, if any. + #[must_use] + pub const fn committed_patch(&self) -> Option<&WarpTickPatchV1> { + self.patch.as_ref() + } +} + +pub(crate) fn retain_committed_execution_v1( + evidence: &EchoOperationExecutionEvidenceV1, +) -> Result, EchoOperationArtifactErrorV1> { + if evidence.receipt.terminal_posture != EchoOperationTerminalPostureV1::Committed + || evidence.snapshot.is_none() + || evidence.tick_receipt.is_none() + || evidence.patch.is_none() + { + return Err(invalid_structure( + "WAL execution retention requires one committed operation consequence", + )); + } + evidence.receipt.to_canonical_bytes() +} + +pub(crate) fn recover_committed_execution_receipt_v1( + bytes: &[u8], +) -> Result { + let receipt = EchoOperationReceiptV1::from_canonical_bytes(bytes)?; + if receipt.terminal_posture != EchoOperationTerminalPostureV1::Committed { + return Err(invalid_structure( + "WAL execution evidence cannot retain a noncommitted candidate patch", + )); + } + Ok(receipt) +} + +pub(crate) fn validate_receipt_installation_v1( + receipt: &EchoOperationReceiptV1, + installed: &InstalledEchoOperationV1, +) -> Result<(), EchoOperationArtifactErrorV1> { + let package_evidence_matches = receipt.package_id == installed.package_id + && receipt.package_admission_id == installed.package_admission_id + && receipt.installed_operation_id == installed.installed_operation_id; + let package_policy_matches = + receipt.package_admission_policy_id == installed.admission_policy_id; + let semantic_evidence_matches = receipt.operation_coordinate == installed.operation_coordinate + && receipt.semantic_identity == installed.semantic_identity + && receipt.lawpack_identity == installed.lawpack_identity + && receipt.target_profile_identity == installed.target_profile_identity + && receipt.interpreter_profile_identity == installed.interpreter_profile_identity + && receipt.intrinsic_profile_identity == installed.intrinsic_profile_identity + && receipt.authority_profile_identity == installed.authority_profile_identity + && receipt.evaluation_basis.application_basis.schema_identity + == installed.application_basis_schema_identity + && receipt.program_id == installed.program_id; + let resource_evidence_matches = receipt + .delegated_budget + .fits_within(installed.budget_ceiling) + && installed + .program + .minimum_budget() + .fits_within(receipt.delegated_budget) + && receipt + .consumed_budget + .fits_within(receipt.delegated_budget); + if !package_evidence_matches + || !package_policy_matches + || !semantic_evidence_matches + || !resource_evidence_matches + { + return Err(invalid_structure( + "operation receipt does not match its retained installation", + )); + } + Ok(()) +} + +/// Failure while turning one complete private preparation into commit material. +#[derive(Clone, Copy, Debug, Error, PartialEq, Eq)] +pub enum EchoOperationCommitErrorV1 { + /// The parent-visible patch was structurally invalid. + #[error(transparent)] + Patch(#[from] TickPatchError), + /// The worldline-local transaction coordinate cannot advance. + #[error("executable-operation transaction coordinate overflow")] + TransactionCoordinateOverflow, +} + +pub(crate) struct EchoOperationCommitMaterialV1 { + pub evidence: EchoOperationExecutionEvidenceV1, + pub snapshot: Snapshot, + pub tick_receipt: TickReceipt, + pub patch: WarpTickPatchV1, +} + +#[derive(Clone, Copy, Debug)] +struct EchoOperationTerminalMaterialV1 { + posture: EchoOperationTerminalPostureV1, + state_root_before: Hash, + state_root_after: Hash, + commit_id: Hash, + tick_receipt_digest: Hash, + commit_global_tick: Option, + worldline_tick_after: WorldlineTick, +} + +pub(crate) fn not_committed_basis_changed( + prepared: &PreparedEchoOperationV1, + current_state_root: Hash, + current_worldline_tick: WorldlineTick, +) -> EchoOperationExecutionEvidenceV1 { + let receipt = build_receipt( + prepared, + EchoOperationTerminalMaterialV1 { + posture: EchoOperationTerminalPostureV1::NotCommittedBasisChanged, + state_root_before: current_state_root, + state_root_after: current_state_root, + commit_id: [0; 32], + tick_receipt_digest: [0; 32], + commit_global_tick: None, + worldline_tick_after: current_worldline_tick, + }, + ); + EchoOperationExecutionEvidenceV1 { + receipt, + snapshot: None, + tick_receipt: None, + patch: None, + } +} + +pub(crate) fn not_committed_installation_unavailable( + prepared: &PreparedEchoOperationV1, + current_state_root: Hash, + current_worldline_tick: WorldlineTick, +) -> EchoOperationExecutionEvidenceV1 { + let receipt = build_receipt( + prepared, + EchoOperationTerminalMaterialV1 { + posture: EchoOperationTerminalPostureV1::NotCommittedInstallationUnavailable, + state_root_before: current_state_root, + state_root_after: current_state_root, + commit_id: [0; 32], + tick_receipt_digest: [0; 32], + commit_global_tick: None, + worldline_tick_after: current_worldline_tick, + }, + ); + EchoOperationExecutionEvidenceV1 { + receipt, + snapshot: None, + tick_receipt: None, + patch: None, + } +} + +pub(crate) fn not_committed_evaluation_authority_mismatch( + prepared: &PreparedEchoOperationV1, + current_state_root: Hash, + current_worldline_tick: WorldlineTick, +) -> EchoOperationExecutionEvidenceV1 { + let receipt = build_receipt( + prepared, + EchoOperationTerminalMaterialV1 { + posture: EchoOperationTerminalPostureV1::NotCommittedEvaluationAuthorityMismatch, + state_root_before: current_state_root, + state_root_after: current_state_root, + commit_id: [0; 32], + tick_receipt_digest: [0; 32], + commit_global_tick: None, + worldline_tick_after: current_worldline_tick, + }, + ); + EchoOperationExecutionEvidenceV1 { + receipt, + snapshot: None, + tick_receipt: None, + patch: None, + } +} + +pub(crate) fn commit_prepared_to_state( + prepared: &PreparedEchoOperationV1, + state: &mut WorldlineState, + commit_global_tick: GlobalTick, +) -> Result { + let state_root_before = state.state_root(); + let tx_raw = state + .current_tick() + .as_u64() + .checked_add(1) + .ok_or(EchoOperationCommitErrorV1::TransactionCoordinateOverflow)?; + let tx = TxId::from_raw(tx_raw); + let scope = prepared.invocation.node; + let rule_id = prepared.installed.installed_operation_id.as_hash(); + let tick_receipt = TickReceipt::new( + tx, + vec![TickReceiptEntry { + rule_id, + scope_hash: crate::scope_hash(&rule_id, &scope), + scope, + disposition: TickReceiptDisposition::Applied, + }], + vec![Vec::new()], + ); + let mut next_state = state.warp_state.clone(); + prepared.patch.apply_to_state(&mut next_state)?; + let state_root_after = + crate::snapshot::compute_state_root_for_warp_state(&next_state, state.root()); + let parents = state + .last_snapshot + .as_ref() + .map(|snapshot| vec![snapshot.hash]) + .unwrap_or_default(); + let patch_digest = prepared.patch.digest(); + let commit_id = compute_commit_hash_v2( + &state_root_after, + &parents, + &patch_digest, + prepared.patch.policy_id(), + ); + let snapshot = Snapshot { + root: *state.root(), + hash: commit_id, + state_root: state_root_after, + parents, + plan_digest: prepared_plan_digest(prepared), + decision_digest: tick_receipt.digest(), + rewrites_digest: prepared_rewrites_digest(prepared), + patch_digest, + policy_id: prepared.patch.policy_id(), + tx, + }; + let receipt = build_receipt( + prepared, + EchoOperationTerminalMaterialV1 { + posture: EchoOperationTerminalPostureV1::Committed, + state_root_before, + state_root_after, + commit_id, + tick_receipt_digest: tick_receipt.digest(), + commit_global_tick: Some(commit_global_tick), + worldline_tick_after: WorldlineTick::from_raw(tx_raw), + }, + ); + state.warp_state = next_state; + state.last_snapshot = Some(snapshot.clone()); + state.tick_history.push(( + snapshot.clone(), + tick_receipt.clone(), + prepared.patch.clone(), + )); + state.tx_counter = tx_raw; + Ok(EchoOperationCommitMaterialV1 { + evidence: EchoOperationExecutionEvidenceV1 { + receipt, + snapshot: Some(snapshot.clone()), + tick_receipt: Some(tick_receipt.clone()), + patch: Some(prepared.patch.clone()), + }, + snapshot, + tick_receipt, + patch: prepared.patch.clone(), + }) +} + +pub(crate) fn genesis_commit_id(writer_head: WriterHeadKey, state_root: Hash) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(GENESIS_COMMIT_DOMAIN); + hasher.update(writer_head.worldline_id.as_bytes()); + hasher.update(writer_head.head_id.as_bytes()); + hasher.update(&state_root); + hasher.finalize().into() +} + +fn build_receipt( + prepared: &PreparedEchoOperationV1, + terminal: EchoOperationTerminalMaterialV1, +) -> EchoOperationReceiptV1 { + let invocation_bytes_digest = domain_hash( + INVOCATION_BYTES_DIGEST_DOMAIN, + &prepared.canonical_invocation_bytes, + ); + let committed = terminal.posture == EchoOperationTerminalPostureV1::Committed; + let mut receipt = EchoOperationReceiptV1 { + package_id: prepared.installed.package_id, + package_admission_id: prepared.installed.package_admission_id, + installed_operation_id: prepared.installed.installed_operation_id, + operation_coordinate: prepared.installed.operation_coordinate.clone(), + semantic_identity: prepared.installed.semantic_identity, + lawpack_identity: prepared.installed.lawpack_identity, + target_profile_identity: prepared.installed.target_profile_identity, + interpreter_profile_identity: prepared.installed.interpreter_profile_identity, + intrinsic_profile_identity: prepared.installed.intrinsic_profile_identity, + package_admission_policy_id: prepared.installed.admission_policy_id, + authority_profile_identity: prepared.installed.authority_profile_identity, + authority_grant_identity: prepared.invocation.authority_grant_identity, + invocation_admission_policy_id: prepared.invocation_admission_policy_id, + invocation_admission_maximum_budget: prepared.invocation_admission_maximum_budget, + invocation_admission_id: prepared.invocation_admission_id, + program_id: prepared.installed.program_id, + invocation_id: prepared.invocation_id, + invocation_bytes_digest, + evaluation_basis: prepared.evaluation_basis, + evaluation_basis_id: prepared.evaluation_basis.identity(), + declared_footprint_digest: prepared.declared_footprint_digest, + actual_footprint_digest: prepared.actual_footprint_digest, + delegated_budget: prepared.invocation.delegated_budget, + consumed_budget: prepared.consumed_budget, + private_evaluation_id: prepared.private_evaluation_id, + preparation_id: prepared.preparation_id, + prepared_patch_digest: prepared.patch.digest(), + prepared_result_id: prepared.result_id, + committed_patch_digest: committed.then(|| prepared.patch.digest()), + committed_result_id: committed.then_some(prepared.result_id), + state_root_before: terminal.state_root_before, + state_root_after: terminal.state_root_after, + commit_id: terminal.commit_id, + composition_digest: committed.then(|| singleton_composition_digest(prepared)), + tick_receipt_digest: terminal.tick_receipt_digest, + commit_global_tick: terminal.commit_global_tick, + worldline_tick_after: terminal.worldline_tick_after, + terminal_posture: terminal.posture, + terminal_outcome_digest: [0; 32], + receipt_digest: [0; 32], + }; + receipt.terminal_outcome_digest = terminal_outcome_digest(&receipt); + receipt.receipt_digest = receipt_digest(&receipt); + receipt +} + +fn receipt_digest(receipt: &EchoOperationReceiptV1) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(RECEIPT_DIGEST_DOMAIN); + hasher.update(&receipt.package_id.as_hash()); + hasher.update(&receipt.package_admission_id.as_hash()); + hasher.update(&receipt.installed_operation_id.as_hash()); + hash_len_bytes(&mut hasher, receipt.operation_coordinate.as_bytes()); + hasher.update(&receipt.semantic_identity); + hasher.update(&receipt.lawpack_identity); + hasher.update(&receipt.target_profile_identity); + hasher.update(&receipt.interpreter_profile_identity); + hasher.update(&receipt.intrinsic_profile_identity); + hasher.update(&receipt.package_admission_policy_id); + hasher.update(&receipt.authority_profile_identity); + hasher.update(&receipt.authority_grant_identity); + hasher.update(&receipt.invocation_admission_policy_id); + hash_budget(&mut hasher, receipt.invocation_admission_maximum_budget); + hasher.update(&receipt.invocation_admission_id.as_hash()); + hasher.update(&receipt.program_id.as_hash()); + hasher.update(&receipt.invocation_id.as_hash()); + hasher.update(&receipt.invocation_bytes_digest); + hasher.update(&receipt.evaluation_basis_id.as_hash()); + hasher.update(&receipt.declared_footprint_digest); + hasher.update(&receipt.actual_footprint_digest); + hash_budget(&mut hasher, receipt.delegated_budget); + hash_budget(&mut hasher, receipt.consumed_budget); + hasher.update(&receipt.private_evaluation_id.as_hash()); + hasher.update(&receipt.preparation_id.as_hash()); + hasher.update(&receipt.prepared_patch_digest); + hasher.update(&receipt.prepared_result_id.as_hash()); + match receipt.committed_patch_digest { + None => { + hasher.update(&[0]); + } + Some(digest) => { + hasher.update(&[1]); + hasher.update(&digest); + } + } + hash_optional_id( + &mut hasher, + receipt + .committed_result_id + .map(EchoOperationResultIdV1::as_hash), + ); + hasher.update(&receipt.state_root_before); + hasher.update(&receipt.state_root_after); + hasher.update(&receipt.commit_id); + hash_optional_id(&mut hasher, receipt.composition_digest); + hasher.update(&receipt.tick_receipt_digest); + match receipt.commit_global_tick { + None => { + hasher.update(&[0]); + } + Some(tick) => { + hasher.update(&[1]); + hasher.update(&tick.as_u64().to_le_bytes()); + } + } + hasher.update(&receipt.worldline_tick_after.as_u64().to_le_bytes()); + hasher.update(&[terminal_posture_code(receipt.terminal_posture)]); + hasher.update(&receipt.terminal_outcome_digest); + hasher.finalize().into() +} + +fn terminal_outcome_digest(receipt: &EchoOperationReceiptV1) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(TERMINAL_OUTCOME_ID_DOMAIN); + hasher.update(&receipt.preparation_id.as_hash()); + hasher.update(&receipt.prepared_patch_digest); + hasher.update(&receipt.prepared_result_id.as_hash()); + hasher.update(&[terminal_posture_code(receipt.terminal_posture)]); + hash_optional_id(&mut hasher, receipt.committed_patch_digest); + hash_optional_id( + &mut hasher, + receipt + .committed_result_id + .map(EchoOperationResultIdV1::as_hash), + ); + hash_optional_id(&mut hasher, receipt.composition_digest); + hasher.update(&receipt.state_root_before); + hasher.update(&receipt.state_root_after); + hasher.update(&receipt.commit_id); + hasher.finalize().into() +} + +fn terminal_posture_code(posture: EchoOperationTerminalPostureV1) -> u8 { + match posture { + EchoOperationTerminalPostureV1::Committed => 1, + EchoOperationTerminalPostureV1::NotCommittedBasisChanged => 2, + EchoOperationTerminalPostureV1::NotCommittedInstallationUnavailable => 3, + EchoOperationTerminalPostureV1::NotCommittedEvaluationAuthorityMismatch => 4, + } +} + +fn hash_optional_id(hasher: &mut Hasher, value: Option) { + match value { + None => { + hasher.update(&[0]); + } + Some(value) => { + hasher.update(&[1]); + hasher.update(&value); + } + } +} + +fn singleton_composition_digest(prepared: &PreparedEchoOperationV1) -> Hash { + singleton_composition_digest_from_parts( + prepared.preparation_id, + prepared.patch.digest(), + prepared.result_id, + prepared.evaluation_basis.identity(), + prepared.actual_footprint_digest, + ) +} + +fn singleton_composition_digest_from_parts( + preparation_id: PreparedEchoOperationIdV1, + patch_digest: Hash, + result_id: EchoOperationResultIdV1, + evaluation_basis_id: EchoOperationEvaluationBasisIdV1, + actual_footprint_digest: Hash, +) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(COMPOSITION_DIGEST_DOMAIN); + hasher.update(&1_u64.to_le_bytes()); + hasher.update(&preparation_id.as_hash()); + hasher.update(&patch_digest); + hasher.update(&result_id.as_hash()); + hasher.update(&evaluation_basis_id.as_hash()); + hasher.update(&actual_footprint_digest); + hasher.finalize().into() +} + +fn prepared_plan_digest(prepared: &PreparedEchoOperationV1) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(PLAN_DIGEST_DOMAIN); + hasher.update(&prepared.installed.installed_operation_id.as_hash()); + hasher.update(&prepared.invocation_admission_id.as_hash()); + hasher.update(&prepared.preparation_id.as_hash()); + hasher.update(&prepared.declared_footprint_digest); + hasher.update(&prepared.invocation_admission_policy_id); + hasher.finalize().into() +} + +fn prepared_rewrites_digest(prepared: &PreparedEchoOperationV1) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(REWRITES_DIGEST_DOMAIN); + hasher.update(&prepared.installed.program_id.as_hash()); + hasher.update(&prepared.private_evaluation_id.as_hash()); + hasher.update(&prepared.patch.digest()); + hasher.update(&prepared.result_id.as_hash()); + hasher.finalize().into() +} + +fn package_admission_id( + package_id: EchoOperationPackageIdV1, + admission_policy_id: Hash, +) -> EchoOperationPackageAdmissionIdV1 { + let mut hasher = Hasher::new(); + hasher.update(PACKAGE_ADMISSION_ID_DOMAIN); + hasher.update(&package_id.as_hash()); + hasher.update(&admission_policy_id); + EchoOperationPackageAdmissionIdV1(hasher.finalize().into()) +} + +fn installed_operation_id(installed: &InstalledEchoOperationV1) -> InstalledEchoOperationIdV1 { + let mut hasher = Hasher::new(); + hasher.update(INSTALLATION_ID_DOMAIN); + hasher.update(&installed.package_id.as_hash()); + hasher.update(&installed.package_admission_id.as_hash()); + hash_len_bytes(&mut hasher, installed.operation_coordinate.as_bytes()); + hasher.update(&installed.program_id.as_hash()); + hash_len_bytes(&mut hasher, &installed.canonical_package_bytes); + InstalledEchoOperationIdV1(hasher.finalize().into()) +} + +fn invocation_admission_id( + installed_operation_id: InstalledEchoOperationIdV1, + invocation_id: EchoOperationInvocationIdV1, + admission_policy_id: Hash, + evaluation_basis_id: EchoOperationEvaluationBasisIdV1, +) -> EchoOperationInvocationAdmissionIdV1 { + let mut hasher = Hasher::new(); + hasher.update(INVOCATION_ADMISSION_ID_DOMAIN); + hasher.update(&installed_operation_id.as_hash()); + hasher.update(&invocation_id.as_hash()); + hasher.update(&admission_policy_id); + hasher.update(&evaluation_basis_id.as_hash()); + EchoOperationInvocationAdmissionIdV1(hasher.finalize().into()) +} + +fn operation_result_id( + installed: &InstalledEchoOperationV1, + invocation: &EchoOperationInvocationV1, + previous_value_digest: Hash, + replacement_value_digest: Hash, + patch_digest: Hash, +) -> EchoOperationResultIdV1 { + let mut hasher = Hasher::new(); + hasher.update(RESULT_ID_DOMAIN); + hasher.update(&installed.installed_operation_id.as_hash()); + hasher.update(&profile_digest(RESULT_SCHEMA)); + hasher.update(invocation.node.warp_id.as_bytes()); + hasher.update(invocation.node.local_id.as_bytes()); + hasher.update(&previous_value_digest); + hasher.update(&replacement_value_digest); + hasher.update(&patch_digest); + EchoOperationResultIdV1(hasher.finalize().into()) +} + +#[allow(clippy::too_many_arguments)] +fn private_evaluation_id_from_parts( + installed_operation_id: InstalledEchoOperationIdV1, + program_id: EchoOperationProgramIdV1, + invocation_admission_id: EchoOperationInvocationAdmissionIdV1, + invocation_id: EchoOperationInvocationIdV1, + evaluation_basis_id: EchoOperationEvaluationBasisIdV1, + declared_footprint_digest: Hash, + actual_footprint_digest: Hash, + consumed_budget: EchoOperationBudgetV1, + patch_digest: Hash, + result_id: EchoOperationResultIdV1, +) -> EchoOperationPrivateEvaluationIdV1 { + let mut hasher = Hasher::new(); + hasher.update(PRIVATE_EVALUATION_ID_DOMAIN); + hasher.update(&installed_operation_id.as_hash()); + hasher.update(&program_id.as_hash()); + hasher.update(&invocation_admission_id.as_hash()); + hasher.update(&invocation_id.as_hash()); + hasher.update(&evaluation_basis_id.as_hash()); + hasher.update(&declared_footprint_digest); + hasher.update(&actual_footprint_digest); + hash_budget(&mut hasher, consumed_budget); + hasher.update(&patch_digest); + hasher.update(&result_id.as_hash()); + EchoOperationPrivateEvaluationIdV1(hasher.finalize().into()) +} + +fn preparation_id( + private_evaluation_id: EchoOperationPrivateEvaluationIdV1, + patch_digest: Hash, + result_id: EchoOperationResultIdV1, +) -> PreparedEchoOperationIdV1 { + let mut hasher = Hasher::new(); + hasher.update(PREPARATION_ID_DOMAIN); + hasher.update(&private_evaluation_id.as_hash()); + hasher.update(&patch_digest); + hasher.update(&result_id.as_hash()); + PreparedEchoOperationIdV1(hasher.finalize().into()) +} + +fn obstruction_kind_code(kind: EchoOperationObstructionKindV1) -> u8 { + match kind { + EchoOperationObstructionKindV1::OperationUnavailable => 1, + EchoOperationObstructionKindV1::BasisChanged => 2, + EchoOperationObstructionKindV1::BudgetExceeded => 3, + EchoOperationObstructionKindV1::NodeMissing => 4, + EchoOperationObstructionKindV1::NodeTypeMismatch => 5, + EchoOperationObstructionKindV1::AttachmentMissing => 6, + EchoOperationObstructionKindV1::AttachmentNotAtom => 7, + EchoOperationObstructionKindV1::AttachmentTypeMismatch => 8, + EchoOperationObstructionKindV1::PreconditionMismatch => 9, + EchoOperationObstructionKindV1::FootprintViolation => 10, + EchoOperationObstructionKindV1::ReplacementTooLarge => 11, + EchoOperationObstructionKindV1::EvaluationAuthorityMismatch => 12, + } +} + +fn anchored_node_footprint(node: NodeKey) -> Footprint { + let mut footprint = Footprint::default(); + record_node_read(&mut footprint, node); + let attachment = AttachmentKey::node_alpha(node); + footprint.a_read.insert(attachment); + footprint.a_write.insert(attachment); + footprint +} + +fn record_node_read(footprint: &mut Footprint, node: NodeKey) { + footprint.n_read.insert(node); + footprint.factor_mask |= 1_u64 << (node.local_id.0[0] & 63); +} + +fn footprint_digest(footprint: &Footprint) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(FOOTPRINT_DIGEST_DOMAIN); + hash_node_set( + &mut hasher, + b"nr", + footprint.n_read.len(), + footprint.n_read.iter(), + ); + hash_node_set( + &mut hasher, + b"nw", + footprint.n_write.len(), + footprint.n_write.iter(), + ); + hash_edge_set( + &mut hasher, + b"er", + footprint.e_read.len(), + footprint.e_read.iter(), + ); + hash_edge_set( + &mut hasher, + b"ew", + footprint.e_write.len(), + footprint.e_write.iter(), + ); + hash_attachment_set( + &mut hasher, + b"ar", + footprint.a_read.len(), + footprint.a_read.iter(), + ); + hash_attachment_set( + &mut hasher, + b"aw", + footprint.a_write.len(), + footprint.a_write.iter(), + ); + hash_port_set( + &mut hasher, + b"bi", + footprint.b_in.len(), + footprint.b_in.iter(), + ); + hash_port_set( + &mut hasher, + b"bo", + footprint.b_out.len(), + footprint.b_out.iter(), + ); + hasher.update(&footprint.factor_mask.to_le_bytes()); + hasher.finalize().into() +} + +fn hash_edge_set<'a>( + hasher: &mut Hasher, + label: &[u8], + count: usize, + values: impl Iterator, +) { + hasher.update(label); + hasher.update(&(count as u64).to_le_bytes()); + for value in values { + hasher.update(value.warp_id.as_bytes()); + hasher.update(value.local_id.as_bytes()); + } +} + +fn hash_node_set<'a>( + hasher: &mut Hasher, + label: &[u8], + count: usize, + values: impl Iterator, +) { + hasher.update(label); + hasher.update(&(count as u64).to_le_bytes()); + for value in values { + hasher.update(value.warp_id.as_bytes()); + hasher.update(value.local_id.as_bytes()); + } +} + +fn hash_attachment_set<'a>( + hasher: &mut Hasher, + label: &[u8], + count: usize, + values: impl Iterator, +) { + hasher.update(label); + hasher.update(&(count as u64).to_le_bytes()); + for value in values { + match value.owner { + crate::AttachmentOwner::Node(node) => { + hasher.update(&[1]); + hasher.update(node.warp_id.as_bytes()); + hasher.update(node.local_id.as_bytes()); + } + crate::AttachmentOwner::Edge(edge) => { + hasher.update(&[2]); + hasher.update(edge.warp_id.as_bytes()); + hasher.update(edge.local_id.as_bytes()); + } + } + hasher.update(&[match value.plane { + crate::AttachmentPlane::Alpha => 1, + crate::AttachmentPlane::Beta => 2, + }]); + } +} + +fn hash_port_set<'a>( + hasher: &mut Hasher, + label: &[u8], + count: usize, + values: impl Iterator, +) { + hasher.update(label); + hasher.update(&(count as u64).to_le_bytes()); + for (warp_id, port) in values { + hasher.update(warp_id.as_bytes()); + hasher.update(&port.to_le_bytes()); + } +} + +fn domain_hash(domain: &[u8], bytes: &[u8]) -> Hash { + let mut hasher = Hasher::new(); + hasher.update(domain); + hasher.update(&(bytes.len() as u64).to_le_bytes()); + hasher.update(bytes); + hasher.finalize().into() +} + +fn profile_digest(label: &str) -> Hash { + domain_hash(b"echo:operation-profile:v1\0", label.as_bytes()) +} + +fn hash_len_bytes(hasher: &mut Hasher, bytes: &[u8]) { + hasher.update(&(bytes.len() as u64).to_le_bytes()); + hasher.update(bytes); +} + +fn hash_budget(hasher: &mut Hasher, budget: EchoOperationBudgetV1) { + hasher.update(&budget.steps.to_le_bytes()); + hasher.update(&budget.read_bytes.to_le_bytes()); + hasher.update(&budget.write_bytes.to_le_bytes()); +} + +fn map_value(fields: [(&str, CanonicalValueV1); N]) -> CanonicalValueV1 { + CanonicalValueV1::Map( + fields + .into_iter() + .map(|(key, value)| (text_value(key), value)) + .collect(), + ) +} + +fn text_value(value: &str) -> CanonicalValueV1 { + CanonicalValueV1::Text(value.to_owned()) +} + +fn hash_value(value: Hash) -> CanonicalValueV1 { + CanonicalValueV1::Bytes(value.to_vec()) +} + +fn uint_value(value: u64) -> CanonicalValueV1 { + CanonicalValueV1::Integer(i128::from(value)) +} + +fn exact_text_map( + value: CanonicalValueV1, + expected: &[&str], +) -> Result, EchoOperationArtifactErrorV1> { + let CanonicalValueV1::Map(entries) = value else { + return Err(invalid_structure("artifact root must be a map")); + }; + let mut fields = BTreeMap::new(); + for (key, value) in entries { + let CanonicalValueV1::Text(key) = key else { + return Err(invalid_structure("artifact map keys must be text")); + }; + if fields.insert(key, value).is_some() { + return Err(invalid_structure("artifact map keys must be unique")); + } + } + let actual = fields.keys().map(String::as_str).collect::>(); + let mut expected = expected.to_vec(); + expected.sort_unstable(); + if actual != expected { + return Err(invalid_structure( + "artifact map fields differ from the v1 schema", + )); + } + Ok(fields) +} + +fn take_field( + fields: &mut BTreeMap, + name: &str, +) -> Result { + fields + .remove(name) + .ok_or_else(|| invalid_structure(format!("missing field {name}"))) +} + +fn take_hash( + fields: &mut BTreeMap, + name: &str, +) -> Result { + let CanonicalValueV1::Bytes(bytes) = take_field(fields, name)? else { + return Err(invalid_structure(format!("field {name} must be bytes"))); + }; + bytes + .try_into() + .map_err(|_| invalid_structure(format!("field {name} must be exactly 32 bytes"))) +} + +fn take_optional_hash( + fields: &mut BTreeMap, + name: &str, +) -> Result, EchoOperationArtifactErrorV1> { + match take_field(fields, name)? { + CanonicalValueV1::Null => Ok(None), + CanonicalValueV1::Bytes(bytes) => bytes + .try_into() + .map(Some) + .map_err(|_| invalid_structure(format!("field {name} must be null or 32 bytes"))), + _ => Err(invalid_structure(format!( + "field {name} must be null or bytes" + ))), + } +} + +fn take_bytes( + fields: &mut BTreeMap, + name: &str, +) -> Result, EchoOperationArtifactErrorV1> { + let CanonicalValueV1::Bytes(bytes) = take_field(fields, name)? else { + return Err(invalid_structure(format!("field {name} must be bytes"))); + }; + Ok(bytes) +} + +fn take_text( + fields: &mut BTreeMap, + name: &str, +) -> Result { + let CanonicalValueV1::Text(value) = take_field(fields, name)? else { + return Err(invalid_structure(format!("field {name} must be text"))); + }; + Ok(value) +} + +fn take_u64( + fields: &mut BTreeMap, + name: &str, +) -> Result { + let CanonicalValueV1::Integer(value) = take_field(fields, name)? else { + return Err(invalid_structure(format!( + "field {name} must be an unsigned integer" + ))); + }; + i128_to_u64(value) +} + +fn i128_to_u64(value: i128) -> Result { + u64::try_from(value).map_err(|_| invalid_structure("integer must fit in u64")) +} + +fn require_text( + fields: &mut BTreeMap, + name: &str, + expected: &str, +) -> Result<(), EchoOperationArtifactErrorV1> { + let actual = take_text(fields, name)?; + if actual == expected { + Ok(()) + } else { + Err(invalid_structure(format!( + "field {name} must equal {expected}" + ))) + } +} + +fn canonical_error(error: CanonicalValueError) -> EchoOperationArtifactErrorV1 { + let kind = if error.kind() == CanonicalValueErrorKind::NonCanonical { + EchoOperationArtifactErrorKindV1::NonCanonical + } else { + EchoOperationArtifactErrorKindV1::MalformedCanonicalBytes + }; + artifact_error(kind, error.to_string()) +} + +fn artifact_error( + kind: EchoOperationArtifactErrorKindV1, + detail: impl Into, +) -> EchoOperationArtifactErrorV1 { + EchoOperationArtifactErrorV1 { + kind, + detail: detail.into(), + } +} + +fn invalid_structure(detail: impl Into) -> EchoOperationArtifactErrorV1 { + artifact_error(EchoOperationArtifactErrorKindV1::InvalidStructure, detail) +} + +fn admission_error( + kind: EchoOperationAdmissionErrorKindV1, + detail: impl Into, +) -> EchoOperationAdmissionErrorV1 { + EchoOperationAdmissionErrorV1 { + kind, + detail: detail.into(), + artifact: None, + } +} + +fn invocation_admission_error( + kind: EchoOperationInvocationAdmissionErrorKindV1, + detail: impl Into, +) -> EchoOperationInvocationAdmissionErrorV1 { + EchoOperationInvocationAdmissionErrorV1 { + kind, + detail: detail.into(), + } +} + +fn installation_error( + kind: EchoOperationInstallationErrorKindV1, + detail: impl Into, +) -> EchoOperationInstallationErrorV1 { + EchoOperationInstallationErrorV1 { + kind, + detail: detail.into(), + } +} + +#[cfg(test)] +#[allow(clippy::expect_used, clippy::panic)] +mod tests { + use super::*; + + fn digest(byte: u8) -> Hash { + [byte; 32] + } + + fn node_key_from_bytes(bytes: [u8; 64]) -> NodeKey { + let mut warp_id = [0; 32]; + let mut node_id = [0; 32]; + warp_id.copy_from_slice(&bytes[..32]); + node_id.copy_from_slice(&bytes[32..]); + NodeKey { + warp_id: crate::WarpId(warp_id), + local_id: crate::NodeId(node_id), + } + } + + #[test] + fn footprint_digest_is_prefix_free_across_adjacent_sets() { + let mut read_node_bytes = [0; 64]; + read_node_bytes[..2].copy_from_slice(b"nw"); + let mut write_node_bytes = [0; 64]; + write_node_bytes[..62].copy_from_slice(&read_node_bytes[2..]); + write_node_bytes[62..].copy_from_slice(b"nw"); + + let mut read_footprint = Footprint::default(); + read_footprint + .n_read + .insert(node_key_from_bytes(read_node_bytes)); + read_footprint.factor_mask = 1; + let mut write_footprint = Footprint::default(); + write_footprint + .n_write + .insert(node_key_from_bytes(write_node_bytes)); + write_footprint.factor_mask = 1; + + assert_ne!( + footprint_digest(&read_footprint), + footprint_digest(&write_footprint), + "set cardinalities must prevent boundary-shift digest collisions" + ); + } + + #[test] + fn footprint_digest_binds_every_resource_axis() { + let node = node_key_from_bytes([7; 64]); + let edge = EdgeKey { + warp_id: node.warp_id, + local_id: crate::EdgeId(digest(8)), + }; + let attachment = AttachmentKey::node_alpha(node); + let mut footprints = Vec::new(); + + let mut footprint = Footprint::default(); + footprint.n_read.insert(node); + footprints.push(footprint); + let mut footprint = Footprint::default(); + footprint.n_write.insert(node); + footprints.push(footprint); + let mut footprint = Footprint::default(); + footprint.e_read.insert(edge); + footprints.push(footprint); + let mut footprint = Footprint::default(); + footprint.e_write.insert(edge); + footprints.push(footprint); + let mut footprint = Footprint::default(); + footprint.a_read.insert(attachment); + footprints.push(footprint); + let mut footprint = Footprint::default(); + footprint.a_write.insert(attachment); + footprints.push(footprint); + let mut footprint = Footprint::default(); + footprint.b_in.insert(node.warp_id, 9); + footprints.push(footprint); + let mut footprint = Footprint::default(); + footprint.b_out.insert(node.warp_id, 9); + footprints.push(footprint); + let mut footprint = Footprint::default(); + footprint.factor_mask = 1; + footprints.push(footprint); + + let digests = footprints.iter().map(footprint_digest).collect::>(); + for (index, digest) in digests.iter().enumerate() { + assert_ne!( + *digest, + footprint_digest(&Footprint::default()), + "resource axis {index} must differ from an empty footprint" + ); + for (other_index, other) in digests.iter().enumerate().skip(index + 1) { + assert_ne!( + digest, other, + "resource axes {index} and {other_index} must not alias" + ); + } + } + } + + #[test] + fn public_artifact_encoders_refuse_values_their_decoders_refuse() { + let invalid_program = EchoOperationProgramV1::anchored_node_attachment_compare_and_set( + crate::make_type_id("invalid-program-node"), + crate::make_type_id("invalid-program-atom"), + 0, + ); + let error = invalid_program + .to_canonical_bytes() + .expect_err("a zero replacement bound must not produce program bytes"); + assert_eq!( + error.kind(), + EchoOperationArtifactErrorKindV1::UnsupportedProgram + ); + invalid_program + .identity() + .expect_err("an invalid program must not mint a stable identity"); + + let node = node_key_from_bytes([10; 64]); + let basis = EchoOperationEvaluationBasisV1::new( + WriterHeadKey { + worldline_id: crate::WorldlineId::from_bytes(digest(11)), + head_id: crate::HeadId::from_bytes(digest(12)), + }, + WorldlineTick::ZERO, + None, + digest(13), + digest(14), + EchoOperationApplicationBasisV1::new(digest(15), digest(16)), + ); + let invalid_invocation = + EchoOperationInvocationV1::anchored_node_attachment_compare_and_set( + EchoOperationPackageIdV1(digest(17)), + "echo.fixture.InvalidBudget.v1", + basis, + digest(18), + EchoOperationBudgetV1::new(0, 64, 32), + node, + digest(19), + Vec::new(), + ); + let error = invalid_invocation + .to_canonical_bytes() + .expect_err("a zero-step budget must not produce invocation bytes"); + assert_eq!( + error.kind(), + EchoOperationArtifactErrorKindV1::InvalidBudget + ); + invalid_invocation + .identity() + .expect_err("an invalid invocation must not mint a stable identity"); + } + + fn retained_fixture_installation() -> InstalledEchoOperationV1 { + let package = ExecutableOperationPackageV1::new( + "echo.fixture.Retention.v1", + EchoOperationSemanticClosureV1::new( + digest(1), + digest(2), + digest(3), + digest(4), + "echo.fixture.RetentionSchema.v1", + digest(5), + "echo.fixture.RetentionLawpack.v1", + digest(6), + ), + echo_operation_target_profile_identity_v1(), + digest(7), + EchoOperationBudgetV1::new(8, 512, 512), + EchoOperationProgramV1::anchored_node_attachment_compare_and_set( + crate::make_type_id("retention-node"), + crate::make_type_id("retention-atom"), + 128, + ), + ); + let bytes = package.to_canonical_bytes().expect("package encodes"); + let package_id = echo_operation_package_id_v1(&bytes); + let policy = EchoOperationAdmissionPolicyV1::exact( + package_id, + "echo.fixture.Retention.v1", + digest(7), + EchoOperationBudgetV1::new(8, 512, 512), + ); + installed_from_admitted(admit_package_v1(&policy, bytes).expect("package admits")) + .expect("an admitted program remains canonically encodable") + } + + fn replace_map_field(bytes: &[u8], field_name: &str, replacement: CanonicalValueV1) -> Vec { + let CanonicalValueV1::Map(mut fields) = + decode_canonical_cbor_v1(bytes).expect("fixture bytes decode") + else { + panic!("fixture bytes must encode a map"); + }; + let field = fields + .iter_mut() + .find(|(key, _)| key == &CanonicalValueV1::Text(field_name.to_owned())) + .expect("fixture field exists"); + field.1 = replacement; + encode_canonical_cbor_v1(&CanonicalValueV1::Map(fields)).expect("fixture map encodes") + } + + #[test] + fn retained_installation_rejects_identity_substitution() { + let installed = retained_fixture_installation(); + let bytes = retain_installation_v1(&installed).expect("installation retains"); + for field_name in [ + "admission_policy_id", + "package_id", + "package_admission_id", + "installed_operation_id", + ] { + let substituted = replace_map_field( + &bytes, + field_name, + CanonicalValueV1::Bytes(digest(99).to_vec()), + ); + let error = recover_installation_v1(&substituted) + .expect_err("retained installation identities cannot be substituted"); + assert_eq!( + error.kind(), + EchoOperationArtifactErrorKindV1::InvalidStructure, + "field {field_name}" + ); + } + let substituted_policy = EchoOperationAdmissionPolicyV1::exact( + installed.package_id, + installed.operation_coordinate.clone(), + installed.authority_profile_identity, + EchoOperationBudgetV1::new(9, 512, 512), + ); + let substituted = + replace_map_field(&bytes, "admission_policy", substituted_policy.to_value()); + let error = recover_installation_v1(&substituted) + .expect_err("recovery must revalidate retained package-admission policy material"); + assert_eq!( + error.kind(), + EchoOperationArtifactErrorKindV1::InvalidStructure + ); + } + + #[test] + fn retained_committed_receipt_rejects_field_substitution() { + let installed = retained_fixture_installation(); + let basis = EchoOperationEvaluationBasisV1::new( + WriterHeadKey { + worldline_id: crate::WorldlineId::from_bytes(digest(8)), + head_id: crate::HeadId::from_bytes(digest(9)), + }, + WorldlineTick::ZERO, + None, + digest(10), + digest(11), + EchoOperationApplicationBasisV1::new( + installed.application_basis_schema_identity, + digest(13), + ), + ); + let invocation_id = EchoOperationInvocationIdV1(digest(14)); + let invocation_admission_maximum_budget = EchoOperationBudgetV1::new(8, 512, 512); + let invocation_admission_policy_id = EchoOperationInvocationAdmissionPolicyV1::new( + installed.authority_profile_identity, + digest(19), + invocation_admission_maximum_budget, + ) + .identity(); + let invocation_admission_id = invocation_admission_id( + installed.installed_operation_id, + invocation_id, + invocation_admission_policy_id, + basis.identity(), + ); + let declared_footprint_digest = digest(21); + let actual_footprint_digest = declared_footprint_digest; + let delegated_budget = EchoOperationBudgetV1::new(8, 512, 512); + let consumed_budget = EchoOperationBudgetV1::new(4, 70, 40); + let prepared_patch_digest = digest(17); + let prepared_result_id = EchoOperationResultIdV1(digest(18)); + let private_evaluation_id = private_evaluation_id_from_parts( + installed.installed_operation_id, + installed.program_id, + invocation_admission_id, + invocation_id, + basis.identity(), + declared_footprint_digest, + actual_footprint_digest, + consumed_budget, + prepared_patch_digest, + prepared_result_id, + ); + let prepared_id = preparation_id( + private_evaluation_id, + prepared_patch_digest, + prepared_result_id, + ); + let composition_digest = singleton_composition_digest_from_parts( + prepared_id, + prepared_patch_digest, + prepared_result_id, + basis.identity(), + actual_footprint_digest, + ); + let mut receipt = EchoOperationReceiptV1 { + package_id: installed.package_id, + package_admission_id: installed.package_admission_id, + installed_operation_id: installed.installed_operation_id, + operation_coordinate: installed.operation_coordinate.clone(), + semantic_identity: installed.semantic_identity, + lawpack_identity: installed.lawpack_identity, + target_profile_identity: installed.target_profile_identity, + interpreter_profile_identity: installed.interpreter_profile_identity, + intrinsic_profile_identity: installed.intrinsic_profile_identity, + package_admission_policy_id: installed.admission_policy_id, + authority_profile_identity: installed.authority_profile_identity, + authority_grant_identity: digest(19), + invocation_admission_policy_id, + invocation_admission_maximum_budget, + invocation_admission_id, + program_id: installed.program_id, + invocation_id, + invocation_bytes_digest: digest(20), + evaluation_basis: basis, + evaluation_basis_id: basis.identity(), + declared_footprint_digest, + actual_footprint_digest, + delegated_budget, + consumed_budget, + private_evaluation_id, + preparation_id: prepared_id, + prepared_patch_digest, + prepared_result_id, + committed_patch_digest: Some(prepared_patch_digest), + committed_result_id: Some(prepared_result_id), + state_root_before: basis.state_root, + state_root_after: digest(24), + commit_id: digest(25), + composition_digest: Some(composition_digest), + tick_receipt_digest: digest(27), + commit_global_tick: Some(GlobalTick::from_raw(1)), + worldline_tick_after: WorldlineTick::from_raw(1), + terminal_posture: EchoOperationTerminalPostureV1::Committed, + terminal_outcome_digest: [0; 32], + receipt_digest: [0; 32], + }; + receipt.terminal_outcome_digest = terminal_outcome_digest(&receipt); + receipt.receipt_digest = receipt_digest(&receipt); + validate_receipt_installation_v1(&receipt, &installed) + .expect("the retained receipt fixture matches its installation"); + let mut mismatched_policy_receipt = receipt.clone(); + mismatched_policy_receipt.invocation_admission_maximum_budget = + EchoOperationBudgetV1::new(9, 512, 512); + mismatched_policy_receipt.receipt_digest = receipt_digest(&mismatched_policy_receipt); + let mismatched_policy_bytes = mismatched_policy_receipt + .to_canonical_bytes() + .expect("mismatched policy receipt retains structurally"); + let error = recover_committed_execution_receipt_v1(&mismatched_policy_bytes) + .expect_err("retained invocation-admission policy material must match its identity"); + assert_eq!( + error.kind(), + EchoOperationArtifactErrorKindV1::InvalidStructure + ); + + let mut forged_private_evaluation = receipt.clone(); + forged_private_evaluation.private_evaluation_id = + EchoOperationPrivateEvaluationIdV1(digest(90)); + forged_private_evaluation.preparation_id = preparation_id( + forged_private_evaluation.private_evaluation_id, + forged_private_evaluation.prepared_patch_digest, + forged_private_evaluation.prepared_result_id, + ); + forged_private_evaluation.composition_digest = + Some(singleton_composition_digest_from_parts( + forged_private_evaluation.preparation_id, + forged_private_evaluation.prepared_patch_digest, + forged_private_evaluation.prepared_result_id, + forged_private_evaluation.evaluation_basis_id, + forged_private_evaluation.actual_footprint_digest, + )); + forged_private_evaluation.terminal_outcome_digest = + terminal_outcome_digest(&forged_private_evaluation); + forged_private_evaluation.receipt_digest = receipt_digest(&forged_private_evaluation); + let forged_private_bytes = forged_private_evaluation + .to_canonical_bytes() + .expect("coordinated private-evaluation substitution encodes"); + recover_committed_execution_receipt_v1(&forged_private_bytes) + .expect_err("recovery must independently derive private-evaluation identity"); + + let mut forged_composition = receipt.clone(); + forged_composition.composition_digest = Some(digest(91)); + forged_composition.terminal_outcome_digest = terminal_outcome_digest(&forged_composition); + forged_composition.receipt_digest = receipt_digest(&forged_composition); + let forged_composition_bytes = forged_composition + .to_canonical_bytes() + .expect("coordinated composition substitution encodes"); + recover_committed_execution_receipt_v1(&forged_composition_bytes) + .expect_err("recovery must independently derive singleton composition identity"); + + let mut impossible_budget = receipt.clone(); + impossible_budget.delegated_budget = EchoOperationBudgetV1::new(1, 1, 1); + impossible_budget.receipt_digest = receipt_digest(&impossible_budget); + let impossible_budget_bytes = impossible_budget + .to_canonical_bytes() + .expect("inconsistent budget evidence encodes"); + recover_committed_execution_receipt_v1(&impossible_budget_bytes) + .expect_err("consumption cannot exceed the retained delegated budget"); + + let mut below_program_minimum = receipt.clone(); + below_program_minimum.delegated_budget = EchoOperationBudgetV1::new(1, 1, 1); + below_program_minimum.consumed_budget = EchoOperationBudgetV1::new(1, 1, 1); + below_program_minimum.private_evaluation_id = private_evaluation_id_from_parts( + below_program_minimum.installed_operation_id, + below_program_minimum.program_id, + below_program_minimum.invocation_admission_id, + below_program_minimum.invocation_id, + below_program_minimum.evaluation_basis_id, + below_program_minimum.declared_footprint_digest, + below_program_minimum.actual_footprint_digest, + below_program_minimum.consumed_budget, + below_program_minimum.prepared_patch_digest, + below_program_minimum.prepared_result_id, + ); + below_program_minimum.preparation_id = preparation_id( + below_program_minimum.private_evaluation_id, + below_program_minimum.prepared_patch_digest, + below_program_minimum.prepared_result_id, + ); + below_program_minimum.composition_digest = Some(singleton_composition_digest_from_parts( + below_program_minimum.preparation_id, + below_program_minimum.prepared_patch_digest, + below_program_minimum.prepared_result_id, + below_program_minimum.evaluation_basis_id, + below_program_minimum.actual_footprint_digest, + )); + below_program_minimum.terminal_outcome_digest = + terminal_outcome_digest(&below_program_minimum); + below_program_minimum.receipt_digest = receipt_digest(&below_program_minimum); + let below_program_minimum_bytes = below_program_minimum + .to_canonical_bytes() + .expect("coordinated low-budget evidence encodes"); + let recovered_below_program_minimum = + recover_committed_execution_receipt_v1(&below_program_minimum_bytes) + .expect("receipt self-validation cannot inspect an installed program"); + validate_receipt_installation_v1(&recovered_below_program_minimum, &installed) + .expect_err("retained delegation must still cover the installed program minimum"); + + let mut foreign_before_root = receipt.clone(); + foreign_before_root.state_root_before = digest(92); + foreign_before_root.terminal_outcome_digest = terminal_outcome_digest(&foreign_before_root); + foreign_before_root.receipt_digest = receipt_digest(&foreign_before_root); + let foreign_before_root_bytes = foreign_before_root + .to_canonical_bytes() + .expect("foreign before-root evidence encodes"); + recover_committed_execution_receipt_v1(&foreign_before_root_bytes) + .expect_err("committed evidence must start at the retained evaluation basis root"); + + let bytes = receipt.to_canonical_bytes().expect("receipt retains"); + let substituted = replace_map_field( + &bytes, + "commit_id", + CanonicalValueV1::Bytes(digest(100).to_vec()), + ); + let error = recover_committed_execution_receipt_v1(&substituted) + .expect_err("a retained receipt field cannot change under its digest"); + assert_eq!( + error.kind(), + EchoOperationArtifactErrorKindV1::InvalidStructure + ); + } +} diff --git a/crates/warp-core/src/engine_impl.rs b/crates/warp-core/src/engine_impl.rs index aec739b1..aea41993 100644 --- a/crates/warp-core/src/engine_impl.rs +++ b/crates/warp-core/src/engine_impl.rs @@ -13,6 +13,10 @@ use crate::contract_registry::{ InstalledContractPackageError, }; use crate::contract_registry::{InstalledContractPackageId, InstalledContractPackageRecord}; +use crate::echo_operation::{ + install_recovered_v1, EchoOperationInstallationErrorV1, EchoOperationPackageIdV1, + InstalledEchoOperationV1, +}; use crate::graph::GraphStore; use crate::graph_view::GraphView; use crate::head_inbox::{IngressEnvelope, IngressPayload, IntentKind}; @@ -436,6 +440,12 @@ pub struct Engine { #[cfg_attr(not(feature = "native_rule_bootstrap"), allow(dead_code))] installed_contract_packages: BTreeMap, + installed_echo_operation_packages: BTreeMap, + #[cfg_attr( + not(all(feature = "native_rule_bootstrap", feature = "trusted_runtime")), + allow(dead_code) + )] + installed_echo_operations_by_coordinate: BTreeMap, #[cfg(all(feature = "native_rule_bootstrap", feature = "trusted_runtime"))] installed_provider_contract_packages: BTreeMap, @@ -878,6 +888,8 @@ impl Engine { rules_by_compact: BTreeMap::new(), canonical_cmd_rules: Vec::new(), installed_contract_packages: BTreeMap::new(), + installed_echo_operation_packages: BTreeMap::new(), + installed_echo_operations_by_coordinate: BTreeMap::new(), #[cfg(all(feature = "native_rule_bootstrap", feature = "trusted_runtime"))] installed_provider_contract_packages: BTreeMap::new(), #[cfg(all(feature = "native_rule_bootstrap", feature = "trusted_runtime"))] @@ -1078,6 +1090,8 @@ impl Engine { rules_by_compact: BTreeMap::new(), canonical_cmd_rules: Vec::new(), installed_contract_packages: BTreeMap::new(), + installed_echo_operation_packages: BTreeMap::new(), + installed_echo_operations_by_coordinate: BTreeMap::new(), #[cfg(all(feature = "native_rule_bootstrap", feature = "trusted_runtime"))] installed_provider_contract_packages: BTreeMap::new(), #[cfg(all(feature = "native_rule_bootstrap", feature = "trusted_runtime"))] @@ -1276,6 +1290,68 @@ impl Engine { Ok(prepared.record) } + /// Returns one installed executable operation package by exact package id. + #[must_use] + pub fn installed_echo_operation_package_v1( + &self, + package_id: EchoOperationPackageIdV1, + ) -> Option<&InstalledEchoOperationV1> { + self.installed_echo_operation_packages.get(&package_id) + } + + #[cfg_attr( + not(all(feature = "native_rule_bootstrap", feature = "trusted_runtime")), + allow(dead_code) + )] + pub(crate) fn preflight_recovered_echo_operation_packages_v1( + &self, + recovered: &[InstalledEchoOperationV1], + ) -> Result<(), EchoOperationInstallationErrorV1> { + let mut packages = self.installed_echo_operation_packages.clone(); + let mut operations = self.installed_echo_operations_by_coordinate.clone(); + for installed in recovered { + install_recovered_v1(&mut packages, &mut operations, installed.clone())?; + } + Ok(()) + } + + #[cfg_attr( + not(all(feature = "native_rule_bootstrap", feature = "trusted_runtime")), + allow(dead_code) + )] + pub(crate) fn restore_recovered_echo_operation_packages_v1( + &mut self, + recovered: &[InstalledEchoOperationV1], + ) -> Result<(), EchoOperationInstallationErrorV1> { + let mut packages = self.installed_echo_operation_packages.clone(); + let mut operations = self.installed_echo_operations_by_coordinate.clone(); + for installed in recovered { + install_recovered_v1(&mut packages, &mut operations, installed.clone())?; + } + self.installed_echo_operation_packages = packages; + self.installed_echo_operations_by_coordinate = operations; + Ok(()) + } + + #[cfg_attr( + not(all(feature = "native_rule_bootstrap", feature = "trusted_runtime")), + allow(dead_code) + )] + pub(crate) fn installed_echo_operation_packages_v1( + &self, + ) -> impl Iterator { + self.installed_echo_operation_packages.values() + } + + /// Returns the engine-owned policy id used by operation patches. + #[cfg_attr( + not(all(feature = "native_rule_bootstrap", feature = "trusted_runtime")), + allow(dead_code) + )] + pub(crate) const fn echo_operation_policy_id(&self) -> u32 { + self.policy_id + } + /// Installs one provider-native contract package through the trusted runtime owner. /// /// The caller supplies an already admitted proposal and a package-root claim. diff --git a/crates/warp-core/src/evidence.rs b/crates/warp-core/src/evidence.rs index fa00a3e9..f3c816eb 100644 --- a/crates/warp-core/src/evidence.rs +++ b/crates/warp-core/src/evidence.rs @@ -361,6 +361,16 @@ mod tests { WalRecordKind::CausalAnchorFactRecorded, AffectedFrontierKind::CausalAnchorIndex, ), + WalTransactionKind::ExecutableOperationInstallation => ( + WalAppendAuthority::RuntimeControl, + WalRecordKind::ExecutableOperationPackageInstalled, + AffectedFrontierKind::ExecutableOperationCatalog, + ), + WalTransactionKind::ExecutableOperationTick => ( + WalAppendAuthority::ExecutionKernel, + WalRecordKind::ExecutableOperationExecutionRecorded, + AffectedFrontierKind::ExecutableOperationReceiptIndex, + ), } } diff --git a/crates/warp-core/src/lib.rs b/crates/warp-core/src/lib.rs index 9a8aecd4..2a5b5cd9 100644 --- a/crates/warp-core/src/lib.rs +++ b/crates/warp-core/src/lib.rs @@ -61,6 +61,11 @@ mod contract_registry; /// Domain separation prefixes for hashing. pub mod domain; mod dynamic_binding; +#[cfg_attr( + not(all(feature = "native_rule_bootstrap", feature = "trusted_runtime")), + allow(dead_code) +)] +mod echo_operation; mod edict_target_ir; mod engine_impl; pub mod evidence; @@ -228,6 +233,26 @@ pub use dynamic_binding::{ ResolvedClosureBinding, ResolvedSlotBinding, StructuredBindingResolver, StructuredBindingRuntime, StructuredRuntimeBindings, }; +pub use echo_operation::{ + echo_operation_anchored_node_application_basis_v1, echo_operation_atom_value_digest_v1, + echo_operation_package_id_v1, echo_operation_target_profile_identity_v1, + AdmittedEchoOperationInvocationV1, AdmittedExecutableOperationPackageV1, + EchoOperationAdmissionErrorKindV1, EchoOperationAdmissionErrorV1, + EchoOperationAdmissionPolicyV1, EchoOperationApplicationBasisV1, + EchoOperationArtifactErrorKindV1, EchoOperationArtifactErrorV1, EchoOperationBudgetV1, + EchoOperationCommitErrorV1, EchoOperationEvaluationBasisIdV1, EchoOperationEvaluationBasisV1, + EchoOperationExecutionEvidenceV1, EchoOperationFootprintContractV1, + EchoOperationInstallationErrorKindV1, EchoOperationInstallationErrorV1, + EchoOperationInvocationAdmissionErrorKindV1, EchoOperationInvocationAdmissionErrorV1, + EchoOperationInvocationAdmissionIdV1, EchoOperationInvocationAdmissionPolicyV1, + EchoOperationInvocationIdV1, EchoOperationInvocationV1, EchoOperationObstructionIdV1, + EchoOperationObstructionKindV1, EchoOperationObstructionV1, EchoOperationPackageAdmissionIdV1, + EchoOperationPackageIdV1, EchoOperationPreparationV1, EchoOperationPrivateEvaluationIdV1, + EchoOperationProgramIdV1, EchoOperationProgramV1, EchoOperationReceiptV1, + EchoOperationResultIdV1, EchoOperationSemanticClosureV1, EchoOperationTerminalPostureV1, + ExecutableOperationPackageV1, InstalledEchoOperationIdV1, InstalledEchoOperationV1, + PreparedEchoOperationIdV1, PreparedEchoOperationV1, +}; pub use edict_target_ir::{ accept_edict_echo_target_ir, execute_accepted_edict_echo_target_ir, AcceptedEdictEchoTargetIr, EdictEchoAttemptInput, EdictEchoAttemptObstruction, EdictEchoAttemptOutcomeKind, diff --git a/crates/warp-core/src/trusted_runtime_host.rs b/crates/warp-core/src/trusted_runtime_host.rs index 1071d029..5f7f6796 100644 --- a/crates/warp-core/src/trusted_runtime_host.rs +++ b/crates/warp-core/src/trusted_runtime_host.rs @@ -18,7 +18,9 @@ use crate::causal_anchor::prepare_causal_anchor_admission; use crate::{ causal_wal::{ - build_causal_anchor_admission_transaction, build_recovery_certificate, + affected_frontiers_root, build_causal_anchor_admission_transaction, + build_executable_operation_installation_transaction, + build_executable_operation_tick_transaction, build_recovery_certificate, build_replayable_tick_transaction, build_submission_acceptance_with_material_transaction, causal_anchor_frontier_digest_from_evidence, causal_anchor_genesis_frontier_digest, causal_history_genesis_frontier_digest, logical_causal_history_frontier_digest, @@ -37,22 +39,38 @@ use crate::{ TRUSTED_RUNTIME_WAL_DOMAIN, }, contract_host::{decode_canonical_eint, encode_canonical_eint}, + echo_operation::{ + admit_invocation_v1, admit_package_v1, commit_prepared_to_state, + decode_invocation_route_v1, genesis_commit_id, install_recovered_v1, + installed_from_admitted, not_committed_basis_changed, + not_committed_evaluation_authority_mismatch, not_committed_installation_unavailable, + prepare_operation_v1, recover_committed_execution_receipt_v1, recover_installation_v1, + retain_committed_execution_v1, retain_installation_v1, validate_receipt_installation_v1, + EchoOperationEvaluationAuthorityV1, + }, provider_contract::admit_provider_contract_package_v1, + AdmittedEchoOperationInvocationV1, AdmittedExecutableOperationPackageV1, AdmittedProviderContractPackageV1, CausalAnchorAdmissionRequest, CausalAnchorClaim, CausalAnchorError, CausalAnchorId, CausalAnchorRootSupportPolicy, CausalAnchorSupportError, CausalFrontierRef, ContractInverseAdmissionRequest, ContractInverseContext, ContractInverseDerivation, ContractInverseHistoryObstruction, ContractInverseObstruction, - ContractOperationKind, Engine, IngressCausalParent, IngressEnvelope, - IngressEnvelopeDecodeError, IngressPayload, IngressSubmissionGeneration, - InstalledContractPackage, InstalledContractPackageError, InstalledContractPackageRecord, + ContractOperationKind, EchoOperationAdmissionErrorV1, EchoOperationAdmissionPolicyV1, + EchoOperationApplicationBasisV1, EchoOperationArtifactErrorV1, EchoOperationCommitErrorV1, + EchoOperationEvaluationBasisV1, EchoOperationExecutionEvidenceV1, + EchoOperationInstallationErrorV1, EchoOperationInvocationAdmissionErrorV1, + EchoOperationInvocationAdmissionPolicyV1, EchoOperationPreparationV1, EchoOperationReceiptV1, + Engine, IngressCausalParent, IngressEnvelope, IngressEnvelopeDecodeError, IngressPayload, + IngressSubmissionGeneration, InstalledContractPackage, InstalledContractPackageError, + InstalledContractPackageRecord, InstalledEchoOperationV1, InstalledProviderContractPackageRecordV1, IntentOutcome, IntentOutcomeDecision, IntentOutcomeObservation, IntentSubmissionHandle, IntentSubmissionRecord, ObservationArtifact, ObservationError, ObservationRequest, ObservationService, OpticAdmissionTicket, - ProvenanceEntry, ProvenanceService, ProvenanceStore, ProviderContractAdmissionError, - ProviderContractAdmissionPolicyV1, ProviderContractInstallationError, - ProviderContractPackageInstallerV1, ProviderContractPackageProposalV1, - ProviderPackageReferenceV1, ReceiptCorrelationPersistenceRecord, ReceiptCorrelationRecord, - RetainedProvenanceError, RuntimeError, SchedulerCoordinator, StepRecord, TickReceiptRejection, + PreparedEchoOperationV1, ProvenanceEntry, ProvenanceService, ProvenanceStore, + ProviderContractAdmissionError, ProviderContractAdmissionPolicyV1, + ProviderContractInstallationError, ProviderContractPackageInstallerV1, + ProviderContractPackageProposalV1, ProviderPackageReferenceV1, + ReceiptCorrelationPersistenceRecord, ReceiptCorrelationRecord, RetainedProvenanceError, + RuntimeError, SchedulerCoordinator, StepRecord, TickReceiptRejection, TicketedRuntimeIngressAuthority, TicketedRuntimeIngressDisposition, WitnessedSubmissionPersistenceRecord, WitnessedSubmissionPersistenceSnapshot, WorldlineRuntime, }; @@ -107,6 +125,15 @@ pub enum TrustedRuntimeHostError { /// Maximum scheduler passes the caller allowed. max_scheduler_passes: u64, }, + /// A privately evaluated operation could not produce complete commit material. + #[error("trusted runtime host executable-operation commit error: {0}")] + EchoOperationCommit(#[from] EchoOperationCommitErrorV1), + /// Exact executable-operation retained material was malformed. + #[error("trusted runtime host executable-operation artifact error: {0}")] + EchoOperationArtifact(#[from] EchoOperationArtifactErrorV1), + /// Executable-operation installation conflicted with installed authority. + #[error("trusted runtime host executable-operation installation error: {0}")] + EchoOperationInstallation(#[from] EchoOperationInstallationErrorV1), } impl From for TrustedRuntimeHostError { @@ -182,6 +209,38 @@ pub enum TrustedRuntimeWalError { /// Tick whose retained transition conflicted. worldline_tick: crate::WorldlineTick, }, + /// Retained executable-operation material failed canonical self-validation. + #[error("trusted runtime WAL executable-operation artifact error: {0}")] + EchoOperationArtifact(#[from] EchoOperationArtifactErrorV1), + /// Recovered executable-operation installations conflicted. + #[error("trusted runtime WAL executable-operation installation error: {0}")] + EchoOperationInstallation(#[from] EchoOperationInstallationErrorV1), + /// More than one committed transaction retained the same typed operation receipt. + #[error( + "trusted runtime WAL recovered duplicate executable-operation receipt {receipt_digest:?}" + )] + EchoOperationReceiptConflict { + /// Content identity claimed by more than one operation-tick transaction. + receipt_digest: Hash, + }, + /// A committed operation receipt disagreed with its retained state transition. + #[error("trusted runtime WAL executable-operation receipt/state mismatch: {detail}")] + EchoOperationExecutionMismatch { + /// Stable explanation of the mismatched commitments. + detail: &'static str, + }, + /// A committed operation transaction carried the wrong exact frontier transition. + #[error( + "trusted runtime WAL executable-operation frontier mismatched transaction {transaction_id:?}: expected {expected:?}, actual {actual:?}" + )] + EchoOperationFrontierMismatch { + /// Operation transaction whose frontier commitment was inconsistent. + transaction_id: Hash, + /// Root recomputed from the exact retained operation material. + expected: Hash, + /// Affected-frontier root carried by the committed transaction. + actual: Hash, + }, /// Causal-anchor recovery omitted evidence for an anchor transaction. #[error("trusted runtime WAL omitted recovered causal-anchor admission for transaction {transaction_id:?}")] CausalAnchorAdmissionMissing { @@ -289,6 +348,8 @@ pub enum RuntimeWalActivationGap { GlobalTick, /// A live worldline frontier differs from its retained provenance history. WorldlineState, + /// Installed executable meaning has no exact durable installation record. + ExecutableOperationInstallation, } /// Summary returned after a trusted host runs the scheduler until idle. @@ -368,6 +429,10 @@ pub struct TrustedRuntimeWalRecovery { pub receipt_correlations: Vec, /// Ordered Echo control history for committed causal-anchor admissions. pub causal_anchor_history: Vec, + /// Exact executable meaning reconstructed from retained installation records. + pub installed_echo_operations: Vec, + /// Typed receipts reconstructed from executable-operation tick records. + pub echo_operation_receipts: Vec, causal_history_frontiers: Vec, } @@ -387,6 +452,8 @@ impl TrustedRuntimeWalRecovery { provenance_entries: &self.provenance_entries, missing_runtime_state_deltas: &self.missing_runtime_state_deltas, causal_anchor_history: &self.causal_anchor_history, + installed_echo_operations: &self.installed_echo_operations, + echo_operation_receipts: &self.echo_operation_receipts, }) } @@ -538,6 +605,7 @@ pub struct TrustedRuntimeHost { engine: Engine, runtime_wal: Option, causal_anchor_support_policy: Option, + echo_operation_evaluation_authority: EchoOperationEvaluationAuthorityV1, } impl TrustedRuntimeHost { @@ -555,6 +623,7 @@ impl TrustedRuntimeHost { engine, runtime_wal: None, causal_anchor_support_policy: None, + echo_operation_evaluation_authority: EchoOperationEvaluationAuthorityV1::new(), }) } @@ -571,6 +640,7 @@ impl TrustedRuntimeHost { engine, runtime_wal: None, causal_anchor_support_policy: None, + echo_operation_evaluation_authority: EchoOperationEvaluationAuthorityV1::new(), } } @@ -598,6 +668,269 @@ impl TrustedRuntimeHost { &self.engine } + /// Admits exact executable-operation package bytes under independent policy. + /// + /// A successful result is an opaque admission token. It does not install + /// the package and the program digest inside the package does not confer an + /// operation coordinate, invocability, or authority. + pub fn admit_echo_operation_package_v1( + &self, + policy: &EchoOperationAdmissionPolicyV1, + canonical_package_bytes: Vec, + ) -> Result { + admit_package_v1(policy, canonical_package_bytes) + } + + /// Installs exact executable meaning carried by an admitted operation package. + /// + /// Installation stores no native callback and still does not authorize an + /// invocation. Application-facing [`TrustedRuntimeApp`] handles cannot call + /// this runtime-owner method. + pub fn install_admitted_echo_operation_package_v1( + &mut self, + admitted: AdmittedExecutableOperationPackageV1, + ) -> Result { + let installed = installed_from_admitted(admitted)?; + self.engine + .preflight_recovered_echo_operation_packages_v1(core::slice::from_ref(&installed))?; + if self + .engine + .installed_echo_operation_package_v1(installed.package_id()) + == Some(&installed) + { + return Ok(installed); + } + + if let Some(runtime_wal) = self.runtime_wal.as_mut() { + let retained = retain_installation_v1(&installed)?; + if let Err(error) = runtime_wal.record_executable_operation_installation(&retained) { + if !runtime_wal.recover_filesystem_executable_operation_installation_after_error( + installed.package_id(), + ) { + return Err(error.into()); + } + } + } + self.engine + .restore_recovered_echo_operation_packages_v1(core::slice::from_ref(&installed))?; + Ok(installed) + } + + /// Resolves the exact current parent basis for an operation invocation. + /// + /// The returned basis binds Echo's writer head, worldline tick, committing + /// global tick when one exists, graph state root, commit identity, and the + /// separately typed application basis proposition. + pub fn echo_operation_evaluation_basis_v1( + &self, + writer_head: crate::WriterHeadKey, + application_basis: EchoOperationApplicationBasisV1, + ) -> Result { + current_echo_operation_basis( + &self.runtime, + &self.provenance, + writer_head, + application_basis, + ) + } + + /// Independently admits a canonical installed-operation invocation. + /// + /// Admission checks exact installed package identity, public operation + /// coordinate, authority profile and grant, delegated budget, and current + /// parent basis. It does not evaluate or mutate the graph. + pub fn admit_echo_operation_invocation_v1( + &self, + policy: &EchoOperationInvocationAdmissionPolicyV1, + canonical_invocation_bytes: &[u8], + ) -> Result { + let (package_id, claimed_basis) = decode_invocation_route_v1(canonical_invocation_bytes)?; + let current_basis = self + .echo_operation_evaluation_basis_v1( + claimed_basis.writer_head(), + claimed_basis.application_basis(), + ) + .map_err(|error| crate::echo_operation::invocation_runtime_error(error.to_string()))?; + let current_state = self + .runtime + .worldlines() + .get(&claimed_basis.writer_head().worldline_id) + .map(crate::WorldlineFrontier::state) + .ok_or_else(|| { + crate::echo_operation::invocation_runtime_error( + "operation invocation worldline is unavailable", + ) + })?; + admit_invocation_v1( + self.engine.installed_echo_operation_package_v1(package_id), + *policy, + canonical_invocation_bytes, + current_basis, + current_state, + self.echo_operation_evaluation_authority.clone(), + ) + } + + /// Evaluates one admitted operation without mutating the parent state. + /// + /// Evaluation returns either one complete prepared patch or one typed + /// obstruction. No partial mutation reaches the parent worldline. + #[must_use] + pub fn prepare_echo_operation_v1( + &self, + admitted: AdmittedEchoOperationInvocationV1, + ) -> EchoOperationPreparationV1 { + let current_basis = self.echo_operation_evaluation_basis_v1( + admitted.evaluation_basis().writer_head(), + admitted.evaluation_basis().application_basis(), + ); + let Ok(current_basis) = current_basis else { + return crate::echo_operation::runtime_basis_obstruction(admitted); + }; + let state = self + .runtime + .worldlines() + .get(¤t_basis.writer_head().worldline_id) + .map(crate::WorldlineFrontier::state); + let Some(state) = state else { + return crate::echo_operation::runtime_basis_obstruction(admitted); + }; + prepare_operation_v1( + self.engine + .installed_echo_operation_package_v1(admitted.package_id()), + admitted, + current_basis, + state, + self.engine.echo_operation_policy_id(), + &self.echo_operation_evaluation_authority, + ) + } + + /// Commits one privately prepared patch only against its exact evaluation basis. + /// + /// A changed basis produces typed noncommit evidence and never retries, + /// rebases, revalidates, or exposes a partial patch. A successful crossing + /// appends one local provenance entry and advances one worldline tick. + pub fn commit_prepared_echo_operation_v1( + &mut self, + prepared: Box, + ) -> Result { + let prepared = *prepared; + let current_basis = self.echo_operation_evaluation_basis_v1( + prepared.evaluation_basis().writer_head(), + prepared.evaluation_basis().application_basis(), + )?; + if ¤t_basis != prepared.evaluation_basis() { + let frontier = self + .runtime + .worldlines() + .get(¤t_basis.writer_head().worldline_id) + .ok_or_else(|| { + RuntimeError::UnknownWorldline(current_basis.writer_head().worldline_id) + })?; + return Ok(not_committed_basis_changed( + &prepared, + frontier.state().state_root(), + frontier.frontier_tick(), + )); + } + if self + .engine + .installed_echo_operation_package_v1(prepared.package_id()) + .is_none_or(|installed| { + installed.installed_operation_id() != prepared.installed_operation_id() + }) + { + return Ok(not_committed_installation_unavailable( + &prepared, + current_basis.state_root(), + current_basis.worldline_tick(), + )); + } + if !prepared.is_owned_by(&self.echo_operation_evaluation_authority) { + return Ok(not_committed_evaluation_authority_mismatch( + &prepared, + current_basis.state_root(), + current_basis.worldline_tick(), + )); + } + + let mut next_runtime = self.runtime.clone(); + let mut next_provenance = self.provenance.clone(); + let commit_global_tick = next_runtime.advance_global_tick()?; + let worldline_id = current_basis.writer_head().worldline_id; + let parents = next_provenance.tip_ref(worldline_id)?.into_iter().collect(); + let worldline_tick = current_basis.worldline_tick(); + let (material, provenance_entry) = { + let frontier = next_runtime.frontier_mut(&worldline_id)?; + let material = + commit_prepared_to_state(&prepared, frontier.state_mut(), commit_global_tick)?; + let worldline_patch = crate::WorldlineTickPatchV1 { + header: crate::WorldlineTickHeaderV1 { + commit_global_tick, + policy_id: material.patch.policy_id(), + rule_pack_id: material.patch.rule_pack_id(), + plan_digest: material.snapshot.plan_digest, + decision_digest: material.snapshot.decision_digest, + rewrites_digest: material.snapshot.rewrites_digest, + }, + warp_id: material.snapshot.root.warp_id, + ops: material.patch.ops().to_vec(), + in_slots: material.patch.in_slots().to_vec(), + out_slots: material.patch.out_slots().to_vec(), + patch_digest: material.patch.digest(), + }; + let entry = ProvenanceEntry::local_commit( + worldline_id, + worldline_tick, + commit_global_tick, + current_basis.writer_head(), + parents, + crate::HashTriplet { + state_root: material.snapshot.state_root, + patch_digest: material.snapshot.patch_digest, + commit_hash: material.snapshot.hash, + }, + worldline_patch, + Vec::new(), + Vec::new(), + ) + .with_tick_receipt(material.tick_receipt.clone()); + next_provenance.append_local_commit(entry.clone())?; + frontier + .advance_tick() + .ok_or(RuntimeError::FrontierTickOverflow(worldline_id))?; + (material, entry) + }; + + if let Some(runtime_wal) = self.runtime_wal.as_mut() { + let state_delta = WalRuntimeStateDeltaRecord::from_provenance_entry( + material.tick_receipt.digest(), + None, + provenance_entry, + ) + .map_err(TrustedRuntimeWalError::from)?; + let state_delta_digest = state_delta.digest().map_err(TrustedRuntimeWalError::from)?; + let retained_execution = retain_committed_execution_v1(&material.evidence)?; + if let Err(error) = runtime_wal.record_executable_operation_tick( + material.evidence.receipt(), + retained_execution, + &state_delta, + state_delta_digest, + ) { + if !runtime_wal.recover_filesystem_executable_operation_tick_after_error( + material.evidence.receipt().digest(), + ) { + return Err(error.into()); + } + } + } + + self.runtime = next_runtime; + self.provenance = next_provenance; + Ok(material.evidence) + } + /// Enables the in-memory WAL adapter used by the reference host tests. /// /// This adapter proves the ACK ordering contract and recovery indexes. It @@ -628,7 +961,14 @@ impl TrustedRuntimeHost { if let Some(receipt_digest) = recovery.missing_runtime_state_deltas.first().copied() { return Err(TrustedRuntimeWalError::RuntimeStateDeltaMissing { receipt_digest }.into()); } - ensure_runtime_authority_is_durable(&self.runtime, &self.provenance, &recovery)?; + ensure_runtime_authority_is_durable( + &self.runtime, + &self.provenance, + &self.engine, + &recovery, + )?; + self.engine + .preflight_recovered_echo_operation_packages_v1(&recovery.installed_echo_operations)?; let mut restored_runtime = self.runtime.clone(); let mut restored_provenance = self.provenance.clone(); @@ -641,6 +981,8 @@ impl TrustedRuntimeHost { &recovery.receipt_correlations, )?; + self.engine + .restore_recovered_echo_operation_packages_v1(&recovery.installed_echo_operations)?; self.runtime = restored_runtime; self.provenance = restored_provenance; self.runtime_wal = Some(runtime_wal); @@ -1274,11 +1616,61 @@ impl ProviderContractPackageInstallerV1 for TrustedRuntimeHost { } } +fn current_echo_operation_basis( + runtime: &WorldlineRuntime, + provenance: &ProvenanceService, + writer_head: crate::WriterHeadKey, + application_basis: EchoOperationApplicationBasisV1, +) -> Result { + if runtime.heads().get(&writer_head).is_none() { + return Err(RuntimeError::UnknownHead(writer_head).into()); + } + let frontier = runtime + .worldlines() + .get(&writer_head.worldline_id) + .ok_or(RuntimeError::UnknownWorldline(writer_head.worldline_id))?; + let state_root = frontier.state().state_root(); + let (commit_global_tick, commit_id) = match provenance.tip_ref(writer_head.worldline_id)? { + Some(tip) => { + let entry = provenance.entry(tip.worldline_id, tip.worldline_tick)?; + (Some(entry.commit_global_tick), entry.expected.commit_hash) + } + None => (None, genesis_commit_id(writer_head, state_root)), + }; + Ok(EchoOperationEvaluationBasisV1::new( + writer_head, + frontier.frontier_tick(), + commit_global_tick, + state_root, + commit_id, + application_basis, + )) +} + fn ensure_runtime_authority_is_durable( runtime: &WorldlineRuntime, provenance: &ProvenanceService, + engine: &Engine, recovery: &TrustedRuntimeWalRecovery, ) -> Result<(), TrustedRuntimeWalError> { + let recovered_operations = recovery + .installed_echo_operations + .iter() + .map(|installed| (installed.package_id(), installed)) + .collect::>(); + if engine + .installed_echo_operation_packages_v1() + .any(|installed| { + recovered_operations + .get(&installed.package_id()) + .is_none_or(|recovered| **recovered != *installed) + }) + { + return Err(TrustedRuntimeWalError::RuntimeAuthorityNotDurable { + gap: RuntimeWalActivationGap::ExecutableOperationInstallation, + }); + } + let recovered_submissions = recovery .witnessed_submissions .records() @@ -1481,6 +1873,8 @@ pub struct TrustedRuntimeWal { submission_frontier_digest: Hash, receipt_frontier_digest: Hash, runtime_state_frontier_digest: Hash, + executable_operation_catalog_frontier_digest: Hash, + executable_operation_receipt_frontier_digest: Hash, causal_anchor_frontier_digest: Hash, causal_history_frontier_digest: Hash, causal_anchor_claim_projection: CausalAnchorClaimProjection, @@ -1541,6 +1935,10 @@ impl TrustedRuntimeWal { submission_frontier_digest: recovered_cursor.submission_frontier_digest, receipt_frontier_digest: recovered_cursor.receipt_frontier_digest, runtime_state_frontier_digest: recovered_cursor.runtime_state_frontier_digest, + executable_operation_catalog_frontier_digest: recovered_cursor + .executable_operation_catalog_frontier_digest, + executable_operation_receipt_frontier_digest: recovered_cursor + .executable_operation_receipt_frontier_digest, causal_anchor_frontier_digest: recovered_cursor.causal_anchor_frontier_digest, causal_history_frontier_digest: recovered_cursor.causal_history_frontier_digest, causal_anchor_claim_projection: recovered_cursor.causal_anchor_claim_projection, @@ -1592,9 +1990,13 @@ impl TrustedRuntimeWal { let (witnessed_submissions, missing_submission_envelopes) = recover_witnessed_submission_material(&report, &submissions)?; let runtime_state = recover_runtime_state_delta_material(&report)?; + let operation_material = + recover_echo_operation_material(&report, &runtime_state.provenance_entries)?; let provenance_entries = runtime_state.provenance_entries; let receipt_correlations = runtime_state.receipt_correlations; let missing_runtime_state_deltas = runtime_state.missing_runtime_state_deltas; + let installed_echo_operations = operation_material.installations; + let echo_operation_receipts = operation_material.receipts; validate_recovered_causal_parent_evidence(&witnessed_submissions, &receipt_correlations)?; let certificate = runtime_wal_recovery_certificate( &report, @@ -1606,6 +2008,8 @@ impl TrustedRuntimeWal { provenance_entries: &provenance_entries, missing_runtime_state_deltas: &missing_runtime_state_deltas, causal_anchor_history: &causal_anchor_history, + installed_echo_operations: &installed_echo_operations, + echo_operation_receipts: &echo_operation_receipts, }, )?; Ok(TrustedRuntimeWalRecovery { @@ -1618,6 +2022,8 @@ impl TrustedRuntimeWal { missing_runtime_state_deltas, receipt_correlations, causal_anchor_history, + installed_echo_operations, + echo_operation_receipts, causal_history_frontiers, }) } @@ -1749,6 +2155,10 @@ impl TrustedRuntimeWal { self.submission_frontier_digest = cursor.submission_frontier_digest; self.receipt_frontier_digest = cursor.receipt_frontier_digest; self.runtime_state_frontier_digest = cursor.runtime_state_frontier_digest; + self.executable_operation_catalog_frontier_digest = + cursor.executable_operation_catalog_frontier_digest; + self.executable_operation_receipt_frontier_digest = + cursor.executable_operation_receipt_frontier_digest; self.causal_anchor_frontier_digest = cursor.causal_anchor_frontier_digest; self.causal_history_frontier_digest = cursor.causal_history_frontier_digest; self.causal_anchor_claim_projection = cursor.causal_anchor_claim_projection; @@ -1806,6 +2216,42 @@ impl TrustedRuntimeWal { .flatten() } + fn recover_filesystem_executable_operation_installation_after_error( + &mut self, + package_id: crate::EchoOperationPackageIdV1, + ) -> bool { + if !self.uses_filesystem_store() { + return false; + } + if self.refresh_cursor_from_store_for_writer().is_err() { + return false; + } + self.recover_read_only().is_ok_and(|recovery| { + recovery + .installed_echo_operations + .iter() + .any(|installed| installed.package_id() == package_id) + }) + } + + fn recover_filesystem_executable_operation_tick_after_error( + &mut self, + receipt_digest: Hash, + ) -> bool { + if !self.uses_filesystem_store() { + return false; + } + if self.refresh_cursor_from_store_for_writer().is_err() { + return false; + } + self.recover_read_only().is_ok_and(|recovery| { + recovery + .echo_operation_receipts + .iter() + .any(|receipt| receipt.digest() == receipt_digest) + }) + } + fn record_submission_acceptance( &mut self, envelope: &IngressEnvelope, @@ -1892,6 +2338,100 @@ impl TrustedRuntimeWal { Ok(commit) } + fn record_executable_operation_installation( + &mut self, + retained_installation_bytes: &[u8], + ) -> Result { + let installed = recover_installation_v1(retained_installation_bytes)?; + let next_catalog_frontier = executable_operation_catalog_frontier_digest( + self.executable_operation_catalog_frontier_digest, + installed.package_id(), + retained_installation_bytes, + ); + let transaction = build_executable_operation_installation_transaction( + self.builder( + WalTransactionKind::ExecutableOperationInstallation, + WalAppendAuthority::RuntimeControl, + WalTransactionId::from_hash(executable_operation_installation_transaction_digest( + self.executable_operation_catalog_frontier_digest, + installed.package_id(), + retained_installation_bytes, + )), + ), + retained_installation_bytes.to_vec(), + vec![AffectedFrontier { + kind: AffectedFrontierKind::ExecutableOperationCatalog, + before_digest: self.executable_operation_catalog_frontier_digest, + after_digest: next_catalog_frontier, + }], + )?; + let commit = self.append_transaction(transaction)?; + self.executable_operation_catalog_frontier_digest = next_catalog_frontier; + Ok(commit) + } + + fn record_executable_operation_tick( + &mut self, + receipt: &EchoOperationReceiptV1, + retained_execution_bytes: Vec, + state_delta: &WalRuntimeStateDeltaRecord, + state_delta_digest: Hash, + ) -> Result { + let retained_receipt = recover_committed_execution_receipt_v1(&retained_execution_bytes)?; + if &retained_receipt != receipt || state_delta.digest()? != state_delta_digest { + return Err(TrustedRuntimeWalError::EchoOperationExecutionMismatch { + detail: "live operation material disagrees with retained bytes", + }); + } + validate_operation_receipt_state_delta(receipt, state_delta)?; + let next_receipt_frontier = executable_operation_receipt_frontier_digest( + self.executable_operation_receipt_frontier_digest, + receipt.digest(), + ); + let entry = state_delta.provenance_entry(); + let worldline_tick_after = entry + .worldline_tick + .checked_add(1) + .ok_or(RetainedProvenanceError::Inconsistent("worldline tick"))?; + let next_runtime_frontier = runtime_state_frontier_digest_from_fields( + self.runtime_state_frontier_digest, + entry.expected.commit_hash, + state_delta_digest, + entry.commit_global_tick, + worldline_tick_after, + ); + let transaction = build_executable_operation_tick_transaction( + self.builder( + WalTransactionKind::ExecutableOperationTick, + WalAppendAuthority::ExecutionKernel, + WalTransactionId::from_hash(executable_operation_tick_transaction_digest( + self.executable_operation_receipt_frontier_digest, + self.runtime_state_frontier_digest, + receipt.digest(), + state_delta_digest, + )), + ), + retained_execution_bytes, + state_delta.to_payload_bytes()?, + vec![ + AffectedFrontier { + kind: AffectedFrontierKind::ExecutableOperationReceiptIndex, + before_digest: self.executable_operation_receipt_frontier_digest, + after_digest: next_receipt_frontier, + }, + AffectedFrontier { + kind: AffectedFrontierKind::RuntimeState, + before_digest: self.runtime_state_frontier_digest, + after_digest: next_runtime_frontier, + }, + ], + )?; + let commit = self.append_transaction(transaction)?; + self.executable_operation_receipt_frontier_digest = next_receipt_frontier; + self.runtime_state_frontier_digest = next_runtime_frontier; + Ok(commit) + } + fn record_causal_anchor_admission( &mut self, claim: CausalAnchorClaim, @@ -2242,6 +2782,8 @@ struct TrustedRuntimeWalCursor { submission_frontier_digest: Hash, receipt_frontier_digest: Hash, runtime_state_frontier_digest: Hash, + executable_operation_catalog_frontier_digest: Hash, + executable_operation_receipt_frontier_digest: Hash, causal_anchor_frontier_digest: Hash, causal_history_frontier_digest: Hash, causal_anchor_claim_projection: CausalAnchorClaimProjection, @@ -2265,6 +2807,12 @@ impl TrustedRuntimeWalCursor { submission_frontier_digest: trusted_runtime_wal_digest("submission-frontier:genesis"), receipt_frontier_digest: trusted_runtime_wal_digest("receipt-frontier:genesis"), runtime_state_frontier_digest: trusted_runtime_wal_digest("runtime-frontier:genesis"), + executable_operation_catalog_frontier_digest: trusted_runtime_wal_digest( + "executable-operation-catalog-frontier:genesis", + ), + executable_operation_receipt_frontier_digest: trusted_runtime_wal_digest( + "executable-operation-receipt-frontier:genesis", + ), causal_anchor_frontier_digest: causal_anchor_genesis_frontier_digest(), causal_history_frontier_digest: causal_history_genesis_frontier_digest(), causal_anchor_claim_projection: CausalAnchorClaimProjection::default(), @@ -2319,6 +2867,63 @@ impl TrustedRuntimeWalCursor { ), }; } + WalTransactionKind::ExecutableOperationInstallation => { + let (installed, retained_bytes) = + operation_installation_from_transaction(transaction)?; + let catalog_before = cursor.executable_operation_catalog_frontier_digest; + let catalog_after = executable_operation_catalog_frontier_digest( + catalog_before, + installed.package_id(), + &retained_bytes, + ); + validate_echo_operation_frontier_root( + transaction, + &[AffectedFrontier { + kind: AffectedFrontierKind::ExecutableOperationCatalog, + before_digest: catalog_before, + after_digest: catalog_after, + }], + )?; + cursor.executable_operation_catalog_frontier_digest = catalog_after; + } + WalTransactionKind::ExecutableOperationTick => { + let (receipt, state_delta, state_delta_digest) = + operation_tick_records_from_transaction(transaction)?; + let receipt_before = cursor.executable_operation_receipt_frontier_digest; + let receipt_after = executable_operation_receipt_frontier_digest( + receipt_before, + receipt.digest(), + ); + let entry = state_delta.provenance_entry(); + let runtime_before = cursor.runtime_state_frontier_digest; + let runtime_after = runtime_state_frontier_digest_from_fields( + runtime_before, + entry.expected.commit_hash, + state_delta_digest, + entry.commit_global_tick, + entry + .worldline_tick + .checked_add(1) + .ok_or(RetainedProvenanceError::Inconsistent("worldline tick"))?, + ); + validate_echo_operation_frontier_root( + transaction, + &[ + AffectedFrontier { + kind: AffectedFrontierKind::ExecutableOperationReceiptIndex, + before_digest: receipt_before, + after_digest: receipt_after, + }, + AffectedFrontier { + kind: AffectedFrontierKind::RuntimeState, + before_digest: runtime_before, + after_digest: runtime_after, + }, + ], + )?; + cursor.executable_operation_receipt_frontier_digest = receipt_after; + cursor.runtime_state_frontier_digest = runtime_after; + } _ => {} } } @@ -2499,6 +3104,215 @@ fn restore_provenance_entries( Ok(()) } +struct RecoveredEchoOperationMaterial { + installations: Vec, + receipts: Vec, +} + +fn recover_echo_operation_material( + report: &RecoveryScanReport, + provenance_entries: &[ProvenanceEntry], +) -> Result { + let mut installations = BTreeMap::new(); + let mut operations = BTreeMap::new(); + let mut receipts_by_digest = BTreeMap::new(); + let mut receipts = Vec::new(); + let provenance_by_coordinate = provenance_entries + .iter() + .map(|entry| ((entry.worldline_id, entry.worldline_tick), entry)) + .collect::>(); + + for transaction in &report.transactions { + match transaction.commit.transaction_kind { + WalTransactionKind::ExecutableOperationInstallation => { + let (installed, _) = operation_installation_from_transaction(transaction)?; + install_recovered_v1(&mut installations, &mut operations, installed)?; + } + WalTransactionKind::ExecutableOperationTick => { + let (receipt, state_delta, _) = + operation_tick_records_from_transaction(transaction)?; + let installed = installations.get(&receipt.package_id()).ok_or( + TrustedRuntimeWalError::EchoOperationExecutionMismatch { + detail: "operation receipt precedes its exact package installation", + }, + )?; + validate_receipt_installation_v1(&receipt, installed)?; + validate_operation_receipt_state_delta(&receipt, &state_delta)?; + validate_operation_receipt_parent_material( + receipt.evaluation_basis(), + state_delta.provenance_entry(), + &provenance_by_coordinate, + )?; + if receipts_by_digest + .insert(receipt.digest(), transaction.commit.transaction_id) + .is_some() + { + return Err(TrustedRuntimeWalError::EchoOperationReceiptConflict { + receipt_digest: receipt.digest(), + }); + } + receipts.push(receipt); + } + _ => {} + } + } + + Ok(RecoveredEchoOperationMaterial { + installations: installations.into_values().collect(), + receipts, + }) +} + +fn operation_patch_scope_v1(patch: &crate::WorldlineTickPatchV1) -> Option { + let [crate::WarpOp::SetAttachment { key, .. }] = patch.ops.as_slice() else { + return None; + }; + let crate::AttachmentOwner::Node(node) = key.owner else { + return None; + }; + let attachment_slot = crate::AttachmentKey::node_alpha(node); + if *key != attachment_slot + || patch.warp_id != node.warp_id + || patch.in_slots.as_slice() + != [ + crate::SlotId::Node(node), + crate::SlotId::Attachment(attachment_slot), + ] + || patch.out_slots.as_slice() != [crate::SlotId::Attachment(attachment_slot)] + { + return None; + } + Some(node) +} + +fn operation_tick_binds_patch_v1( + tick_receipt: &crate::TickReceipt, + patch: &crate::WorldlineTickPatchV1, + expected_rule_id: Hash, +) -> bool { + let Some(expected_scope) = operation_patch_scope_v1(patch) else { + return false; + }; + matches!( + tick_receipt.entries(), + [tick_entry] + if patch.rule_pack_id() == expected_rule_id + && tick_entry.rule_id == expected_rule_id + && tick_entry.scope == expected_scope + && tick_entry.scope_hash == crate::scope_hash(&expected_rule_id, &expected_scope) + && tick_entry.disposition == crate::TickReceiptDisposition::Applied + && tick_receipt.blocked_by(0).is_empty() + ) +} + +fn validate_operation_receipt_state_delta( + receipt: &EchoOperationReceiptV1, + state_delta: &WalRuntimeStateDeltaRecord, +) -> Result<(), TrustedRuntimeWalError> { + let entry = state_delta.provenance_entry(); + let basis = receipt.evaluation_basis(); + let expected_rule_id = receipt.installed_operation_id().as_hash(); + let patch_binds_installation = entry.patch.as_ref().is_some_and(|patch| { + patch.rule_pack_id() == expected_rule_id + && receipt.committed_patch_digest() == Some(patch.patch_digest) + }); + let tick_binds_installation = entry + .patch + .as_ref() + .zip(entry.tick_receipt.as_ref()) + .is_some_and(|(patch, tick_receipt)| { + operation_tick_binds_patch_v1(tick_receipt, patch, expected_rule_id) + }); + let worldline_tick_after = entry + .worldline_tick + .checked_add(1) + .ok_or(RetainedProvenanceError::Inconsistent("worldline tick"))?; + if state_delta.contract().is_some() + || entry.event_kind != crate::ProvenanceEventKind::LocalCommit + || entry.worldline_id != basis.writer_head().worldline_id + || entry.head_key != Some(basis.writer_head()) + || entry.worldline_tick != basis.worldline_tick() + || receipt.tick_receipt_digest() != state_delta.receipt_digest() + || entry + .tick_receipt + .as_ref() + .is_none_or(|tick_receipt| tick_receipt.digest() != state_delta.receipt_digest()) + || !patch_binds_installation + || !tick_binds_installation + || receipt.commit_id() != entry.expected.commit_hash + || receipt.state_root_after() != entry.expected.state_root + || receipt.committed_patch_digest() != Some(entry.expected.patch_digest) + || receipt.commit_global_tick() != Some(entry.commit_global_tick) + || receipt.worldline_tick_after() != worldline_tick_after + { + return Err(TrustedRuntimeWalError::EchoOperationExecutionMismatch { + detail: "typed operation receipt disagrees with replayable provenance", + }); + } + let parent_matches_basis = if basis.worldline_tick() == crate::WorldlineTick::ZERO { + entry.parents.is_empty() + && basis.commit_global_tick().is_none() + && basis.commit_id() + == crate::echo_operation::genesis_commit_id(basis.writer_head(), basis.state_root()) + } else { + let Some(parent_tick) = basis.worldline_tick().as_u64().checked_sub(1) else { + return Err(TrustedRuntimeWalError::EchoOperationExecutionMismatch { + detail: "operation receipt has an impossible non-genesis basis tick", + }); + }; + matches!( + entry.parents.as_slice(), + [parent] + if parent.worldline_id == basis.writer_head().worldline_id + && parent.worldline_tick == crate::WorldlineTick::from_raw(parent_tick) + && parent.commit_hash == basis.commit_id() + ) + }; + if !parent_matches_basis { + return Err(TrustedRuntimeWalError::EchoOperationExecutionMismatch { + detail: "typed operation receipt basis disagrees with provenance parents", + }); + } + Ok(()) +} + +fn validate_operation_receipt_parent_material( + basis: EchoOperationEvaluationBasisV1, + entry: &ProvenanceEntry, + provenance_by_coordinate: &BTreeMap< + (crate::WorldlineId, crate::WorldlineTick), + &ProvenanceEntry, + >, +) -> Result<(), TrustedRuntimeWalError> { + if basis.worldline_tick() == crate::WorldlineTick::ZERO { + return Ok(()); + } + let parent_tick = basis.worldline_tick().as_u64().checked_sub(1).ok_or( + TrustedRuntimeWalError::EchoOperationExecutionMismatch { + detail: "operation receipt has an impossible non-genesis basis tick", + }, + )?; + let parent_coordinate = ( + basis.writer_head().worldline_id, + crate::WorldlineTick::from_raw(parent_tick), + ); + let parent = provenance_by_coordinate.get(&parent_coordinate).ok_or( + TrustedRuntimeWalError::EchoOperationExecutionMismatch { + detail: "operation receipt basis has no retained parent provenance", + }, + )?; + let parent_matches_basis = entry.parents.as_slice() == [parent.as_ref()] + && parent.expected.state_root == basis.state_root() + && parent.expected.commit_hash == basis.commit_id() + && basis.commit_global_tick() == Some(parent.commit_global_tick); + if !parent_matches_basis { + return Err(TrustedRuntimeWalError::EchoOperationExecutionMismatch { + detail: "operation receipt basis disagrees with retained parent provenance", + }); + } + Ok(()) +} + struct RecoveredRuntimeStateMaterial { provenance_entries: Vec, receipt_correlations: Vec, @@ -2513,6 +3327,22 @@ fn recover_runtime_state_delta_material( let mut submission_by_ticket = BTreeMap::new(); let mut missing = Vec::new(); for transaction in &report.transactions { + if transaction.commit.transaction_kind == WalTransactionKind::ExecutableOperationTick { + let (_, state_delta, _) = operation_tick_records_from_transaction(transaction)?; + let entry = state_delta.provenance_entry().clone(); + let coordinate = (entry.worldline_id, entry.worldline_tick); + if entries_by_coordinate + .get(&coordinate) + .is_some_and(|existing| existing != &entry) + { + return Err(TrustedRuntimeWalError::RuntimeStateDeltaConflict { + worldline_id: entry.worldline_id, + worldline_tick: entry.worldline_tick, + }); + } + entries_by_coordinate.insert(coordinate, entry); + continue; + } if transaction.commit.transaction_kind != WalTransactionKind::SchedulerTick { continue; } @@ -2653,6 +3483,53 @@ fn validate_recovered_causal_parent_evidence( Ok(()) } +fn operation_installation_from_transaction( + transaction: &crate::causal_wal::WalRecoveredTransaction, +) -> Result<(InstalledEchoOperationV1, Vec), TrustedRuntimeWalError> { + let frame = required_unique_transaction_frame( + transaction, + WalRecordKind::ExecutableOperationPackageInstalled, + )?; + let retained_bytes = frame.payload.canonical_bytes.clone(); + let installed = recover_installation_v1(&retained_bytes)?; + Ok((installed, retained_bytes)) +} + +fn validate_echo_operation_frontier_root( + transaction: &crate::causal_wal::WalRecoveredTransaction, + expected_frontiers: &[AffectedFrontier], +) -> Result<(), TrustedRuntimeWalError> { + let expected = affected_frontiers_root(expected_frontiers); + let actual = transaction.commit.affected_frontiers_root; + if actual != expected { + return Err(TrustedRuntimeWalError::EchoOperationFrontierMismatch { + transaction_id: transaction.commit.transaction_id.as_hash(), + expected, + actual, + }); + } + Ok(()) +} + +fn operation_tick_records_from_transaction( + transaction: &crate::causal_wal::WalRecoveredTransaction, +) -> Result<(EchoOperationReceiptV1, WalRuntimeStateDeltaRecord, Hash), TrustedRuntimeWalError> { + let receipt_frame = required_unique_transaction_frame( + transaction, + WalRecordKind::ExecutableOperationExecutionRecorded, + )?; + let receipt = recover_committed_execution_receipt_v1(&receipt_frame.payload.canonical_bytes)?; + let state_delta_frame = required_unique_transaction_frame( + transaction, + WalRecordKind::ExecutableOperationStateDeltaRecorded, + )?; + let state_delta = + WalRuntimeStateDeltaRecord::from_payload_bytes(&state_delta_frame.payload.canonical_bytes)?; + let state_delta_digest = state_delta.digest()?; + validate_operation_receipt_state_delta(&receipt, &state_delta)?; + Ok((receipt, state_delta, state_delta_digest)) +} + fn tick_records_from_transaction( transaction: &crate::causal_wal::WalRecoveredTransaction, ) -> Result< @@ -3302,6 +4179,183 @@ mod tests { } } + #[test] + fn operation_recovery_rejects_uncorroborated_frontier_root() { + let expected_frontiers = [AffectedFrontier { + kind: AffectedFrontierKind::ExecutableOperationCatalog, + before_digest: [1; 32], + after_digest: [2; 32], + }]; + let expected = affected_frontiers_root(&expected_frontiers); + let actual = [3; 32]; + let transaction_id = WalTransactionId::from_hash([4; 32]); + let transaction = crate::causal_wal::WalRecoveredTransaction { + commit: WalTransactionCommit { + writer_epoch: WriterEpochId::from_hash([5; 32]), + transaction_id, + transaction_kind: WalTransactionKind::ExecutableOperationInstallation, + first_lsn: Lsn::from_raw(0), + last_lsn: Lsn::from_raw(0), + record_count: 1, + records_root: [6; 32], + affected_frontiers_root: actual, + previous_committed_transaction_digest: [7; 32], + durability_mode: WalDurabilityMode::StrictFilesystem, + schema_version: 1, + commit_digest: [8; 32], + }, + frames: Vec::new(), + }; + + assert_eq!( + validate_echo_operation_frontier_root(&transaction, &expected_frontiers), + Err(TrustedRuntimeWalError::EchoOperationFrontierMismatch { + transaction_id: transaction_id.as_hash(), + expected, + actual, + }) + ); + } + + #[test] + fn operation_recovery_requires_tick_scope_to_bind_the_patch_target() { + let node = crate::NodeKey { + warp_id: crate::make_warp_id("operation-recovery-scope"), + local_id: crate::make_node_id("operation-recovery-target"), + }; + let attachment_slot = crate::AttachmentKey::node_alpha(node); + let rule_id = [21; 32]; + let runtime_patch = crate::WarpTickPatchV1::new( + 7, + rule_id, + crate::TickCommitStatus::Committed, + vec![ + crate::SlotId::Node(node), + crate::SlotId::Attachment(attachment_slot), + ], + vec![crate::SlotId::Attachment(attachment_slot)], + vec![crate::WarpOp::SetAttachment { + key: attachment_slot, + value: None, + }], + ); + let patch = crate::WorldlineTickPatchV1 { + header: crate::WorldlineTickHeaderV1 { + commit_global_tick: GlobalTick::from_raw(1), + policy_id: runtime_patch.policy_id(), + rule_pack_id: runtime_patch.rule_pack_id(), + plan_digest: [22; 32], + decision_digest: [23; 32], + rewrites_digest: [24; 32], + }, + warp_id: node.warp_id, + ops: runtime_patch.ops().to_vec(), + in_slots: runtime_patch.in_slots().to_vec(), + out_slots: runtime_patch.out_slots().to_vec(), + patch_digest: runtime_patch.digest(), + }; + let receipt = |scope, scope_hash| { + crate::TickReceipt::new( + crate::TxId::from_raw(1), + vec![crate::TickReceiptEntry { + rule_id, + scope_hash, + scope, + disposition: crate::TickReceiptDisposition::Applied, + }], + vec![Vec::new()], + ) + }; + let valid = receipt(node, crate::scope_hash(&rule_id, &node)); + assert!(operation_tick_binds_patch_v1(&valid, &patch, rule_id)); + + let wrong_scope = crate::NodeKey { + warp_id: node.warp_id, + local_id: crate::make_node_id("operation-recovery-wrong-scope"), + }; + let self_consistent_wrong_scope = + receipt(wrong_scope, crate::scope_hash(&rule_id, &wrong_scope)); + assert!(!operation_tick_binds_patch_v1( + &self_consistent_wrong_scope, + &patch, + rule_id + )); + + let forged_scope_hash = receipt(node, [25; 32]); + assert!(!operation_tick_binds_patch_v1( + &forged_scope_hash, + &patch, + rule_id + )); + } + + #[test] + fn operation_recovery_corroborates_non_genesis_parent_basis_material() { + let head_key = test_head_key(); + let parent_state_root = [31; 32]; + let parent_commit = [32; 32]; + let parent_global_tick = GlobalTick::from_raw(7); + let parent = ProvenanceEntry { + worldline_id: head_key.worldline_id, + worldline_tick: WorldlineTick::ZERO, + commit_global_tick: parent_global_tick, + head_key: Some(head_key), + parents: Vec::new(), + event_kind: crate::ProvenanceEventKind::LocalCommit, + expected: crate::HashTriplet { + state_root: parent_state_root, + patch_digest: [33; 32], + commit_hash: parent_commit, + }, + patch: None, + tick_receipt: None, + outputs: Vec::new(), + atom_writes: Vec::new(), + }; + let child = ProvenanceEntry { + worldline_id: head_key.worldline_id, + worldline_tick: WorldlineTick::from_raw(1), + commit_global_tick: GlobalTick::from_raw(8), + head_key: Some(head_key), + parents: vec![parent.as_ref()], + event_kind: crate::ProvenanceEventKind::LocalCommit, + expected: crate::HashTriplet { + state_root: [34; 32], + patch_digest: [35; 32], + commit_hash: [36; 32], + }, + patch: None, + tick_receipt: None, + outputs: Vec::new(), + atom_writes: Vec::new(), + }; + let basis = EchoOperationEvaluationBasisV1::new( + head_key, + WorldlineTick::from_raw(1), + Some(parent_global_tick), + parent_state_root, + parent_commit, + EchoOperationApplicationBasisV1::new([37; 32], [38; 32]), + ); + let parent_coordinate = (parent.worldline_id, parent.worldline_tick); + assert!( + validate_operation_receipt_parent_material(basis, &child, &BTreeMap::new()).is_err() + ); + let provenance = BTreeMap::from([(parent_coordinate, &parent)]); + validate_operation_receipt_parent_material(basis, &child, &provenance) + .expect("the exact retained parent corroborates every causal basis field"); + + let mut wrong_root = parent.clone(); + wrong_root.expected.state_root = [39; 32]; + let provenance = BTreeMap::from([(parent_coordinate, &wrong_root)]); + assert!(validate_operation_receipt_parent_material(basis, &child, &provenance).is_err()); + + let mut wrong_global_tick = parent.clone(); + wrong_global_tick.commit_global_tick = GlobalTick::from_raw(9); + let provenance = BTreeMap::from([(parent_coordinate, &wrong_global_tick)]); + assert!(validate_operation_receipt_parent_material(basis, &child, &provenance).is_err()); + } + fn test_correlation(receipt_digest: Hash) -> ReceiptCorrelationRecord { let head_key = test_head_key(); ReceiptCorrelationRecord { @@ -3968,6 +5022,59 @@ fn wal_tick_decision_code(decision: WalTickDecision) -> u8 { } } +fn executable_operation_catalog_frontier_digest( + previous: Hash, + package_id: crate::EchoOperationPackageIdV1, + retained_installation_bytes: &[u8], +) -> Hash { + let mut hasher = blake3::Hasher::new(); + hasher.update(TRUSTED_RUNTIME_WAL_DOMAIN); + hasher.update(b"executable-operation-catalog-frontier"); + hasher.update(&previous); + hasher.update(&package_id.as_hash()); + hasher.update(&(retained_installation_bytes.len() as u64).to_le_bytes()); + hasher.update(retained_installation_bytes); + hasher.finalize().into() +} + +fn executable_operation_receipt_frontier_digest(previous: Hash, receipt_digest: Hash) -> Hash { + let mut hasher = blake3::Hasher::new(); + hasher.update(TRUSTED_RUNTIME_WAL_DOMAIN); + hasher.update(b"executable-operation-receipt-frontier"); + hasher.update(&previous); + hasher.update(&receipt_digest); + hasher.finalize().into() +} + +fn executable_operation_installation_transaction_digest( + catalog_frontier: Hash, + package_id: crate::EchoOperationPackageIdV1, + retained_installation_bytes: &[u8], +) -> Hash { + let mut hasher = blake3::Hasher::new(); + hasher.update(b"echo:trusted-runtime:executable-operation-installation-transaction:v1\0"); + hasher.update(&catalog_frontier); + hasher.update(&package_id.as_hash()); + hasher.update(&(retained_installation_bytes.len() as u64).to_le_bytes()); + hasher.update(retained_installation_bytes); + hasher.finalize().into() +} + +fn executable_operation_tick_transaction_digest( + receipt_frontier: Hash, + runtime_state_frontier: Hash, + receipt_digest: Hash, + state_delta_digest: Hash, +) -> Hash { + let mut hasher = blake3::Hasher::new(); + hasher.update(b"echo:trusted-runtime:executable-operation-tick-transaction:v1\0"); + hasher.update(&receipt_frontier); + hasher.update(&runtime_state_frontier); + hasher.update(&receipt_digest); + hasher.update(&state_delta_digest); + hasher.finalize().into() +} + fn runtime_state_frontier_digest( previous: Hash, correlation: &ReceiptCorrelationRecord, @@ -4022,6 +5129,8 @@ struct RecoveredRuntimeWalIndexEvidence<'a> { provenance_entries: &'a [ProvenanceEntry], missing_runtime_state_deltas: &'a [Hash], causal_anchor_history: &'a [WitnessedCausalAnchorAdmission], + installed_echo_operations: &'a [InstalledEchoOperationV1], + echo_operation_receipts: &'a [EchoOperationReceiptV1], } fn runtime_wal_recovery_certificate( @@ -4055,10 +5164,39 @@ fn recovered_runtime_wal_indexes_root( indexes.provenance_entries, indexes.missing_runtime_state_deltas, )?; - Ok(recovered_causal_anchor_index_root( - runtime_root, - indexes.causal_anchor_history, - )) + let causal_anchor_root = + recovered_causal_anchor_index_root(runtime_root, indexes.causal_anchor_history); + recovered_echo_operation_index_root( + causal_anchor_root, + indexes.installed_echo_operations, + indexes.echo_operation_receipts, + ) +} + +fn recovered_echo_operation_index_root( + base_root: Hash, + installations: &[InstalledEchoOperationV1], + receipts: &[EchoOperationReceiptV1], +) -> Result { + if installations.is_empty() && receipts.is_empty() { + return Ok(base_root); + } + let mut hasher = blake3::Hasher::new(); + hasher.update(b"echo:trusted-runtime-wal:executable-operation-index:v1\0"); + hasher.update(&base_root); + hasher.update(&(installations.len() as u64).to_le_bytes()); + for installed in installations { + let bytes = retain_installation_v1(installed)?; + hasher.update(&(bytes.len() as u64).to_le_bytes()); + hasher.update(&bytes); + } + hasher.update(&(receipts.len() as u64).to_le_bytes()); + for receipt in receipts { + let bytes = receipt.to_canonical_bytes()?; + hasher.update(&(bytes.len() as u64).to_le_bytes()); + hasher.update(&bytes); + } + Ok(hasher.finalize().into()) } fn recovered_causal_anchor_index_root( diff --git a/crates/warp-core/tests/executable_operation_pipeline_tests.rs b/crates/warp-core/tests/executable_operation_pipeline_tests.rs new file mode 100644 index 00000000..5f20793f --- /dev/null +++ b/crates/warp-core/tests/executable_operation_pipeline_tests.rs @@ -0,0 +1,1615 @@ +#![allow(clippy::expect_used, clippy::panic)] +// SPDX-License-Identifier: Apache-2.0 +// © James Ross Ω FLYING•ROBOTS +//! External-consumer witnesses for Echo-owned executable operation semantics. + +use std::{ + fs, + path::{Path, PathBuf}, + sync::atomic::{AtomicUsize, Ordering}, +}; + +use bytes::Bytes; +use echo_edict_canonical::{decode_canonical_cbor_v1, encode_canonical_cbor_v1, CanonicalValueV1}; +use warp_core::causal_wal::{ + AffectedFrontier, AffectedFrontierKind, Lsn, PayloadCodecId, PayloadSchemaId, + WalAppendAuthority, WalBuildError, WalDurabilityMode, WalRecordKind, WalSegmentId, + WalTransactionBuilder, WalTransactionId, WalTransactionKind, WalValidationError, WriterEpochId, +}; +use warp_core::{ + make_head_id, make_node_id, make_type_id, make_warp_id, AtomPayload, AttachmentValue, + EchoOperationAdmissionErrorKindV1, EchoOperationAdmissionPolicyV1, + EchoOperationApplicationBasisV1, EchoOperationArtifactErrorKindV1, EchoOperationBudgetV1, + EchoOperationInvocationAdmissionErrorKindV1, EchoOperationInvocationAdmissionPolicyV1, + EchoOperationInvocationV1, EchoOperationObstructionKindV1, EchoOperationPreparationV1, + EchoOperationProgramV1, EchoOperationSemanticClosureV1, EchoOperationTerminalPostureV1, + EngineBuilder, ExecutableOperationPackageV1, GraphStore, InboxPolicy, InstalledEchoOperationV1, + NodeKey, NodeRecord, PlaybackMode, RuntimeWalActivationGap, SchedulerKind, TrustedRuntimeHost, + TrustedRuntimeHostError, TrustedRuntimeWalConfig, TrustedRuntimeWalError, WorldlineId, + WorldlineRuntime, WorldlineState, WriterHead, WriterHeadKey, +}; + +const OPERATION_COORDINATE: &str = "echo.fixture.SetAnchoredAtom.v1"; +static TEMP_DIR_COUNTER: AtomicUsize = AtomicUsize::new(0); + +struct TempWalDir(PathBuf); + +impl TempWalDir { + fn new() -> Self { + let root = PathBuf::from("target").join("warp-core-test-tmp"); + fs::create_dir_all(&root).expect("the test WAL fixture root is creatable"); + for _ in 0..1024 { + let ordinal = TEMP_DIR_COUNTER.fetch_add(1, Ordering::Relaxed); + let path = root.join(format!("echo-executable-operation-wal-{ordinal}")); + match fs::create_dir(&path) { + Ok(()) => return Self(path), + Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {} + Err(error) => panic!( + "failed to create executable-operation WAL fixture {}: {error}", + path.display() + ), + } + } + panic!("exhausted deterministic executable-operation WAL fixture attempts"); + } + + fn path(&self) -> &Path { + &self.0 + } +} + +impl Drop for TempWalDir { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } +} + +fn digest(label: &str) -> [u8; 32] { + *blake3::hash(label.as_bytes()).as_bytes() +} + +fn operation_wal_builder( + label: &str, + kind: WalTransactionKind, + authority: WalAppendAuthority, +) -> WalTransactionBuilder { + WalTransactionBuilder::new( + WriterEpochId::from_hash(digest("operation-wal-writer")), + WalSegmentId::from_raw(1), + WalTransactionId::from_hash(digest(label)), + kind, + authority, + Lsn::from_raw(0), + digest("operation-wal-previous-frame"), + digest("operation-wal-previous-commit"), + WalDurabilityMode::StrictFilesystem, + PayloadCodecId::from_hash(digest("operation-wal-codec")), + PayloadSchemaId::from_hash(digest("operation-wal-schema")), + 1, + 1, + digest("operation-wal-domain"), + ) +} + +fn operation_frontier(kind: AffectedFrontierKind, label: &str) -> AffectedFrontier { + AffectedFrontier { + kind, + before_digest: digest(&format!("{label}:before")), + after_digest: digest(&format!("{label}:after")), + } +} + +fn fixture_host() -> (TrustedRuntimeHost, WriterHeadKey, NodeKey) { + let warp_id = make_warp_id("operation-fixture"); + let node_id = make_node_id("operation-fixture-root"); + let node_type = make_type_id("operation-fixture-node"); + let attachment_type = make_type_id("operation-fixture-atom"); + let node = NodeKey { + warp_id, + local_id: node_id, + }; + let mut store = GraphStore::new(warp_id); + store.insert_node(node_id, NodeRecord { ty: node_type }); + store.set_node_attachment( + node_id, + Some(AttachmentValue::Atom(AtomPayload::new( + attachment_type, + Bytes::from_static(b"before"), + ))), + ); + let state = WorldlineState::from_root_store(store, node_id) + .expect("the fixture state has one lawful root"); + let worldline_id = WorldlineId::from_bytes(digest("operation-fixture-worldline")); + let head_key = WriterHeadKey { + worldline_id, + head_id: make_head_id("operation-fixture-writer"), + }; + let mut runtime = WorldlineRuntime::new(); + runtime + .register_worldline(worldline_id, state) + .expect("the fixture worldline registers"); + runtime + .register_writer_head(WriterHead::with_routing( + head_key, + PlaybackMode::Play, + InboxPolicy::AcceptAll, + None, + true, + )) + .expect("the fixture writer registers"); + + let mut engine_store = GraphStore::default(); + let engine_root = make_node_id("root"); + engine_store.insert_node( + engine_root, + NodeRecord { + ty: make_type_id("world"), + }, + ); + let engine = EngineBuilder::new(engine_store, engine_root) + .scheduler(SchedulerKind::Radix) + .workers(1) + .build(); + let host = TrustedRuntimeHost::new(runtime, engine) + .expect("the trusted Echo runtime host initializes"); + (host, head_key, node) +} + +fn semantic_closure() -> EchoOperationSemanticClosureV1 { + EchoOperationSemanticClosureV1::new( + digest("fixture-edict-source"), + digest("edict-semantic-identity"), + digest("fixture-edict-core"), + digest("fixture-echo-target-ir"), + "echo.fixture.GraphSchema.v1", + digest("fixture-graph-schema"), + "echo.fixture.Lawpack.v1", + digest("fixture-lawpack"), + ) +} + +fn operation_package( + node_type: warp_core::TypeId, + attachment_type: warp_core::TypeId, +) -> ExecutableOperationPackageV1 { + operation_package_at(OPERATION_COORDINATE, node_type, attachment_type) +} + +fn operation_package_at( + operation_coordinate: &str, + node_type: warp_core::TypeId, + attachment_type: warp_core::TypeId, +) -> ExecutableOperationPackageV1 { + ExecutableOperationPackageV1::new( + operation_coordinate, + semantic_closure(), + warp_core::echo_operation_target_profile_identity_v1(), + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + EchoOperationProgramV1::anchored_node_attachment_compare_and_set( + node_type, + attachment_type, + 1_024, + ), + ) +} + +fn install_fixture_operation(host: &mut TrustedRuntimeHost) -> InstalledEchoOperationV1 { + install_fixture_operation_at(host, OPERATION_COORDINATE) +} + +fn install_fixture_operation_at( + host: &mut TrustedRuntimeHost, + operation_coordinate: &str, +) -> InstalledEchoOperationV1 { + let package_bytes = operation_package_at( + operation_coordinate, + make_type_id("operation-fixture-node"), + make_type_id("operation-fixture-atom"), + ) + .to_canonical_bytes() + .expect("fixture package encodes"); + let package_id = warp_core::echo_operation_package_id_v1(&package_bytes); + let admitted = host + .admit_echo_operation_package_v1( + &EchoOperationAdmissionPolicyV1::exact( + package_id, + operation_coordinate, + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + package_bytes, + ) + .expect("the exact fixture package is admitted"); + host.install_admitted_echo_operation_package_v1(admitted) + .expect("the admitted fixture package installs") +} + +fn application_basis() -> EchoOperationApplicationBasisV1 { + warp_core::echo_operation_anchored_node_application_basis_v1( + NodeKey { + warp_id: make_warp_id("operation-fixture"), + local_id: make_node_id("operation-fixture-root"), + }, + make_type_id("operation-fixture-atom"), + b"before", + ) +} + +fn invocation_policy() -> EchoOperationInvocationAdmissionPolicyV1 { + EchoOperationInvocationAdmissionPolicyV1::new( + digest("fixture-authority-profile"), + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ) +} + +fn canonical_invocation( + installed: &InstalledEchoOperationV1, + basis: warp_core::EchoOperationEvaluationBasisV1, + node: NodeKey, + expected_value_digest: [u8; 32], + replacement: Vec, + budget: EchoOperationBudgetV1, +) -> Vec { + EchoOperationInvocationV1::anchored_node_attachment_compare_and_set( + installed.package_id(), + installed.operation_coordinate(), + basis, + digest("fixture-authority-grant"), + budget, + node, + expected_value_digest, + replacement, + ) + .to_canonical_bytes() + .expect("fixture invocation encodes canonically") +} + +fn replace_canonical_map_field( + bytes: &[u8], + field_name: &str, + replacement: CanonicalValueV1, +) -> Vec { + let value = decode_canonical_cbor_v1(bytes).expect("fixture bytes decode"); + let CanonicalValueV1::Map(mut fields) = value else { + panic!("fixture value must be a canonical map"); + }; + let field = fields + .iter_mut() + .find(|(key, _)| key == &CanonicalValueV1::Text(field_name.to_owned())) + .expect("fixture field exists"); + field.1 = replacement; + encode_canonical_cbor_v1(&CanonicalValueV1::Map(fields)) + .expect("modified fixture map re-encodes canonically") +} + +fn canonical_map_bytes_field(bytes: &[u8], field_name: &str) -> Vec { + let value = decode_canonical_cbor_v1(bytes).expect("fixture bytes decode"); + let CanonicalValueV1::Map(fields) = value else { + panic!("fixture value must be a canonical map"); + }; + let value = fields + .into_iter() + .find_map(|(key, value)| { + (key == CanonicalValueV1::Text(field_name.to_owned())).then_some(value) + }) + .expect("fixture field exists"); + let CanonicalValueV1::Bytes(value) = value else { + panic!("fixture field must carry canonical bytes"); + }; + value +} + +#[test] +fn admitted_data_only_program_commits_one_typed_attachment_patch() { + let (mut host, head_key, node) = fixture_host(); + let node_type = make_type_id("operation-fixture-node"); + let attachment_type = make_type_id("operation-fixture-atom"); + let package = operation_package(node_type, attachment_type); + let package_bytes = package + .to_canonical_bytes() + .expect("package has one canonical Edict encoding"); + let package_id = warp_core::echo_operation_package_id_v1(&package_bytes); + let package_policy = EchoOperationAdmissionPolicyV1::exact( + package_id, + OPERATION_COORDINATE, + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ); + let admitted_package = host + .admit_echo_operation_package_v1(&package_policy, package_bytes) + .expect("the independently pinned package is admitted"); + let installed = host + .install_admitted_echo_operation_package_v1(admitted_package) + .expect("admitted executable meaning installs without callbacks"); + + let application_basis = application_basis(); + let evaluation_basis = host + .echo_operation_evaluation_basis_v1(head_key, application_basis) + .expect("Echo resolves the exact current parent basis"); + let invocation = EchoOperationInvocationV1::anchored_node_attachment_compare_and_set( + installed.package_id(), + installed.operation_coordinate(), + evaluation_basis, + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + node, + warp_core::echo_operation_atom_value_digest_v1(attachment_type, b"before"), + b"after".to_vec(), + ); + let invocation_id = invocation + .identity() + .expect("the generated-style invocation has one identity"); + let invocation_bytes = invocation + .to_canonical_bytes() + .expect("the generated-style invocation is canonical"); + let invocation_policy = EchoOperationInvocationAdmissionPolicyV1::new( + digest("fixture-authority-profile"), + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ); + let admitted_invocation = host + .admit_echo_operation_invocation_v1(&invocation_policy, &invocation_bytes) + .expect("Echo independently admits the installed operation invocation"); + let preparation = host.prepare_echo_operation_v1(admitted_invocation); + let EchoOperationPreparationV1::Prepared(prepared) = preparation else { + panic!("the lawful fixture invocation must prepare"); + }; + assert_eq!(prepared.evaluation_basis(), &evaluation_basis); + assert_eq!(prepared.declared_footprint(), prepared.actual_footprint()); + let invocation_admission_id = prepared.invocation_admission_id(); + let private_evaluation_id = prepared.private_evaluation_id(); + let preparation_id = prepared.preparation_id(); + let result_id = prepared.result_id(); + let prepared_patch_digest = prepared.patch().digest(); + + let execution = host + .commit_prepared_echo_operation_v1(prepared) + .expect("Echo commits one privately evaluated patch"); + assert_eq!( + execution.receipt().terminal_posture(), + EchoOperationTerminalPostureV1::Committed + ); + assert_eq!(execution.receipt().package_id(), package_id); + assert_eq!( + execution.receipt().package_admission_id(), + installed.package_admission_id() + ); + assert_eq!( + execution.receipt().installed_operation_id(), + installed.installed_operation_id() + ); + assert_eq!( + execution.receipt().operation_coordinate(), + OPERATION_COORDINATE + ); + assert_eq!(execution.receipt().invocation_id(), invocation_id); + assert_ne!( + execution.receipt().invocation_bytes_digest(), + invocation_id.as_hash(), + "the exact-bytes digest must remain domain-separated from invocation identity" + ); + assert_ne!( + execution.receipt().package_id().as_hash(), + execution.receipt().program_id().as_hash(), + "a subordinate program digest must not collapse package identity" + ); + assert_eq!( + execution.receipt().evaluation_basis_id(), + evaluation_basis.identity() + ); + assert_eq!(execution.receipt().evaluation_basis(), evaluation_basis); + assert_eq!(execution.receipt().program_id(), installed.program_id()); + assert_eq!( + execution + .committed_patch() + .expect("the committed outcome carries its exact patch") + .rule_pack_id(), + installed.installed_operation_id().as_hash(), + "the causal patch must bind the admitted installation, not a naked program" + ); + assert_eq!( + execution + .tick_receipt() + .expect("the committed outcome carries its singleton tick receipt") + .entries()[0] + .rule_id, + installed.installed_operation_id().as_hash(), + "generic tick evidence must not promote a program digest to rule authority" + ); + assert_eq!( + execution.receipt().declared_footprint_digest(), + execution.receipt().actual_footprint_digest() + ); + assert_eq!( + execution.receipt().delegated_budget(), + EchoOperationBudgetV1::new(16, 4_096, 4_096) + ); + assert_eq!( + execution.receipt().consumed_budget(), + EchoOperationBudgetV1::new(4, 70, 37) + ); + assert_eq!( + execution.receipt().invocation_admission_id(), + invocation_admission_id + ); + assert_eq!( + execution.receipt().private_evaluation_id(), + private_evaluation_id + ); + assert_eq!(execution.receipt().preparation_id(), preparation_id); + assert_eq!(execution.receipt().prepared_result_id(), result_id); + assert_eq!(execution.receipt().committed_result_id(), Some(result_id)); + assert_eq!( + execution.receipt().prepared_patch_digest(), + prepared_patch_digest + ); + assert_eq!( + execution.receipt().committed_patch_digest(), + Some(prepared_patch_digest) + ); + assert!(execution.receipt().composition_digest().is_some()); + assert_ne!(execution.receipt().terminal_outcome_digest(), [0; 32]); + assert_eq!( + execution.receipt().state_root_before(), + evaluation_basis.state_root() + ); + assert_ne!(execution.receipt().commit_id(), [0; 32]); + assert_eq!( + execution + .receipt() + .commit_global_tick() + .expect("a committed operation receives a global tick") + .as_u64(), + 1 + ); + assert_eq!(execution.receipt().worldline_tick_after().as_u64(), 1); + + let state = host + .runtime() + .worldlines() + .get(&head_key.worldline_id) + .expect("the committed worldline remains registered") + .state(); + assert_eq!(state.current_tick().as_u64(), 1); + assert_eq!(execution.receipt().state_root_after(), state.state_root()); + assert_eq!( + state + .store(&node.warp_id) + .and_then(|store| store.node_attachment(&node.local_id)), + Some(&AttachmentValue::Atom(AtomPayload::new( + attachment_type, + Bytes::from_static(b"after"), + ))) + ); +} + +#[test] +fn a_prepared_operation_cannot_commit_after_its_parent_basis_changes() { + let (mut host, head_key, node) = fixture_host(); + let node_type = make_type_id("operation-fixture-node"); + let attachment_type = make_type_id("operation-fixture-atom"); + let package_bytes = operation_package(node_type, attachment_type) + .to_canonical_bytes() + .expect("package encodes"); + let package_id = warp_core::echo_operation_package_id_v1(&package_bytes); + let admitted_package = host + .admit_echo_operation_package_v1( + &EchoOperationAdmissionPolicyV1::exact( + package_id, + OPERATION_COORDINATE, + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + package_bytes, + ) + .expect("the exact package is admitted"); + let installed = host + .install_admitted_echo_operation_package_v1(admitted_package) + .expect("the exact package installs"); + let application_basis = application_basis(); + + let prepare = |host: &TrustedRuntimeHost, replacement: &[u8]| { + let basis = host + .echo_operation_evaluation_basis_v1(head_key, application_basis) + .expect("Echo resolves the current basis"); + let invocation = EchoOperationInvocationV1::anchored_node_attachment_compare_and_set( + installed.package_id(), + installed.operation_coordinate(), + basis, + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + node, + warp_core::echo_operation_atom_value_digest_v1(attachment_type, b"before"), + replacement.to_vec(), + ); + let bytes = invocation.to_canonical_bytes().expect("invocation encodes"); + let admitted = host + .admit_echo_operation_invocation_v1( + &EchoOperationInvocationAdmissionPolicyV1::new( + digest("fixture-authority-profile"), + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + &bytes, + ) + .expect("invocation is admitted"); + match host.prepare_echo_operation_v1(admitted) { + EchoOperationPreparationV1::Prepared(prepared) => prepared, + EchoOperationPreparationV1::Obstructed(obstruction) => { + panic!("lawful invocation obstructed: {obstruction:?}") + } + } + }; + + let stale_preparation = prepare(&host, b"stale-result"); + let winning_preparation = prepare(&host, b"winning-result"); + host.commit_prepared_echo_operation_v1(winning_preparation) + .expect("one exact-basis operation commits"); + let evidence = host + .commit_prepared_echo_operation_v1(stale_preparation) + .expect("basis refusal is typed evidence, not a host fault"); + assert_eq!( + evidence.receipt().terminal_posture(), + EchoOperationTerminalPostureV1::NotCommittedBasisChanged + ); + assert_eq!(evidence.receipt().committed_patch_digest(), None); + assert_eq!(evidence.receipt().committed_result_id(), None); + assert_eq!(evidence.receipt().composition_digest(), None); + + let state = host + .runtime() + .worldlines() + .get(&head_key.worldline_id) + .expect("the worldline remains registered") + .state(); + assert_eq!(state.current_tick().as_u64(), 1); + assert_eq!( + state + .store(&node.warp_id) + .and_then(|store| store.node_attachment(&node.local_id)), + Some(&AttachmentValue::Atom(AtomPayload::new( + attachment_type, + Bytes::from_static(b"winning-result"), + ))) + ); +} + +#[test] +fn a_prepared_program_cannot_substitute_for_installation_authority() { + let (mut evaluating_host, head_key, node) = fixture_host(); + let installed = install_fixture_operation(&mut evaluating_host); + let basis = evaluating_host + .echo_operation_evaluation_basis_v1(head_key, application_basis()) + .expect("Echo resolves the evaluation basis"); + let bytes = canonical_invocation( + &installed, + basis, + node, + warp_core::echo_operation_atom_value_digest_v1( + make_type_id("operation-fixture-atom"), + b"before", + ), + b"uninstalled-after".to_vec(), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ); + let admitted = evaluating_host + .admit_echo_operation_invocation_v1(&invocation_policy(), &bytes) + .expect("the installed operation invocation is admitted"); + let EchoOperationPreparationV1::Prepared(prepared) = + evaluating_host.prepare_echo_operation_v1(admitted) + else { + panic!("the installed operation must prepare"); + }; + let foreign_prepared = prepared.clone(); + + let (mut uninstalled_host, _, _) = fixture_host(); + let evidence = uninstalled_host + .commit_prepared_echo_operation_v1(prepared) + .expect("missing installation produces typed noncommit evidence"); + assert_eq!( + evidence.receipt().terminal_posture(), + EchoOperationTerminalPostureV1::NotCommittedInstallationUnavailable + ); + assert_eq!(evidence.receipt().committed_patch_digest(), None); + assert_eq!(evidence.receipt().committed_result_id(), None); + assert_eq!(evidence.receipt().composition_digest(), None); + let state = uninstalled_host + .runtime() + .worldlines() + .get(&head_key.worldline_id) + .expect("the parent worldline remains registered") + .state(); + assert_eq!(state.current_tick().as_u64(), 0); + assert_eq!( + state + .store(&node.warp_id) + .and_then(|store| store.node_attachment(&node.local_id)), + Some(&AttachmentValue::Atom(AtomPayload::new( + make_type_id("operation-fixture-atom"), + Bytes::from_static(b"before"), + ))) + ); + + let (mut foreign_host, _, _) = fixture_host(); + let foreign_installation = install_fixture_operation(&mut foreign_host); + assert_eq!( + foreign_installation.installed_operation_id(), + installed.installed_operation_id(), + "identical retained installation evidence must remain deterministic" + ); + let evidence = foreign_host + .commit_prepared_echo_operation_v1(foreign_prepared) + .expect("foreign runtime ownership produces typed noncommit evidence"); + assert_eq!( + evidence.receipt().terminal_posture(), + EchoOperationTerminalPostureV1::NotCommittedEvaluationAuthorityMismatch + ); + assert_eq!(evidence.receipt().committed_patch_digest(), None); +} + +#[test] +fn identical_program_bytes_do_not_merge_admitted_operation_identities() { + const ALTERNATE_COORDINATE: &str = "echo.fixture.SetAnchoredAtomAlternate.v1"; + + let (mut first_host, first_head, first_node) = fixture_host(); + let first_installed = install_fixture_operation_at(&mut first_host, OPERATION_COORDINATE); + let (mut second_host, second_head, second_node) = fixture_host(); + let second_installed = install_fixture_operation_at(&mut second_host, ALTERNATE_COORDINATE); + + assert_eq!(first_installed.program_id(), second_installed.program_id()); + assert_ne!(first_installed.package_id(), second_installed.package_id()); + assert_ne!( + first_installed.installed_operation_id(), + second_installed.installed_operation_id() + ); + + let execute = + |host: &mut TrustedRuntimeHost, installed: &InstalledEchoOperationV1, head, node| { + let basis = host + .echo_operation_evaluation_basis_v1(head, application_basis()) + .expect("Echo resolves the exact package-specific basis"); + let invocation = canonical_invocation( + installed, + basis, + node, + warp_core::echo_operation_atom_value_digest_v1( + make_type_id("operation-fixture-atom"), + b"before", + ), + b"same-consequence".to_vec(), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ); + let admitted = host + .admit_echo_operation_invocation_v1(&invocation_policy(), &invocation) + .expect("the exact package-specific invocation is admitted"); + let EchoOperationPreparationV1::Prepared(prepared) = + host.prepare_echo_operation_v1(admitted) + else { + panic!("the exact package-specific invocation must prepare"); + }; + host.commit_prepared_echo_operation_v1(prepared) + .expect("the exact package-specific preparation commits") + }; + + let first = execute(&mut first_host, &first_installed, first_head, first_node); + let second = execute( + &mut second_host, + &second_installed, + second_head, + second_node, + ); + assert_eq!( + first.receipt().state_root_after(), + second.receipt().state_root_after() + ); + assert_ne!(first.committed_patch(), second.committed_patch()); + assert_ne!(first.receipt().commit_id(), second.receipt().commit_id()); + assert_eq!( + first + .committed_patch() + .expect("first operation committed") + .rule_pack_id(), + first_installed.installed_operation_id().as_hash() + ); + assert_eq!( + second + .committed_patch() + .expect("second operation committed") + .rule_pack_id(), + second_installed.installed_operation_id().as_hash() + ); +} + +#[test] +fn package_admission_rejects_malformed_unsupported_mismatched_and_over_budget_bytes() { + let (host, _, _) = fixture_host(); + let package = operation_package( + make_type_id("operation-fixture-node"), + make_type_id("operation-fixture-atom"), + ); + let package_bytes = package + .to_canonical_bytes() + .expect("fixture package encodes"); + let package_id = warp_core::echo_operation_package_id_v1(&package_bytes); + + let digest_mismatch = host + .admit_echo_operation_package_v1( + &EchoOperationAdmissionPolicyV1::exact( + warp_core::echo_operation_package_id_v1(b"different package bytes"), + OPERATION_COORDINATE, + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + package_bytes.clone(), + ) + .expect_err("a package digest cannot confer a match on different bytes"); + assert_eq!( + digest_mismatch.kind(), + EchoOperationAdmissionErrorKindV1::PackageIdentityMismatch + ); + + let malformed_bytes = vec![0xff]; + let malformed = host + .admit_echo_operation_package_v1( + &EchoOperationAdmissionPolicyV1::exact( + warp_core::echo_operation_package_id_v1(&malformed_bytes), + OPERATION_COORDINATE, + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + malformed_bytes, + ) + .expect_err("malformed canonical bytes fail before installation"); + assert_eq!( + malformed.kind(), + EchoOperationAdmissionErrorKindV1::ArtifactInvalid + ); + + let unsupported_target_bytes = ExecutableOperationPackageV1::new( + OPERATION_COORDINATE, + semantic_closure(), + digest("unsupported-target-profile"), + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + EchoOperationProgramV1::anchored_node_attachment_compare_and_set( + make_type_id("operation-fixture-node"), + make_type_id("operation-fixture-atom"), + 1_024, + ), + ) + .to_canonical_bytes() + .expect("unsupported target package still has canonical source bytes"); + let unsupported_target = host + .admit_echo_operation_package_v1( + &EchoOperationAdmissionPolicyV1::exact( + warp_core::echo_operation_package_id_v1(&unsupported_target_bytes), + OPERATION_COORDINATE, + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + unsupported_target_bytes, + ) + .expect_err("an unimplemented target profile fails closed"); + assert_eq!( + unsupported_target + .artifact() + .expect("the target refusal retains its artifact cause") + .kind(), + EchoOperationArtifactErrorKindV1::UnsupportedTargetProfile + ); + + let program_bytes = canonical_map_bytes_field(&package_bytes, "program"); + let unsupported_program_bytes = replace_canonical_map_field( + &program_bytes, + "intrinsic_profile_identity", + CanonicalValueV1::Bytes(digest("unsupported-program-intrinsics").to_vec()), + ); + let unsupported_program_package = replace_canonical_map_field( + &package_bytes, + "program", + CanonicalValueV1::Bytes(unsupported_program_bytes), + ); + let unsupported_program = host + .admit_echo_operation_package_v1( + &EchoOperationAdmissionPolicyV1::exact( + warp_core::echo_operation_package_id_v1(&unsupported_program_package), + OPERATION_COORDINATE, + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + unsupported_program_package, + ) + .expect_err("program bytes must bind the supported intrinsic profile directly"); + assert_eq!( + unsupported_program + .artifact() + .expect("the program-profile refusal retains its artifact cause") + .kind(), + EchoOperationArtifactErrorKindV1::UnsupportedTargetProfile + ); + + let unsupported_schema_bytes = replace_canonical_map_field( + &package_bytes, + "input_schema_identity", + CanonicalValueV1::Bytes(digest("unsupported-input-schema").to_vec()), + ); + let unsupported_schema = host + .admit_echo_operation_package_v1( + &EchoOperationAdmissionPolicyV1::exact( + warp_core::echo_operation_package_id_v1(&unsupported_schema_bytes), + OPERATION_COORDINATE, + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + unsupported_schema_bytes, + ) + .expect_err("an unimplemented input schema fails closed"); + assert_eq!( + unsupported_schema + .artifact() + .expect("the schema refusal retains its artifact cause") + .kind(), + EchoOperationArtifactErrorKindV1::UnsupportedSchema + ); + + let authority_mismatch = host + .admit_echo_operation_package_v1( + &EchoOperationAdmissionPolicyV1::exact( + package_id, + OPERATION_COORDINATE, + digest("different-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + package_bytes.clone(), + ) + .expect_err("package identity cannot substitute for authority policy"); + assert_eq!( + authority_mismatch.kind(), + EchoOperationAdmissionErrorKindV1::AuthorityProfileMismatch + ); + + let over_budget = host + .admit_echo_operation_package_v1( + &EchoOperationAdmissionPolicyV1::exact( + package_id, + OPERATION_COORDINATE, + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(15, 4_096, 4_096), + ), + package_bytes, + ) + .expect_err("a package cannot widen the runtime-owned budget ceiling"); + assert_eq!( + over_budget.kind(), + EchoOperationAdmissionErrorKindV1::BudgetExceedsPolicy + ); + + let impossible_package = ExecutableOperationPackageV1::new( + OPERATION_COORDINATE, + semantic_closure(), + warp_core::echo_operation_target_profile_identity_v1(), + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(1, 1, 1), + EchoOperationProgramV1::anchored_node_attachment_compare_and_set( + make_type_id("operation-fixture-node"), + make_type_id("operation-fixture-atom"), + 1_024, + ), + ) + .to_canonical_bytes() + .expect_err("a package budget must permit the smallest lawful evaluation"); + assert_eq!( + impossible_package.kind(), + EchoOperationArtifactErrorKindV1::InvalidBudget + ); +} + +#[test] +fn invocation_admission_keeps_contract_authority_budget_and_basis_separate() { + let (mut host, head_key, node) = fixture_host(); + let installed = install_fixture_operation(&mut host); + let basis = host + .echo_operation_evaluation_basis_v1(head_key, application_basis()) + .expect("Echo resolves the exact invocation basis"); + let valid_bytes = canonical_invocation( + &installed, + basis, + node, + warp_core::echo_operation_atom_value_digest_v1( + make_type_id("operation-fixture-atom"), + b"before", + ), + b"after".to_vec(), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ); + + let malformed = host + .admit_echo_operation_invocation_v1(&invocation_policy(), &[0xff]) + .expect_err("malformed invocation bytes fail before routing"); + assert_eq!( + malformed.kind(), + EchoOperationInvocationAdmissionErrorKindV1::MalformedInvocation + ); + + let wrong_profile = host + .admit_echo_operation_invocation_v1( + &EchoOperationInvocationAdmissionPolicyV1::new( + digest("wrong-authority-profile"), + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + &valid_bytes, + ) + .expect_err("runtime authority policy remains independent of package identity"); + assert_eq!( + wrong_profile.kind(), + EchoOperationInvocationAdmissionErrorKindV1::AuthorityProfileMismatch + ); + + let wrong_grant = host + .admit_echo_operation_invocation_v1( + &EchoOperationInvocationAdmissionPolicyV1::new( + digest("fixture-authority-profile"), + digest("wrong-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + &valid_bytes, + ) + .expect_err("the operation package does not confer an invocation grant"); + assert_eq!( + wrong_grant.kind(), + EchoOperationInvocationAdmissionErrorKindV1::AuthorityGrantMismatch + ); + + let over_budget_bytes = canonical_invocation( + &installed, + basis, + node, + warp_core::echo_operation_atom_value_digest_v1( + make_type_id("operation-fixture-atom"), + b"before", + ), + b"after".to_vec(), + EchoOperationBudgetV1::new(17, 4_096, 4_096), + ); + let over_budget = host + .admit_echo_operation_invocation_v1(&invocation_policy(), &over_budget_bytes) + .expect_err("delegation cannot exceed package or runtime policy"); + assert_eq!( + over_budget.kind(), + EchoOperationInvocationAdmissionErrorKindV1::BudgetExceeded + ); + + let below_minimum_bytes = canonical_invocation( + &installed, + basis, + node, + warp_core::echo_operation_atom_value_digest_v1( + make_type_id("operation-fixture-atom"), + b"before", + ), + b"after".to_vec(), + EchoOperationBudgetV1::new(1, 1, 1), + ); + let below_minimum = host + .admit_echo_operation_invocation_v1(&invocation_policy(), &below_minimum_bytes) + .expect_err("delegation below the program minimum fails during admission"); + assert_eq!( + below_minimum.kind(), + EchoOperationInvocationAdmissionErrorKindV1::BudgetExceeded + ); + + let unbounded_basis_read_bytes = canonical_invocation( + &installed, + basis, + node, + warp_core::echo_operation_atom_value_digest_v1( + make_type_id("operation-fixture-atom"), + b"before", + ), + Vec::new(), + EchoOperationBudgetV1::new(4, 64, 32), + ); + let unbounded_basis_read = host + .admit_echo_operation_invocation_v1(&invocation_policy(), &unbounded_basis_read_bytes) + .expect_err("basis corroboration must fit inside the delegated read budget"); + assert_eq!( + unbounded_basis_read.kind(), + EchoOperationInvocationAdmissionErrorKindV1::BudgetExceeded + ); + + let wrong_operation = EchoOperationInvocationV1::anchored_node_attachment_compare_and_set( + installed.package_id(), + "echo.fixture.DifferentOperation.v1", + basis, + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + node, + warp_core::echo_operation_atom_value_digest_v1( + make_type_id("operation-fixture-atom"), + b"before", + ), + b"after".to_vec(), + ) + .to_canonical_bytes() + .expect("the structurally valid wrong operation invocation encodes"); + let wrong_operation = host + .admit_echo_operation_invocation_v1(&invocation_policy(), &wrong_operation) + .expect_err("a package digest does not confer another operation coordinate"); + assert_eq!( + wrong_operation.kind(), + EchoOperationInvocationAdmissionErrorKindV1::OperationCoordinateMismatch + ); + + let uncorroborated_basis = host + .echo_operation_evaluation_basis_v1( + head_key, + EchoOperationApplicationBasisV1::new( + digest("invented-application-basis-schema"), + digest("invented-application-basis-value"), + ), + ) + .expect("a client may claim basis bytes before invocation admission"); + let uncorroborated_basis_bytes = canonical_invocation( + &installed, + uncorroborated_basis, + node, + warp_core::echo_operation_atom_value_digest_v1( + make_type_id("operation-fixture-atom"), + b"before", + ), + b"after".to_vec(), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ); + let uncorroborated_basis = host + .admit_echo_operation_invocation_v1(&invocation_policy(), &uncorroborated_basis_bytes) + .expect_err("Echo must corroborate the application basis from current graph state"); + assert_eq!( + uncorroborated_basis.kind(), + EchoOperationInvocationAdmissionErrorKindV1::BasisMismatch + ); +} + +#[test] +fn private_evaluation_returns_typed_obstructions_without_parent_mutation() { + let (mut host, head_key, node) = fixture_host(); + let installed = install_fixture_operation(&mut host); + let basis = host + .echo_operation_evaluation_basis_v1(head_key, application_basis()) + .expect("Echo resolves the exact invocation basis"); + let state_root_before = host + .runtime() + .worldlines() + .get(&head_key.worldline_id) + .expect("the fixture worldline exists") + .state() + .state_root(); + let attachment_type = make_type_id("operation-fixture-atom"); + + let cases = [ + ( + canonical_invocation( + &installed, + basis, + node, + digest("wrong-value-precondition"), + b"after".to_vec(), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + EchoOperationObstructionKindV1::PreconditionMismatch, + ), + ( + canonical_invocation( + &installed, + basis, + node, + warp_core::echo_operation_atom_value_digest_v1(attachment_type, b"before"), + vec![0x61; 1_025], + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + EchoOperationObstructionKindV1::ReplacementTooLarge, + ), + ( + canonical_invocation( + &installed, + basis, + node, + warp_core::echo_operation_atom_value_digest_v1(attachment_type, b"before"), + b"after".to_vec(), + EchoOperationBudgetV1::new(4, 70, 32), + ), + EchoOperationObstructionKindV1::BudgetExceeded, + ), + ]; + + for (invocation_bytes, expected_kind) in cases { + let admitted = host + .admit_echo_operation_invocation_v1(&invocation_policy(), &invocation_bytes) + .expect("structurally lawful input reaches private evaluation"); + let EchoOperationPreparationV1::Obstructed(obstruction) = + host.prepare_echo_operation_v1(admitted) + else { + panic!("the negative fixture must not prepare a patch"); + }; + assert_eq!(obstruction.kind(), expected_kind); + } + + let unresolved_basis = canonical_invocation( + &installed, + basis, + NodeKey { + warp_id: node.warp_id, + local_id: make_node_id("missing-operation-node"), + }, + warp_core::echo_operation_atom_value_digest_v1(attachment_type, b"before"), + b"after".to_vec(), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ); + let error = host + .admit_echo_operation_invocation_v1(&invocation_policy(), &unresolved_basis) + .expect_err("Echo must independently resolve the claimed application basis"); + assert_eq!( + error.kind(), + EchoOperationInvocationAdmissionErrorKindV1::BasisMismatch + ); + + let state = host + .runtime() + .worldlines() + .get(&head_key.worldline_id) + .expect("the parent worldline remains registered") + .state(); + assert_eq!(state.current_tick().as_u64(), 0); + assert_eq!(state.state_root(), state_root_before); + assert_eq!( + state + .store(&node.warp_id) + .and_then(|store| store.node_attachment(&node.local_id)), + Some(&AttachmentValue::Atom(AtomPayload::new( + attachment_type, + Bytes::from_static(b"before"), + ))) + ); +} + +#[test] +fn obstruction_identity_binds_the_exact_invocation_admission() { + let (mut host, head_key, node) = fixture_host(); + let installed = install_fixture_operation(&mut host); + let basis = host + .echo_operation_evaluation_basis_v1(head_key, application_basis()) + .expect("Echo resolves the exact invocation basis"); + let invocation_bytes = canonical_invocation( + &installed, + basis, + node, + digest("wrong-value-precondition"), + b"after".to_vec(), + EchoOperationBudgetV1::new(8, 512, 512), + ); + let first_policy = EchoOperationInvocationAdmissionPolicyV1::new( + digest("fixture-authority-profile"), + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ); + let second_policy = EchoOperationInvocationAdmissionPolicyV1::new( + digest("fixture-authority-profile"), + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(32, 8_192, 8_192), + ); + + let obstruct = |host: &mut TrustedRuntimeHost, + policy: &EchoOperationInvocationAdmissionPolicyV1| { + let admitted = host + .admit_echo_operation_invocation_v1(policy, &invocation_bytes) + .expect("both runtime policies admit the same invocation bytes"); + let EchoOperationPreparationV1::Obstructed(obstruction) = + host.prepare_echo_operation_v1(admitted) + else { + panic!("the false precondition must obstruct"); + }; + assert_eq!( + obstruction.kind(), + EchoOperationObstructionKindV1::PreconditionMismatch + ); + obstruction + }; + let first = obstruct(&mut host, &first_policy); + let second = obstruct(&mut host, &second_policy); + + assert_eq!( + first.installed_operation_id(), + second.installed_operation_id() + ); + assert_ne!( + first.invocation_admission_id(), + second.invocation_admission_id() + ); + assert_ne!( + first.identity(), + second.identity(), + "different invocation-admission evidence must not alias one obstruction identity" + ); +} + +#[test] +fn identical_admitted_basis_and_inputs_produce_identical_consequences() { + let execute = || { + let (mut host, head_key, node) = fixture_host(); + let installed = install_fixture_operation(&mut host); + let basis = host + .echo_operation_evaluation_basis_v1(head_key, application_basis()) + .expect("Echo resolves the exact invocation basis"); + let bytes = canonical_invocation( + &installed, + basis, + node, + warp_core::echo_operation_atom_value_digest_v1( + make_type_id("operation-fixture-atom"), + b"before", + ), + b"deterministic-after".to_vec(), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ); + let admitted = host + .admit_echo_operation_invocation_v1(&invocation_policy(), &bytes) + .expect("the exact invocation is admitted"); + let EchoOperationPreparationV1::Prepared(prepared) = + host.prepare_echo_operation_v1(admitted) + else { + panic!("the deterministic fixture must prepare"); + }; + host.commit_prepared_echo_operation_v1(prepared) + .expect("the deterministic fixture commits") + }; + + let first = execute(); + let second = execute(); + assert_eq!(first.receipt(), second.receipt()); + assert_eq!(first.snapshot(), second.snapshot()); + assert_eq!(first.tick_receipt(), second.tick_receipt()); + assert_eq!(first.committed_patch(), second.committed_patch()); +} + +#[test] +fn wal_activation_refuses_process_only_executable_installation_authority() { + let (mut host, _, _) = fixture_host(); + install_fixture_operation(&mut host); + let error = host + .enable_in_memory_runtime_wal() + .expect_err("WAL activation must not forget a process-only operation installation"); + assert!(matches!( + error, + TrustedRuntimeHostError::Wal(TrustedRuntimeWalError::RuntimeAuthorityNotDurable { + gap: RuntimeWalActivationGap::ExecutableOperationInstallation, + }) + )); +} + +#[test] +fn operation_wal_codes_append_without_renumbering_legacy_evidence() { + assert_eq!(WalTransactionKind::SubmissionIntake.stable_code(), 1); + assert_eq!(WalTransactionKind::SchedulerTick.stable_code(), 2); + assert_eq!(WalTransactionKind::CausalAnchorAdmission.stable_code(), 7); + assert_eq!( + WalTransactionKind::ExecutableOperationInstallation.stable_code(), + 8 + ); + assert_eq!(WalTransactionKind::ExecutableOperationTick.stable_code(), 9); + + assert_eq!(WalRecordKind::TickReceiptRecorded.stable_code(), 6); + assert_eq!(WalRecordKind::RuntimeStateDeltaRecorded.stable_code(), 7); + assert_eq!( + WalRecordKind::CausalAnchorAdmissionReceiptRecorded.stable_code(), + 24 + ); + assert_eq!( + WalRecordKind::ExecutableOperationPackageInstalled.stable_code(), + 25 + ); + assert_eq!( + WalRecordKind::ExecutableOperationExecutionRecorded.stable_code(), + 26 + ); + assert_eq!( + WalRecordKind::ExecutableOperationStateDeltaRecorded.stable_code(), + 27 + ); + + assert_eq!(AffectedFrontierKind::RuntimeState.stable_code(), 2); + assert_eq!(AffectedFrontierKind::CausalAnchorIndex.stable_code(), 8); + assert_eq!( + AffectedFrontierKind::ExecutableOperationCatalog.stable_code(), + 9 + ); + assert_eq!( + AffectedFrontierKind::ExecutableOperationReceiptIndex.stable_code(), + 10 + ); +} + +#[test] +fn operation_wal_transactions_reject_noncanonical_shapes() { + let mut duplicate_installation = operation_wal_builder( + "duplicate-operation-installation", + WalTransactionKind::ExecutableOperationInstallation, + WalAppendAuthority::RuntimeControl, + ); + for _ in 0..2 { + duplicate_installation + .push_record( + WalRecordKind::ExecutableOperationPackageInstalled, + b"retained-installation".to_vec(), + ) + .expect("the raw fixture record has installation authority"); + } + assert!(matches!( + duplicate_installation.commit(vec![operation_frontier( + AffectedFrontierKind::ExecutableOperationCatalog, + "duplicate-operation-installation", + )]), + Err(WalBuildError::Validation( + WalValidationError::ExecutableOperationInstallationFrameShapeMismatch + )) + )); + + let mut missing_installation_frontier = operation_wal_builder( + "missing-operation-installation-frontier", + WalTransactionKind::ExecutableOperationInstallation, + WalAppendAuthority::RuntimeControl, + ); + missing_installation_frontier + .push_record( + WalRecordKind::ExecutableOperationPackageInstalled, + b"retained-installation".to_vec(), + ) + .expect("the raw fixture record has installation authority"); + assert!(matches!( + missing_installation_frontier.commit(Vec::new()), + Err(WalBuildError::Validation( + WalValidationError::ExecutableOperationInstallationFrontierShapeMismatch + )) + )); + + let mut reversed_tick = operation_wal_builder( + "reversed-operation-tick", + WalTransactionKind::ExecutableOperationTick, + WalAppendAuthority::ExecutionKernel, + ); + reversed_tick + .push_record( + WalRecordKind::ExecutableOperationStateDeltaRecorded, + b"retained-state-delta".to_vec(), + ) + .expect("the raw fixture record has execution-kernel authority"); + reversed_tick + .push_record( + WalRecordKind::ExecutableOperationExecutionRecorded, + b"retained-receipt".to_vec(), + ) + .expect("the raw fixture record has execution-kernel authority"); + assert!(matches!( + reversed_tick.commit(vec![ + operation_frontier( + AffectedFrontierKind::ExecutableOperationReceiptIndex, + "reversed-operation-receipt", + ), + operation_frontier( + AffectedFrontierKind::RuntimeState, + "reversed-operation-runtime", + ), + ]), + Err(WalBuildError::Validation( + WalValidationError::ExecutableOperationTickFrameShapeMismatch + )) + )); + + let mut reversed_frontiers = operation_wal_builder( + "reversed-operation-frontiers", + WalTransactionKind::ExecutableOperationTick, + WalAppendAuthority::ExecutionKernel, + ); + reversed_frontiers + .push_record( + WalRecordKind::ExecutableOperationExecutionRecorded, + b"retained-receipt".to_vec(), + ) + .expect("the raw fixture record has execution-kernel authority"); + reversed_frontiers + .push_record( + WalRecordKind::ExecutableOperationStateDeltaRecorded, + b"retained-state-delta".to_vec(), + ) + .expect("the raw fixture record has execution-kernel authority"); + assert!(matches!( + reversed_frontiers.commit(vec![ + operation_frontier( + AffectedFrontierKind::RuntimeState, + "reversed-frontier-runtime", + ), + operation_frontier( + AffectedFrontierKind::ExecutableOperationReceiptIndex, + "reversed-frontier-receipt", + ), + ]), + Err(WalBuildError::Validation( + WalValidationError::ExecutableOperationTickFrontierShapeMismatch + )) + )); +} + +#[test] +fn filesystem_wal_recovers_installed_meaning_consequence_and_typed_receipt() { + let wal_dir = TempWalDir::new(); + let node_type = make_type_id("operation-fixture-node"); + let attachment_type = make_type_id("operation-fixture-atom"); + let package_bytes = operation_package(node_type, attachment_type) + .to_canonical_bytes() + .expect("package encodes"); + let package_id = warp_core::echo_operation_package_id_v1(&package_bytes); + let application_basis = application_basis(); + let receipt_digests; + + { + let (mut host, head_key, node) = fixture_host(); + host.enable_runtime_wal(TrustedRuntimeWalConfig::filesystem(wal_dir.path())) + .expect("the fresh filesystem WAL opens"); + let admitted_package = host + .admit_echo_operation_package_v1( + &EchoOperationAdmissionPolicyV1::exact( + package_id, + OPERATION_COORDINATE, + digest("fixture-authority-profile"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + package_bytes, + ) + .expect("the package is admitted"); + let installed = host + .install_admitted_echo_operation_package_v1(admitted_package) + .expect("installation enters the WAL before the live catalog"); + let evaluation_basis = host + .echo_operation_evaluation_basis_v1(head_key, application_basis) + .expect("Echo resolves the exact basis"); + let invocation = EchoOperationInvocationV1::anchored_node_attachment_compare_and_set( + installed.package_id(), + installed.operation_coordinate(), + evaluation_basis, + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + node, + warp_core::echo_operation_atom_value_digest_v1(attachment_type, b"before"), + b"recovered-after".to_vec(), + ); + let invocation_bytes = invocation.to_canonical_bytes().expect("invocation encodes"); + let admitted = host + .admit_echo_operation_invocation_v1( + &EchoOperationInvocationAdmissionPolicyV1::new( + digest("fixture-authority-profile"), + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + &invocation_bytes, + ) + .expect("the invocation is independently admitted"); + let EchoOperationPreparationV1::Prepared(prepared) = + host.prepare_echo_operation_v1(admitted) + else { + panic!("the lawful invocation must prepare"); + }; + let evidence = host + .commit_prepared_echo_operation_v1(prepared) + .expect("the durable operation commits"); + let first_receipt_digest = evidence.receipt().digest(); + + let second_application_basis = warp_core::echo_operation_anchored_node_application_basis_v1( + node, + attachment_type, + b"recovered-after", + ); + let second_evaluation_basis = host + .echo_operation_evaluation_basis_v1(head_key, second_application_basis) + .expect("Echo resolves the retained non-genesis parent basis"); + let second_invocation = EchoOperationInvocationV1::anchored_node_attachment_compare_and_set( + installed.package_id(), + installed.operation_coordinate(), + second_evaluation_basis, + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + node, + warp_core::echo_operation_atom_value_digest_v1(attachment_type, b"recovered-after"), + b"recovered-twice".to_vec(), + ); + let second_invocation_bytes = second_invocation + .to_canonical_bytes() + .expect("the second invocation encodes"); + let second_admitted = host + .admit_echo_operation_invocation_v1( + &EchoOperationInvocationAdmissionPolicyV1::new( + digest("fixture-authority-profile"), + digest("fixture-authority-grant"), + EchoOperationBudgetV1::new(16, 4_096, 4_096), + ), + &second_invocation_bytes, + ) + .expect("the second invocation admits against the exact retained parent"); + let EchoOperationPreparationV1::Prepared(second_prepared) = + host.prepare_echo_operation_v1(second_admitted) + else { + panic!("the second lawful invocation must prepare"); + }; + let second_evidence = host + .commit_prepared_echo_operation_v1(second_prepared) + .expect("the second durable operation commits"); + receipt_digests = vec![first_receipt_digest, second_evidence.receipt().digest()]; + + let wal = host.runtime_wal().expect("the WAL remains enabled"); + assert_eq!( + wal.commits() + .iter() + .map(|commit| commit.transaction_kind) + .collect::>(), + vec![ + WalTransactionKind::ExecutableOperationInstallation, + WalTransactionKind::ExecutableOperationTick, + WalTransactionKind::ExecutableOperationTick, + ] + ); + let recovery = wal + .recover_read_only() + .expect("live read-only recovery works"); + assert_eq!(recovery.installed_echo_operations.len(), 1); + assert_eq!( + recovery + .echo_operation_receipts + .iter() + .map(warp_core::EchoOperationReceiptV1::digest) + .collect::>(), + receipt_digests + ); + assert_eq!( + recovery.recomputed_indexes_root().expect("indexes rehash"), + recovery.certificate.recovered_indexes_root + ); + } + + let (mut recovered, head_key, node) = fixture_host(); + recovered + .enable_runtime_wal(TrustedRuntimeWalConfig::filesystem(wal_dir.path())) + .expect("a fresh host recovers without executing application callbacks"); + assert!(recovered + .engine() + .installed_echo_operation_package_v1(package_id) + .is_some()); + let state = recovered + .runtime() + .worldlines() + .get(&head_key.worldline_id) + .expect("the recovered worldline exists") + .state(); + assert_eq!(state.current_tick().as_u64(), 2); + assert_eq!( + state + .store(&node.warp_id) + .and_then(|store| store.node_attachment(&node.local_id)), + Some(&AttachmentValue::Atom(AtomPayload::new( + attachment_type, + Bytes::from_static(b"recovered-twice"), + ))) + ); + let recovery = recovered + .runtime_wal() + .expect("the recovered WAL remains enabled") + .recover_read_only() + .expect("fresh-host read-only recovery remains stable"); + assert_eq!( + recovery + .echo_operation_receipts + .iter() + .map(warp_core::EchoOperationReceiptV1::digest) + .collect::>(), + receipt_digests + ); +} diff --git a/crates/warp-core/tests/provider_contract_admission_tests.rs b/crates/warp-core/tests/provider_contract_admission_tests.rs index 50fbcd69..a9b91e7d 100644 --- a/crates/warp-core/tests/provider_contract_admission_tests.rs +++ b/crates/warp-core/tests/provider_contract_admission_tests.rs @@ -44,7 +44,7 @@ const RELEASE_DIGEST: &str = const UNAPPROVED_RELEASE_DIGEST: &str = "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"; const PACKAGE_ARTIFACT_SHA256: &str = - "e0ccd4503c7f5830a1affa1c5a676f866aa0fab976a5ec2a0075c70916a64b69"; + "ee870c75ec08c8818b3f80ab6562ae62a5cf741cd709edcee0085d951c5d5a7b"; const UNAPPROVED_PACKAGE_ARTIFACT_SHA256: &str = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; const PROVIDER_PACKAGE_COORDINATE: &str = "echo.edict-provider@1"; diff --git a/docs/architecture/application-contract-hosting.md b/docs/architecture/application-contract-hosting.md index e8220722..4f1793bc 100644 --- a/docs/architecture/application-contract-hosting.md +++ b/docs/architecture/application-contract-hosting.md @@ -147,16 +147,22 @@ Echo runtime authority. The preceding paragraph describes the implemented provider-v1 compatibility corridor. [ADR 0023](../adr/0023-admitted-executable-operation-packages.md) -accepts a distinct, not-yet-implemented executable-operation category for new -application mutations. That category installs a digest-bound declarative -program interpreted by Echo and exposes no application matcher, executor, or -footprint callback. Provider v1 remains stable while consumers migrate; it is -not renamed or silently reinterpreted as the executable-operation corridor. -The program digest supplies executable meaning only: it cannot independently -confer an operation coordinate, invocability, or authority, and Echo cannot -install or invoke it naked. The admitted Edict operation package binds the -public contract and semantic closure to the exact program, after which Echo -independently admits each invocation. +accepts a distinct executable-operation category for new application +mutations. Echo now implements its first generic runtime slice: package and +invocation admission, installation of a data-only anchored-attachment program, +private bounded evaluation, exact-basis singleton commit, typed evidence, WAL, +and callback-free fresh-host recovery of committed consequences. Noncommitted +evidence is returned to the caller but does not enter the operation-tick WAL. +The slice exposes no application +matcher, executor, or footprint callback. It is not yet emitted by Edict, does +not implement Jedit `ReplaceRange`, and has no structurally separate target +verifier or general scheduler batch composition. Provider v1 remains stable +while consumers migrate; it is not renamed or silently reinterpreted as the +executable-operation corridor. The program digest supplies executable meaning +only: it cannot independently confer an operation coordinate, invocability, or +authority, and Echo cannot install or invoke it naked. The admitted operation +package binds the public contract and semantic closure to the exact program, +after which Echo independently admits each invocation. ## External Edict Provider Artifacts diff --git a/docs/plans/2026-07-18-jim-edict-echo-executable-warp-semantics.md b/docs/plans/2026-07-18-jim-edict-echo-executable-warp-semantics.md index 8ce16b5e..25fe4a1c 100644 --- a/docs/plans/2026-07-18-jim-edict-echo-executable-warp-semantics.md +++ b/docs/plans/2026-07-18-jim-edict-echo-executable-warp-semantics.md @@ -6,7 +6,7 @@ - **Status:** Historical discovery reconciled by [ADR 0023](../adr/0023-admitted-executable-operation-packages.md) - **Date:** 2026-07-18 -- **Implementation status:** Campaign 1 has not started +- **Implementation status:** Historical plan; live status belongs in GitHub ADR 0023 is authoritative where this source inventory's earlier hypotheses differ from the accepted executable-operation boundary. The accepted campaign @@ -1265,8 +1265,9 @@ required. ## 13. Accepted bounded campaign ADR 0023 resolves the architecture question raised by this inventory. Campaign -1 implementation has not started. Work proceeds as four separately reviewed -and explicitly merged stages: +implementation status is intentionally not maintained in this historical +inventory. Work proceeds as four separately reviewed and explicitly merged +stages: 1. add only the minimal Edict operation prerequisites: exact fixed-width values, explicit basis, Jedit lawpack-resource binding through Core and diff --git a/docs/topics/GeneratedRules.md b/docs/topics/GeneratedRules.md index 91370474..c6fb832c 100644 --- a/docs/topics/GeneratedRules.md +++ b/docs/topics/GeneratedRules.md @@ -7,8 +7,10 @@ Product and application rules are authored in contract languages. Hand-written Rust rule registration is not a supported product authoring surface. The first Edict provider-v1 compatibility closure now reaches provider-native scheduling, receipts, and WAL recovery. Its semantic implementation remains host-bound. -Wesley packaging and the generated bounded-read corridor remain incomplete and -must not borrow that mutation evidence. +Echo now also has the first generic, hook-free executable-operation runtime +slice. No Edict compiler or Jedit consumer emits its package yet. Wesley +packaging and the generated bounded-read corridor remain incomplete and must +not borrow evidence from either mutation path. ## Current Implementation @@ -127,23 +129,41 @@ installed generated-pack witness. The Edict mutation proposal intentionally rejects `Query`; that refusal does not turn a read into a mutation or eliminate the observer/optic crossing. -### Required executable-operation corridor +### Executable-operation corridor [ADR 0023](../adr/0023-admitted-executable-operation-packages.md) governs the -not-yet-implemented corridor required for newly authored application mutations: +operation-oriented corridor required for newly authored application mutations. +The currently implemented generic runtime slice is: + +```text +exact canonical ExecutableOperationPackageV1 bytes +-> Echo-owned package admission under a separate policy +-> installed data-only EchoOperationProgramV1 +-> exact basis-bearing canonical invocation +-> Echo-owned invocation admission under authority and delegated budget +-> bounded private evaluation with recorded actual footprint +-> exact-basis singleton commit attempt +-> one committed TickPatch and typed receipt, or returned typed no-patch evidence +-> committed outcomes only: execution-kernel WAL retention and callback-free recovery +``` + +The first program is an anchored typed-node alpha-attachment compare-and-set. +It is a generic vertical witness, not Jedit `ReplaceRange`, a rope intrinsic, or +a general-purpose virtual machine. The package binds Edict source, canonical +meaning, Core, Target IR, application-schema, and lawpack identities as opaque +substitution evidence. Echo does not yet obtain those identities from a real +Edict compiler crossing or validate their semantic relation through a +structurally separate target verifier. + +The remaining compiler and application closure is: ```text Jedit-owned Edict operation source and public contract -> canonical meaning, Core, and exact Jedit schema/lawpack closure -> exact subordinate EchoOperationProgramV1 -> structurally separate target verification --> Echo-owned package admission, byte corroboration, and installation --> exact basis-bearing invocation and Echo-owned invocation admission --> bounded private Echo evaluation with no application callbacks --> scheduler composition and exact-basis commit --> committed TickPatch and result, or typed no-patch outcome --> execution evidence / WAL carrying package, invocation, program, basis, - budget, footprint, scheduler, and terminal-outcome identities +-> the implemented Echo package, invocation, evaluation, commit, receipt, + WAL, and recovery crossings above ``` The operation package owns the public contract and runtime-recognized @@ -151,6 +171,22 @@ invocability. The subordinate program supplies executable meaning only. Echo cannot install or invoke a naked program digest, and a generated artifact does not confer caller authority. Provider v1 remains stable while existing consumers migrate; it is not silently reinterpreted as this new category. +The program bytes bind their interpreter and intrinsic profiles directly. The +parent patch and singleton tick entry use the admitted installed-operation +identity as their rule-pack/rule identity, so two packages that reuse identical +program bytes do not collapse into one causal operation identity. +The first slice composes one preparation and does not claim general scheduler +batch integration or independent implementation evidence. Its canonical +decoding, identity recomputation, budget and footprint checks, exact-basis +comparison, deterministic repeatability, and WAL recovery are deterministic +self-validation. + +The slice currently reuses `TrustedRuntimeHost`, whose module is exposed only +under the joint `native_rule_bootstrap` and `trusted_runtime` feature gate. That +compile-time coupling does not place a callback in the operation program, but +it prevents the surface from serving as the final product cutover boundary. +The host/WAL shell must be separated from `native_rule_bootstrap` before Jedit +can delete the legacy feature without also losing executable operations. ## Footprint Honesty diff --git a/docs/topics/WAL.md b/docs/topics/WAL.md index 993e9991..5a1348c9 100644 --- a/docs/topics/WAL.md +++ b/docs/topics/WAL.md @@ -17,7 +17,7 @@ Echo may only claim what its WAL can recover. ## What We Found -The current runtime WAL evidence says eight concrete things. +The current runtime WAL evidence says nine concrete things. First, accepted-submission evidence is not just an in-memory editor event. The WAL-backed ACK path, `submit_intent_with_runtime_wal_ack(...)`, returns only @@ -110,6 +110,23 @@ trusted-host API requires the current logical durable frontier and exact host-installed root support before invoking this transition, and returns only after commit. +Ninth, executable-operation installation and commitment use their own +append-only transaction kinds, record kinds, affected frontiers, and +authorities. Runtime-control authority retains the exact canonical operation +package together with its complete package-admission policy. Fresh-host +recovery canonical-decodes the package, recomputes every identity, reconstructs +the policy identity, and runs the same package-admission checks before restoring +the installed operation. It does not resolve or invoke application callbacks. +Execution-kernel authority separately retains one committed typed operation +receipt and its replayable state delta. Recovery requires exactly one of each, +revalidates the receipt's internal result evidence, checks the state delta's +basis, installed-operation rule identity, patch, tick, and state-root +correspondence, and reconstructs the installed and execution indexes +idempotently. Each new transaction kind rejects missing, duplicate, reversed, +or mis-scoped frame/frontier shapes. These records do not make a naked program +digest invocable and do not persist the process-local authority needed to +commit an unretained preparation. + ## Boundaries The WAL belongs to the trusted runtime host. Application-facing code can submit @@ -265,6 +282,14 @@ codes, committed round-trip, uncommitted-tail invisibility, required-frame cardinality, truncation, trailing bytes, malformed enum codes, cross-admission evidence mismatch, and WAL-coordinate binding. +The hook-free executable-operation and filesystem-recovery witnesses live in +`crates/warp-core/tests/executable_operation_pipeline_tests.rs`. They cover +canonical package and invocation admission, authority and budget refusal, +exact-basis noncommit, program-substitution refusal, same-host preparation +authority, typed private-evaluation obstruction, deterministic duplicate-host +results, append-only WAL codes, activation refusal for process-only +installations, and fresh-host installation and consequence recovery. + The host surface lives in `crates/warp-core/src/trusted_runtime_host.rs`, especially `TrustedRuntimeHost`, `TrustedRuntimeApp`, `TrustedRuntimeWal`, `submit_intent_with_runtime_wal_ack(...)`, `admit_causal_anchor(...)`, @@ -278,13 +303,14 @@ belong in GitHub. ## Current Caveat The trusted-runtime suite uses both the fast in-memory adapter and the strict -filesystem adapter. Filesystem witnesses now prove accepted-submission and -ticketed-transition replay across host reconstruction. The replayable -state-delta path is currently emitted from ticketed receipt correlations; work -committed through lower-level, non-ticketed scheduler ingress does not yet have -the same trusted-host WAL reconstruction claim. Jim must therefore use the -WAL-backed application-intent path for user-facing edits and inverses instead -of treating an internal runtime mutation as durable history. +filesystem adapter. Filesystem witnesses now prove accepted-submission, +ticketed-transition, and executable-operation replay across host +reconstruction. The scheduler replayable state-delta path is currently emitted +from ticketed receipt correlations; work committed through lower-level, +non-ticketed scheduler ingress does not yet have the same trusted-host WAL +reconstruction claim. Jim must therefore use a WAL-backed admitted-operation or +application-intent path for user-facing edits and inverses instead of treating +an internal runtime mutation as durable history. WSC export/import shape, retained reading availability, multi-correlation tick packing, and complete application-session projection recovery remain separate diff --git a/schemas/edict-provider/generated/v1/evidence/provenance.provider-generation.json b/schemas/edict-provider/generated/v1/evidence/provenance.provider-generation.json index af69c579..b57a41cc 100644 --- a/schemas/edict-provider/generated/v1/evidence/provenance.provider-generation.json +++ b/schemas/edict-provider/generated/v1/evidence/provenance.provider-generation.json @@ -1 +1 @@ -{"apiVersion":"wesley.generation-provenance-manifest/v1","contractVersions":{"generatorAbi":"wesley.extension-generator/v1","inputSchema":"wesley.extension-generation-input/v1","provenanceSchema":"wesley.generation-provenance-manifest/v1"},"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac","version":"0.1.0"},"settingsDigest":"sha256:0f708e76898de6fdb8186352e81d0f5c445adf94bb6c7de9204952d9fe913d4a","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file +{"apiVersion":"wesley.generation-provenance-manifest/v1","contractVersions":{"generatorAbi":"wesley.extension-generator/v1","inputSchema":"wesley.extension-generation-input/v1","provenanceSchema":"wesley.generation-provenance-manifest/v1"},"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a","version":"0.1.0"},"settingsDigest":"sha256:0f708e76898de6fdb8186352e81d0f5c445adf94bb6c7de9204952d9fe913d4a","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/schemas/edict-provider/generated/v1/evidence/review.provider-generation.json b/schemas/edict-provider/generated/v1/evidence/review.provider-generation.json index 12997f41..bdbbe353 100644 --- a/schemas/edict-provider/generated/v1/evidence/review.provider-generation.json +++ b/schemas/edict-provider/generated/v1/evidence/review.provider-generation.json @@ -1 +1 @@ -{"apiVersion":"wesley.generation-review/v1","authoritative":false,"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac","version":"0.1.0"},"projectionRoles":["authority-facts.echo-dpo","authority-facts.echo-lawpack","generated-artifact-profile.echo-dpo-registration","lawpack.echo-dpo","schema.echo-provider-artifacts","target-profile.echo-dpo"],"provenanceManifestDigest":"sha256:ce67a27c13a0f830f99abe4cabaf519b519c3fb5904d198dd9550d1b349d276e","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file +{"apiVersion":"wesley.generation-review/v1","authoritative":false,"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a","version":"0.1.0"},"projectionRoles":["authority-facts.echo-dpo","authority-facts.echo-lawpack","generated-artifact-profile.echo-dpo-registration","lawpack.echo-dpo","schema.echo-provider-artifacts","target-profile.echo-dpo"],"provenanceManifestDigest":"sha256:ddb18917c074ed929938ee4e231ff2b9382fb0acf132196e57f35a67fe7c1993","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json b/schemas/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json index af69c579..b57a41cc 100644 --- a/schemas/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json +++ b/schemas/edict-provider/package/v1/generated/evidence/provenance.provider-generation.json @@ -1 +1 @@ -{"apiVersion":"wesley.generation-provenance-manifest/v1","contractVersions":{"generatorAbi":"wesley.extension-generator/v1","inputSchema":"wesley.extension-generation-input/v1","provenanceSchema":"wesley.generation-provenance-manifest/v1"},"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac","version":"0.1.0"},"settingsDigest":"sha256:0f708e76898de6fdb8186352e81d0f5c445adf94bb6c7de9204952d9fe913d4a","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file +{"apiVersion":"wesley.generation-provenance-manifest/v1","contractVersions":{"generatorAbi":"wesley.extension-generator/v1","inputSchema":"wesley.extension-generation-input/v1","provenanceSchema":"wesley.generation-provenance-manifest/v1"},"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a","version":"0.1.0"},"settingsDigest":"sha256:0f708e76898de6fdb8186352e81d0f5c445adf94bb6c7de9204952d9fe913d4a","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/generated/evidence/review.provider-generation.json b/schemas/edict-provider/package/v1/generated/evidence/review.provider-generation.json index 12997f41..bdbbe353 100644 --- a/schemas/edict-provider/package/v1/generated/evidence/review.provider-generation.json +++ b/schemas/edict-provider/package/v1/generated/evidence/review.provider-generation.json @@ -1 +1 @@ -{"apiVersion":"wesley.generation-review/v1","authoritative":false,"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac","version":"0.1.0"},"projectionRoles":["authority-facts.echo-dpo","authority-facts.echo-lawpack","generated-artifact-profile.echo-dpo-registration","lawpack.echo-dpo","schema.echo-provider-artifacts","target-profile.echo-dpo"],"provenanceManifestDigest":"sha256:ce67a27c13a0f830f99abe4cabaf519b519c3fb5904d198dd9550d1b349d276e","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file +{"apiVersion":"wesley.generation-review/v1","authoritative":false,"emittedArtifacts":[{"coordinate":"echo.dpo-authority-facts@1","digest":"sha256:e4ecd21b5b4d7c97c66f9aa9ef55794fd8610f58a6ccb5e37f411576b8247ca9"},{"coordinate":"echo.dpo-lawpack-authority-facts@1","digest":"sha256:893af4f37e2821525cc609e5740e7e95ae0e1795ca2444d2267f438a69d2283f"},{"coordinate":"echo.dpo-lawpack@1","digest":"sha256:2539a9856295db8545db10d1001b6b1697e40bff0ea07b45afd33255d45d4269"},{"coordinate":"echo.dpo.registration/v1","digest":"sha256:4ef3aaad0d2131ba7129a3e2ae178d10c44f7e9b062af2c1fe211de781462f34"},{"coordinate":"echo.dpo@1","digest":"sha256:a2ecfe500dcedb25b22129a412a7c83379fc265d9f04792355425ff80b52a2ba"},{"coordinate":"echo.provider-artifacts.cddl@1","digest":"sha256:faece52eaf8ec040c374e5fe2a5ea040b522b58f415973f481e9c836ecfc4cde"}],"generationInputDigest":"sha256:aff2b3737d635728190e6252515d03a1c3a9b58031c6a126a9cc9007f3998aa8","generator":{"coordinate":"echo-wesley-gen.provider-artifact-generator@1","digest":"sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a","version":"0.1.0"},"projectionRoles":["authority-facts.echo-dpo","authority-facts.echo-lawpack","generated-artifact-profile.echo-dpo-registration","lawpack.echo-dpo","schema.echo-provider-artifacts","target-profile.echo-dpo"],"provenanceManifestDigest":"sha256:ddb18917c074ed929938ee4e231ff2b9382fb0acf132196e57f35a67fe7c1993","sourceArtifacts":[{"coordinate":"echo.semantic-schema@1","digest":"sha256:09e621edf0ba0a21bb602d759d7c66a5db394065ab2bc3578a6b7c31bc1ab335"},{"coordinate":"edict.provider-contract-pack.cddl@1","digest":"sha256:92697bc9a5262c68258be9ee451ee8c144aeb363b92142915b8224430b85cf74"},{"coordinate":"edict.provider-contract-pack.manifest@1","digest":"sha256:6902467149fec3e0338bb90e8cd7963ee21b8ce24f368f9b12e748343cbe0e4f"}]} \ No newline at end of file diff --git a/schemas/edict-provider/package/v1/provider-manifest.echo.json b/schemas/edict-provider/package/v1/provider-manifest.echo.json index a6098a92..a6e6d320 100644 --- a/schemas/edict-provider/package/v1/provider-manifest.echo.json +++ b/schemas/edict-provider/package/v1/provider-manifest.echo.json @@ -3,7 +3,7 @@ "providerAbi": "edict:target-provider@1.0.0", "provider": { "coordinate": "echo.edict-provider@1", - "digest": "sha256:e0ccd4503c7f5830a1affa1c5a676f866aa0fab976a5ec2a0075c70916a64b69" + "digest": "sha256:ee870c75ec08c8818b3f80ab6562ae62a5cf741cd709edcee0085d951c5d5a7b" }, "artifacts": [ { @@ -21,7 +21,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -40,7 +40,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -59,7 +59,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -78,7 +78,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -102,7 +102,7 @@ "artifactKind": "generationProvenance", "resource": { "coordinate": "echo.edict-provider-generation-provenance@1", - "digest": "sha256:2c6b23e85be5f3c36447135bf8c3fb9b24cdef2426f0e5d84889f6cc11f45228" + "digest": "sha256:509131f36dab0243077b70bb4f66c3d9a3eab0e9c6501b26f53d199f75483c56" }, "source": { "kind": "generated", @@ -112,7 +112,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -121,7 +121,7 @@ "artifactKind": "reviewArtifact", "resource": { "coordinate": "echo.edict-provider-generation-review@1", - "digest": "sha256:9748d55ab83e43e64fb37e48682a4ce4c104ed1dce969089335d53bb11a321ae" + "digest": "sha256:7db063768bdb03f8ee0e2a754c15ac0d25f0cd864d8351381e9fc40c6d6be10b" }, "source": { "kind": "generated", @@ -131,7 +131,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -150,7 +150,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, @@ -169,7 +169,7 @@ }, "generator": { "coordinate": "echo-wesley-gen.provider-artifact-generator@1", - "digest": "sha256:ee0caf86a92f8f7f8a84d40787b9dbb794dc7614ed9fe31aa02b243bc7d9f0ac" + "digest": "sha256:60e1d2c4b755d0dbf756aab7a52d6ba53dbaca2d2a6038773bb31eb58d9ed19a" } } }, diff --git a/scripts/verify-local.sh b/scripts/verify-local.sh index 0e8d3d11..89c6fcf0 100755 --- a/scripts/verify-local.sh +++ b/scripts/verify-local.sh @@ -1138,6 +1138,9 @@ pre_push_feature_string_for_file() { local file="$2" case "${crate}:${file}" in + warp-core:crates/warp-core/src/trusted_runtime_host.rs) + printf '%s\n' "native_rule_bootstrap,trusted_runtime" + ;; warp-wasm:crates/warp-wasm/src/warp_kernel.rs|\ warp-wasm:crates/warp-wasm/Cargo.toml) printf '%s\n' "engine" @@ -1153,9 +1156,14 @@ pre_push_feature_string_for_test_target() { warp-core:inbox|warp-core:installed_contract_intent_pipeline_tests) printf '%s\n' "native_rule_bootstrap,host_test" ;; - warp-core:external_consumer_contract_fixture_tests|warp-core:provider_contract_admission_tests) + warp-core:external_consumer_contract_fixture_tests|\ + warp-core:provider_contract_admission_tests|\ + warp-core:executable_operation_pipeline_tests) printf '%s\n' "native_rule_bootstrap,trusted_runtime" ;; + warp-core:installed_contract_registry_tests) + printf '%s\n' "native_rule_bootstrap" + ;; warp-core:trusted_runtime_host_loop_tests) printf '%s\n' "native_rule_bootstrap,trusted_runtime,host_test" ;; @@ -1411,18 +1419,12 @@ prepare_warp_math_scope() { warp_core_feature_args_for_test() { local test_target="$1" + local features - case "$test_target" in - inbox|installed_contract_intent_pipeline_tests) - printf '%s\n' "--features" "native_rule_bootstrap,host_test" - ;; - external_consumer_contract_fixture_tests|provider_contract_admission_tests) - printf '%s\n' "--features" "native_rule_bootstrap,trusted_runtime" - ;; - causal_fact_publication_tests|optic_invocation_admission_tests) - printf '%s\n' "--features" "host_test" - ;; - esac + features="$(pre_push_feature_string_for_test_target "warp-core" "$test_target")" + if [[ -n "$features" ]]; then + printf '%s\n' "--features" "$features" + fi } run_warp_core_test_target() { diff --git a/tests/docs/test_generated_rule_truth.sh b/tests/docs/test_generated_rule_truth.sh index 8377cc6a..56edf0ce 100755 --- a/tests/docs/test_generated_rule_truth.sh +++ b/tests/docs/test_generated_rule_truth.sh @@ -66,17 +66,25 @@ require_literal "$readme" 'The helper performs pure, fail-closed preflight acros require_literal "$readme" 'opaque, non-installing provider package proposal' require_literal "$readme" 'The helper does not construct an `InstalledContractPackage`, register or' require_literal "$readme" '`AdmittedProviderContractPackageV1`' -require_literal "$readme" 'mutation closure now spans publication through provider-native Echo execution' +require_literal "$readme" 'mutation closure spans publication through provider-native Echo execution and' require_literal "$readme" 'provider record and the package, root, mutation-operation, and scheduler-rule' require_literal "$readme" 'After a generated client submits canonical EINT v1 bytes, the trusted host can' require_literal "$readme" 'exact EINT intent-kind domain and an installed provider operation before it' require_literal "$readme" 'same-scope system acknowledgement cannot stand in for provider' require_literal "$readme" 'Those crossings prove the first local provider-mutation execution and recovery' -require_literal "$readme" 'The first Edict mutation branch of the package-shaped flow is now executable;' -require_literal "$readme" 'Wesley packaging and generated bounded reads are not yet complete:' +require_literal "$readme" 'These corridors are not one pipeline with optional source nouns.' +require_literal "$readme" 'The current Wesley compatibility path is:' +require_literal "$readme" 'The current Edict provider-v1 compatibility path is:' +require_literal "$readme" 'The current executable-operation runtime slice is:' +require_literal "$readme" 'installed data-only EchoOperationProgramV1' +require_literal "$readme" 'The first two paths are callback-shaped compatibility infrastructure.' +require_literal "$readme" 'The host supplies no application matcher, executor, or' +require_literal "$readme" 'Deterministic runtime judgments' reject_literal "$readme" 'It does not yet provide codec-bound invocation' reject_literal "$readme" 'The package-shaped flow below is the target corridor, not a current end-to-end application path:' reject_literal "$readme" 'Wesley generates type-safe helpers, codecs, registry metadata, and host adapters.' +reject_literal "$readme" 'admitted Wesley or Edict source' +reject_literal "$readme" 'bind the host-supplied executor or observer' require_literal "$jim_case" 'produce an opaque' require_literal "$jim_case" '`ProviderContractPackageProposalV1`. The proposal retains generated registry' diff --git a/tests/edict-provider-host-v1/Cargo.lock b/tests/edict-provider-host-v1/Cargo.lock index 02005372..a7e2b57d 100644 --- a/tests/edict-provider-host-v1/Cargo.lock +++ b/tests/edict-provider-host-v1/Cargo.lock @@ -421,6 +421,14 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "echo-edict-canonical" +version = "0.1.0" +dependencies = [ + "hex", + "sha2", +] + [[package]] name = "echo-edict-provider-host-v1-witness" version = "0.0.0" @@ -1202,6 +1210,7 @@ dependencies = [ "blake3", "bytemuck", "bytes", + "echo-edict-canonical", "echo-registry-api", "echo-runtime-schema", "echo-wasm-abi", diff --git a/tests/hooks/test_verify_local.sh b/tests/hooks/test_verify_local.sh index 4e4a1e2a..517b4135 100755 --- a/tests/hooks/test_verify_local.sh +++ b/tests/hooks/test_verify_local.sh @@ -268,6 +268,7 @@ edition = "2021" EOF printf '%s\n' 'pub fn anchor() {}' >"$tmp/crates/warp-core/src/lib.rs" printf '%s\n' '#[cfg(test)] mod tests { #[test] fn ok() {} }' >"$tmp/crates/warp-core/src/observation.rs" + printf '%s\n' '#[cfg(test)] mod tests { #[test] fn ok() {} }' >"$tmp/crates/warp-core/src/trusted_runtime_host.rs" printf '%s\n' 'pub fn helper() {}' >"$tmp/crates/warp-core/tests/common/mod.rs" cat >"$tmp/crates/warp-math/Cargo.toml" <<'EOF' @@ -1610,6 +1611,15 @@ else printf '%s\n' "$fake_pre_push_provider_contract_output" fi +fake_pre_push_installed_contract_output="$(run_fake_verify pre-push crates/warp-core/tests/installed_contract_registry_tests.rs)" +fake_pre_push_installed_contract_cargo_log="$(extract_log_section cargo-log "$fake_pre_push_installed_contract_output")" +if printf '%s\n' "$fake_pre_push_installed_contract_cargo_log" | grep -q -- 'test -p warp-core --features native_rule_bootstrap --test installed_contract_registry_tests'; then + pass "pre-push keeps required installed-contract feature for the exact integration test" +else + fail "pre-push should keep required native_rule_bootstrap feature for installed_contract_registry_tests" + printf '%s\n' "$fake_pre_push_installed_contract_output" +fi + fake_pre_push_external_contract_output="$(run_fake_verify pre-push crates/warp-core/tests/external_consumer_contract_fixture_tests.rs)" fake_pre_push_external_contract_cargo_log="$(extract_log_section cargo-log "$fake_pre_push_external_contract_output")" if printf '%s\n' "$fake_pre_push_external_contract_cargo_log" | grep -q -- 'test -p warp-core --features native_rule_bootstrap,trusted_runtime --test external_consumer_contract_fixture_tests'; then @@ -1646,6 +1656,15 @@ else printf '%s\n' "$fake_pre_push_warp_math_prng_src_output" fi +fake_pre_push_trusted_runtime_src_output="$(run_fake_verify pre-push crates/warp-core/src/trusted_runtime_host.rs)" +fake_pre_push_trusted_runtime_src_cargo_log="$(extract_log_section cargo-log "$fake_pre_push_trusted_runtime_src_output")" +if printf '%s\n' "$fake_pre_push_trusted_runtime_src_cargo_log" | grep -q -- 'test -p warp-core --features native_rule_bootstrap,trusted_runtime --lib trusted_runtime_host::tests'; then + pass "pre-push keeps required features for trusted-runtime inline tests" +else + fail "pre-push should keep required features for trusted_runtime_host inline tests" + printf '%s\n' "$fake_pre_push_trusted_runtime_src_output" +fi + fake_pre_push_warp_math_root_test_output="$(run_fake_verify pre-push crates/warp-math/src/root_level_test.rs)" fake_pre_push_warp_math_root_test_cargo_log="$(extract_log_section cargo-log "$fake_pre_push_warp_math_root_test_output")" if printf '%s\n' "$fake_pre_push_warp_math_root_test_cargo_log" | grep -q -- 'test -p warp-math --lib root_level_test::tests'; then