kaymyg commited on
Commit ·
a74f5ab
1
Parent(s): f6c6557
Implement kaysentinel-hash (domain-separated BLAKE3 commitments, RC1 spec); update README and roadmap to reflect real progress
Browse files- README.md +33 -3
- docs/hash_specification.md +99 -0
- docs/implementation_roadmap.md +32 -3
- runtime/Cargo.lock +65 -0
- runtime/hash/Cargo.toml +1 -0
- runtime/hash/src/lib.rs +210 -1
- runtime/hash/vectors/candidate.json +30 -0
README.md
CHANGED
|
@@ -10,23 +10,42 @@ tags:
|
|
| 10 |
|
| 11 |
A post-execution authorization framework for state-machine systems (e.g. Ethereum-class runtimes). Kaysentinel lifts execution traces into a canonical **Structural Sufficient Representation (SSR)** and evaluates policy purely over that quotient space, so authorization decisions are provably independent of *which* client (Geth, Reth, ...) produced the trace.
|
| 12 |
|
| 13 |
-
> **Status:**
|
| 14 |
|
| 15 |
## Repo contents
|
| 16 |
|
| 17 |
- [`docs/framework.md`](docs/framework.md) — full formal spec: execution model, SSR canonical form, authorization/factorization theory, multi-client portability theorem, gating semantics, complexity model.
|
| 18 |
- [`docs/differential_testing.md`](docs/differential_testing.md) — the differential testing engine design used to check byte-level convergence between a Geth-side and Reth-side SSR extractor.
|
| 19 |
- [`docs/implementation_roadmap.md`](docs/implementation_roadmap.md) — concrete engineering tasks (Phase 1–3) to turn the spec into a working implementation.
|
|
|
|
|
|
|
|
|
|
| 20 |
- Curated test fixtures now live in a separate dataset repo: [`Sahek/kaysentinel-fixtures`](https://huggingface.co/datasets/Sahek/kaysentinel-fixtures).
|
| 21 |
- [`scripts/verify_ssr.py`](scripts/verify_ssr.py) — standalone verifier that hashes and byte-diffs two SSZ-encoded SSR outputs.
|
| 22 |
- [`tests/transient_storage_case.json`](tests/transient_storage_case.json) — example test vector (EIP-1153 transient storage + reentrancy rollback).
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
## Roadmap
|
| 25 |
|
| 26 |
-
|
| 27 |
|
| 28 |
**Phase 1 — Canonical type system**
|
| 29 |
-
- [
|
|
|
|
| 30 |
|
| 31 |
**Phase 2 — Client extractors**
|
| 32 |
- [ ] Geth-side extractor (`StateDB` journal hook)
|
|
@@ -36,6 +55,17 @@ Nothing below is implemented yet. See [`docs/implementation_roadmap.md`](docs/im
|
|
| 36 |
- [x] Verification harness (`scripts/verify_ssr.py`) — comparison logic only, done
|
| 37 |
- [ ] Test runner environment + GitHub Actions CI
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
## License
|
| 40 |
|
| 41 |
MIT — see [`LICENSE`](LICENSE).
|
|
|
|
| 10 |
|
| 11 |
A post-execution authorization framework for state-machine systems (e.g. Ethereum-class runtimes). Kaysentinel lifts execution traces into a canonical **Structural Sufficient Representation (SSR)** and evaluates policy purely over that quotient space, so authorization decisions are provably independent of *which* client (Geth, Reth, ...) produced the trace.
|
| 12 |
|
| 13 |
+
> **Status:** formal specification, differential-test harness skeleton, and a working Rust reference implementation of the canonical event ABI, the builder pipeline (partition → reduce → lifecycle resolution → certificate assembly), and domain-separated cryptographic commitments. There is not yet a working Geth/Reth extractor, SSZ canonical encoding, or storage-trie derivation — see the roadmap below for exactly what's real vs. still open.
|
| 14 |
|
| 15 |
## Repo contents
|
| 16 |
|
| 17 |
- [`docs/framework.md`](docs/framework.md) — full formal spec: execution model, SSR canonical form, authorization/factorization theory, multi-client portability theorem, gating semantics, complexity model.
|
| 18 |
- [`docs/differential_testing.md`](docs/differential_testing.md) — the differential testing engine design used to check byte-level convergence between a Geth-side and Reth-side SSR extractor.
|
| 19 |
- [`docs/implementation_roadmap.md`](docs/implementation_roadmap.md) — concrete engineering tasks (Phase 1–3) to turn the spec into a working implementation.
|
| 20 |
+
- [`docs/semantic_architecture_spec.md`](docs/semantic_architecture_spec.md) — formal stage contracts (partition → projection → canonicalization → serialization → commitment) with real implementation cross-references.
|
| 21 |
+
- [`docs/hash_specification.md`](docs/hash_specification.md) — normative cryptographic commitment protocol (domain-separated BLAKE3), implemented in `runtime/hash`.
|
| 22 |
+
- [`runtime/`](runtime/) — the Rust reference implementation. See below.
|
| 23 |
- Curated test fixtures now live in a separate dataset repo: [`Sahek/kaysentinel-fixtures`](https://huggingface.co/datasets/Sahek/kaysentinel-fixtures).
|
| 24 |
- [`scripts/verify_ssr.py`](scripts/verify_ssr.py) — standalone verifier that hashes and byte-diffs two SSZ-encoded SSR outputs.
|
| 25 |
- [`tests/transient_storage_case.json`](tests/transient_storage_case.json) — example test vector (EIP-1153 transient storage + reentrancy rollback).
|
| 26 |
|
| 27 |
+
## `runtime/` — Rust reference implementation
|
| 28 |
+
|
| 29 |
+
A Cargo workspace. Status per crate, honestly:
|
| 30 |
+
|
| 31 |
+
| Crate | Status |
|
| 32 |
+
| --- | --- |
|
| 33 |
+
| `cse` | **Done.** Canonical Semantic Event ABI: frozen event/payload types, execution context, a sequence/transaction-boundary validator. |
|
| 34 |
+
| `builder` | **In progress, core pipeline works.** Partitions a CSE stream into flat relational tables (`partition.rs`), reduces per-key timelines to terminal transitions (`reduce.rs`), resolves account lifecycle generations (`lifecycle/resolve.rs`, `canonicalize.rs`), and assembles canonical account certificates from real state + generation data (`lifecycle/certificate.rs`, `hydration.rs`). 21 passing unit tests across the workspace. |
|
| 35 |
+
| `hash` | **Core primitive done.** Domain-separated BLAKE3 commitments (`derive_commitment`), per the RC1 spec. 8 passing tests, including real (not hand-typed) pinned vectors — see `runtime/hash/vectors/candidate.json`. Not yet cross-language-verified (no Python/Go implementation exists to check against), so nothing has been promoted to `normative.json`. |
|
| 36 |
+
| `ssr` | Empty stub. |
|
| 37 |
+
| `ssz` | Empty stub — canonical encoding doesn't exist yet, so nothing has real bytes to hash yet either. |
|
| 38 |
+
| `verify` | Empty stub. |
|
| 39 |
+
|
| 40 |
+
Known open gaps (tracked in `docs/semantic_architecture_spec.md`): no real `AccountSnapshotSource` (prior-block state) implementation; storage roots are always `AwaitingDerivation` (no Phase 5 trie construction); access-list touches aren't tracked; several `TraceProvenance`/generation-chain fields (`frame_ordinal`, `ProvenanceMetadataDivergence`, `MalformedProvenanceChain`) are defined but not yet wired to real detection logic.
|
| 41 |
+
|
| 42 |
## Roadmap
|
| 43 |
|
| 44 |
+
See [`docs/implementation_roadmap.md`](docs/implementation_roadmap.md) for the full phased task breakdown. Short version:
|
| 45 |
|
| 46 |
**Phase 1 — Canonical type system**
|
| 47 |
+
- [x] Canonical Semantic Event ABI (`runtime/cse`)
|
| 48 |
+
- [ ] SSZ schema + serialization (`runtime/ssz` — stub only)
|
| 49 |
|
| 50 |
**Phase 2 — Client extractors**
|
| 51 |
- [ ] Geth-side extractor (`StateDB` journal hook)
|
|
|
|
| 55 |
- [x] Verification harness (`scripts/verify_ssr.py`) — comparison logic only, done
|
| 56 |
- [ ] Test runner environment + GitHub Actions CI
|
| 57 |
|
| 58 |
+
**Phase 4 — Builder pipeline (Rust reference implementation)**
|
| 59 |
+
- [x] Partition + terminal projection (`runtime/builder/src/partition.rs`, `ir/timeline.rs`)
|
| 60 |
+
- [x] Lifecycle resolution + canonicalization (`runtime/builder/src/lifecycle/`)
|
| 61 |
+
- [x] Certificate hydration + assembly (`runtime/builder/src/lifecycle/{certificate,hydration}.rs`)
|
| 62 |
+
- [ ] Storage subtree/trie derivation (`StorageRootDeriver` trait exists, no implementation)
|
| 63 |
+
|
| 64 |
+
**Phase 5 — Cryptographic commitment**
|
| 65 |
+
- [x] Domain-separated BLAKE3 commitment primitive (`runtime/hash`)
|
| 66 |
+
- [ ] Cross-language (Python/Go) differential verification of vectors
|
| 67 |
+
- [ ] Wire real commitments into `VerifiedGeneration.state_table_proof_root` and `CanonicalAccountCertificate.storage_root`
|
| 68 |
+
|
| 69 |
## License
|
| 70 |
|
| 71 |
MIT — see [`LICENSE`](LICENSE).
|
docs/hash_specification.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# KAYSentinel Hash Specification (RC1)
|
| 2 |
+
|
| 3 |
+
Normative cryptographic protocol for `kaysentinel-hash`.
|
| 4 |
+
|
| 5 |
+
**Status:** Release Candidate 1 (RC1). The Rust reference implementation exists and
|
| 6 |
+
is self-consistent (deterministic, domain-separated, regression-pinned by unit
|
| 7 |
+
tests). Cross-language verification (Python/Go) has **not** happened — no
|
| 8 |
+
implementations exist in those languages yet — so vectors in `vectors/candidate.json`
|
| 9 |
+
remain `"status": "candidate"`, not `"normative"`, per the promotion criteria below.
|
| 10 |
+
|
| 11 |
+
## 1. Terminology
|
| 12 |
+
|
| 13 |
+
The key words "MUST", "MUST NOT", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", and
|
| 14 |
+
"MAY" in this document are to be interpreted as described in RFC 2119 and updated by
|
| 15 |
+
RFC 8174.
|
| 16 |
+
|
| 17 |
+
## 2. Cryptographic Primitive & Configuration
|
| 18 |
+
|
| 19 |
+
* **Core Algorithm:** The protocol SHALL use the BLAKE3 algorithm. All commitments
|
| 20 |
+
MUST use the standard unkeyed BLAKE3 hash function. Keyed hashing, `derive_key`
|
| 21 |
+
mode, and extendable-output mode (XOF) are prohibited.
|
| 22 |
+
* **Commitment Layout:** The resulting digest is an immutable sequence of exactly 32
|
| 23 |
+
bytes (256 bits). No integer interpretation, byte-swapping, or endianness applies
|
| 24 |
+
to the final digest sequence.
|
| 25 |
+
* **Security Constraints:** The security architecture relies strictly on the
|
| 26 |
+
collision resistance and preimage resistance properties of the underlying BLAKE3
|
| 27 |
+
hash function.
|
| 28 |
+
* **Portability & Determinism:** Given identical `DomainBytes` and `CanonicalBytes`,
|
| 29 |
+
all conforming implementations SHALL produce identical digest bytes on every
|
| 30 |
+
platform, host architecture, and execution environment.
|
| 31 |
+
|
| 32 |
+
## 3. Domain Separation Architecture
|
| 33 |
+
|
| 34 |
+
```
|
| 35 |
+
HashInput := DomainBytes || CanonicalBytes
|
| 36 |
+
Digest := BLAKE3(HashInput)
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
* **DomainBytes**: the raw, fixed byte sequence representing the unique protocol
|
| 40 |
+
constant.
|
| 41 |
+
* **CanonicalBytes**: the deterministic, byte-stable serialization of the underlying
|
| 42 |
+
data structure.
|
| 43 |
+
* **||**: strict byte concatenation.
|
| 44 |
+
|
| 45 |
+
Core invariants:
|
| 46 |
+
|
| 47 |
+
1. **Structural Ordering:** domain separation SHALL precede all canonical bytes. No
|
| 48 |
+
protocol structure may insert additional bytes, padding, version flags,
|
| 49 |
+
delimiters, framing bytes, or metadata between the domain identifier and the
|
| 50 |
+
canonical serialization.
|
| 51 |
+
2. **Infallibility by Contract:** commitment derivation is total and cannot fail once
|
| 52 |
+
valid canonical bytes have been produced.
|
| 53 |
+
3. **No Normalization:** domain identifiers SHALL be matched strictly byte-for-byte.
|
| 54 |
+
Unicode normalization, case folding, locale conversion, whitespace trimming, or
|
| 55 |
+
character encoding transformations are prohibited.
|
| 56 |
+
|
| 57 |
+
## 4. Canonical Encoding Separation
|
| 58 |
+
|
| 59 |
+
The canonical encoding of every protocol structure is defined independently of the
|
| 60 |
+
hashing function. Canonical encoding SHALL be deterministic, injective (no two
|
| 61 |
+
distinct logical objects serialize to identical byte sequences), and entirely
|
| 62 |
+
independent of the runtime environment. The hashing function operates exclusively on
|
| 63 |
+
raw, opaque byte sequences and is not responsible for serialization semantics.
|
| 64 |
+
|
| 65 |
+
**Status:** no canonical encoding (SSZ) exists yet — `runtime/ssz` is still an empty
|
| 66 |
+
stub crate. Everything hashed so far in tests/vectors is raw test bytes, not real
|
| 67 |
+
canonical-encoded protocol structures.
|
| 68 |
+
|
| 69 |
+
## 5. Hash Domain Registry (Version: 1)
|
| 70 |
+
|
| 71 |
+
Future protocol versions MAY append new domain tags to this registry. Existing tags
|
| 72 |
+
SHALL remain permanently immutable, SHALL NOT change their underlying semantic
|
| 73 |
+
meaning, and SHALL NEVER be reused or recycled upon feature retirement. No two
|
| 74 |
+
protocol operations SHALL share the same domain byte sequence.
|
| 75 |
+
|
| 76 |
+
| Protocol Constant | Normative Value (ASCII) | Context / Usage |
|
| 77 |
+
| --- | --- | --- |
|
| 78 |
+
| `KAY_TRIE_LEAF` | `"KAY_TRIE_LEAF"` | Storage trie leaf nodes |
|
| 79 |
+
| `KAY_TRIE_BRANCH` | `"KAY_TRIE_BRANCH"` | Storage trie branch nodes |
|
| 80 |
+
| `KAY_LIFE_CERT` | `"KAY_LIFE_CERT"` | Verification certificates |
|
| 81 |
+
| `KAY_SSR_ROOT` | `"KAY_SSR_ROOT"` | Serialization Record roots |
|
| 82 |
+
|
| 83 |
+
## 6. Reference Implementation
|
| 84 |
+
|
| 85 |
+
`runtime/hash/src/lib.rs` implements this specification: `Digest`, `Domain`,
|
| 86 |
+
`derive_commitment(domain, canonical_bytes) -> Digest`. 8 unit tests cover
|
| 87 |
+
determinism, domain separation, length validation, and pin real (not hand-typed)
|
| 88 |
+
BLAKE3 output for one vector per registered domain — see
|
| 89 |
+
`runtime/hash/vectors/candidate.json`.
|
| 90 |
+
|
| 91 |
+
## 7. Vector Status & Promotion Criteria
|
| 92 |
+
|
| 93 |
+
Per the execution lifecycle, a vector is `"candidate"` once a reference
|
| 94 |
+
implementation computes and regression-pins it, and becomes `"normative"` only after
|
| 95 |
+
independent implementations in multiple languages are shown to converge on the same
|
| 96 |
+
digest via automated differential verification. `runtime/hash/vectors/candidate.json`
|
| 97 |
+
holds real, computed, Rust-self-consistent values — but since no Python or Go
|
| 98 |
+
implementation exists yet to check against, none have been promoted to
|
| 99 |
+
`vectors/normative.json`, and that file doesn't exist yet.
|
docs/implementation_roadmap.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
# Implementation Roadmap
|
| 2 |
|
| 3 |
-
Concrete engineering tasks to turn the [framework spec](framework.md) and [differential testing design](differential_testing.md) into working code.
|
|
|
|
|
|
|
| 4 |
|
| 5 |
## Phase 1 — Canonical Type System & Serialization
|
| 6 |
|
|
@@ -9,7 +11,20 @@ The serialization layer needs to be settled before client logic begins, to preve
|
|
| 9 |
- [ ] **1.1 Define the strict SSZ schema.** Draft the official SSZ definitions for `CanonicalSSR` and its sub-containers. Enforce little-endian byte ordering for integers and explicit padding rules for `Uint256` (32-byte arrays).
|
| 10 |
- [ ] **1.2 Implement serialization libraries.**
|
| 11 |
- **Go:** integrate an SSZ library (e.g. [`fastssz`](https://github.com/ferranbt/fastssz)) to generate static encoder/decoder methods for the Go-side SSR types.
|
| 12 |
-
- **Rust:** configure an SSZ crate (e.g. `ssz` or `lighthouse_types`) to derive encoding logic on the Rust structs.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
## Phase 2 — Client Extractor Modules
|
| 15 |
|
|
@@ -21,7 +36,7 @@ The serialization layer needs to be settled before client logic begins, to preve
|
|
| 21 |
|
| 22 |
### Reth extractor (Rust)
|
| 23 |
|
| 24 |
-
- [ ] **3.1 Intercept execution outputs.** Inject the extraction module where `BlockExecutionResult` yields its finalized in-memory `BundleState`.
|
| 25 |
- [ ] **3.2 Map BundleState structural diffs.** Map state maps to `AccountMutation` vectors; use `sort_by_key` to enforce the same lexicographic ordering as the Go side over addresses and slots.
|
| 26 |
|
| 27 |
## Phase 3 — Infrastructure & Differential Testing CI
|
|
@@ -63,14 +78,28 @@ Depends entirely on Phase 1 and Phase 2 being complete — there's no extractor
|
|
| 63 |
|
| 64 |
See the companion dataset repo: [`Sahek/kaysentinel-fixtures`](https://huggingface.co/datasets/Sahek/kaysentinel-fixtures) for the curated Phase 3 test fixtures that exist today.
|
| 65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
## Current status summary
|
| 67 |
|
| 68 |
| Phase | Item | Status |
|
| 69 |
|---|---|---|
|
| 70 |
| 1 | SSZ schema definition | Not started |
|
| 71 |
| 1 | Go/Rust serialization libs | Not started |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
| 2 | Geth extractor | Not started |
|
| 73 |
| 2 | Reth extractor | Not started |
|
| 74 |
| 3 | Verification harness (`verify_ssr.py`) | Done (comparison logic only — see script docstring) |
|
| 75 |
| 3 | Test runner environment | Not started |
|
| 76 |
| 3 | CI automation | Not started |
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# Implementation Roadmap
|
| 2 |
|
| 3 |
+
Concrete engineering tasks to turn the [framework spec](framework.md) and [differential testing design](differential_testing.md) into working code.
|
| 4 |
+
|
| 5 |
+
> **Update:** the Rust reference implementation (`runtime/`) now has real, tested code for the canonical event ABI and most of the builder pipeline (Phase 1.2's Rust half, partially) plus the cryptographic commitment primitive (Phase 5). Status per item below has been updated accordingly — everything not explicitly marked done is still not started.
|
| 6 |
|
| 7 |
## Phase 1 — Canonical Type System & Serialization
|
| 8 |
|
|
|
|
| 11 |
- [ ] **1.1 Define the strict SSZ schema.** Draft the official SSZ definitions for `CanonicalSSR` and its sub-containers. Enforce little-endian byte ordering for integers and explicit padding rules for `Uint256` (32-byte arrays).
|
| 12 |
- [ ] **1.2 Implement serialization libraries.**
|
| 13 |
- **Go:** integrate an SSZ library (e.g. [`fastssz`](https://github.com/ferranbt/fastssz)) to generate static encoder/decoder methods for the Go-side SSR types.
|
| 14 |
+
- **Rust:** configure an SSZ crate (e.g. `ssz` or `lighthouse_types`) to derive encoding logic on the Rust structs. **Not started** — `runtime/ssz` is still an empty stub crate. The canonical event *types* that would eventually be serialized do exist (`runtime/cse`), but nothing encodes them to SSZ bytes yet.
|
| 15 |
+
|
| 16 |
+
## Phase 1.5 — Rust Builder Pipeline (new, not in the original phase breakdown)
|
| 17 |
+
|
| 18 |
+
The event-to-certificate compiler pipeline that consumes a validated `CanonicalSemanticEvent` stream (from `runtime/cse`) and produces canonical per-account certificates. This sits logically between Phase 1 (types) and Phase 2 (extractors) — it's what a real extractor's output would eventually flow into, but it doesn't extract from a real client itself (see Phase 2 status, unchanged).
|
| 19 |
+
|
| 20 |
+
- [x] **Canonical Semantic Event ABI** (`runtime/cse`) — frozen event/payload types, execution context, sequence/transaction-boundary validation.
|
| 21 |
+
- [x] **Partition + terminal projection** (`runtime/builder/src/partition.rs`, `ir/timeline.rs`) — flattens an event stream into relational, `CanonicalKey`-keyed tables; reduces per-key timelines to verified terminal transitions.
|
| 22 |
+
- [x] **Lifecycle resolution + canonicalization** (`runtime/builder/src/lifecycle/{resolve,canonicalize}.rs`) — resolves account creation/destruction into identity "generations," with formal invariant verification (uniqueness, temporal well-formedness, chronological adjacency).
|
| 23 |
+
- [x] **Certificate hydration + assembly** (`runtime/builder/src/lifecycle/{certificate,hydration}.rs`) — cross-references real state facts (balance/nonce/code) to resolved generations to build `CanonicalAccountCertificate`s, with a real (if unimplemented) `AccountSnapshotSource`/`StorageRootDeriver` extension point for prior-state and Phase-5 storage-trie data.
|
| 24 |
+
- [ ] Storage subtree/trie derivation — `StorageRootDeriver` trait exists, no implementation. Every certificate's `storage_root` is currently `AwaitingDerivation`.
|
| 25 |
+
- [ ] Access-list tracking — `CsePayload::AccessListTouched` events are parsed but not recorded anywhere in the pipeline.
|
| 26 |
+
|
| 27 |
+
21 passing unit tests across `runtime/cse` + `runtime/builder`. See `docs/semantic_architecture_spec.md` for the full stage-by-stage contract writeup and honest gap list.
|
| 28 |
|
| 29 |
## Phase 2 — Client Extractor Modules
|
| 30 |
|
|
|
|
| 36 |
|
| 37 |
### Reth extractor (Rust)
|
| 38 |
|
| 39 |
+
- [ ] **3.1 Intercept execution outputs.** Inject the extraction module where `BlockExecutionResult` yields its finalized in-memory `BundleState`. **Not started** — nothing in `runtime/` reads from a real Reth `BundleState` yet; the builder pipeline (Phase 1.5) consumes already-abstracted `CanonicalSemanticEvent`s, which still need to be produced by a real extractor hook.
|
| 40 |
- [ ] **3.2 Map BundleState structural diffs.** Map state maps to `AccountMutation` vectors; use `sort_by_key` to enforce the same lexicographic ordering as the Go side over addresses and slots.
|
| 41 |
|
| 42 |
## Phase 3 — Infrastructure & Differential Testing CI
|
|
|
|
| 78 |
|
| 79 |
See the companion dataset repo: [`Sahek/kaysentinel-fixtures`](https://huggingface.co/datasets/Sahek/kaysentinel-fixtures) for the curated Phase 3 test fixtures that exist today.
|
| 80 |
|
| 81 |
+
## Phase 5 — Cryptographic Commitment (new, not in the original phase breakdown)
|
| 82 |
+
|
| 83 |
+
- [x] **Domain-separated BLAKE3 commitment primitive** (`runtime/hash`) — `derive_commitment(domain, canonical_bytes) -> Digest`, per `docs/hash_specification.md` (RC1). 8 passing tests, including real computed-and-pinned vectors per domain.
|
| 84 |
+
- [ ] Cross-language (Python/Go) differential verification of vectors — no Python/Go implementation exists yet, so nothing in `runtime/hash/vectors/candidate.json` has been promoted to `normative.json`.
|
| 85 |
+
- [ ] Wire real commitments into `VerifiedGeneration.state_table_proof_root` and `CanonicalAccountCertificate.storage_root` — both are still placeholder/`AwaitingDerivation` values in the builder pipeline.
|
| 86 |
+
|
| 87 |
## Current status summary
|
| 88 |
|
| 89 |
| Phase | Item | Status |
|
| 90 |
|---|---|---|
|
| 91 |
| 1 | SSZ schema definition | Not started |
|
| 92 |
| 1 | Go/Rust serialization libs | Not started |
|
| 93 |
+
| 1.5 | Canonical Semantic Event ABI (`runtime/cse`) | **Done** |
|
| 94 |
+
| 1.5 | Builder pipeline: partition + terminal projection | **Done** |
|
| 95 |
+
| 1.5 | Builder pipeline: lifecycle resolution + canonicalization | **Done** |
|
| 96 |
+
| 1.5 | Builder pipeline: certificate hydration + assembly | **Done** |
|
| 97 |
+
| 1.5 | Storage subtree/trie derivation | Not started (`StorageRootDeriver` trait only) |
|
| 98 |
| 2 | Geth extractor | Not started |
|
| 99 |
| 2 | Reth extractor | Not started |
|
| 100 |
| 3 | Verification harness (`verify_ssr.py`) | Done (comparison logic only — see script docstring) |
|
| 101 |
| 3 | Test runner environment | Not started |
|
| 102 |
| 3 | CI automation | Not started |
|
| 103 |
+
| 5 | BLAKE3 commitment primitive (`runtime/hash`) | **Done** |
|
| 104 |
+
| 5 | Cross-language vector verification | Not started |
|
| 105 |
+
| 5 | Real commitments wired into builder pipeline | Not started |
|
runtime/Cargo.lock
CHANGED
|
@@ -2,9 +2,65 @@
|
|
| 2 |
# It is not intended for manual editing.
|
| 3 |
version = 3
|
| 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
[[package]]
|
| 6 |
name = "kaysentinel-builder"
|
| 7 |
version = "0.1.0"
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
[[package]]
|
| 10 |
name = "kaysentinel-cse"
|
|
@@ -13,6 +69,9 @@ version = "0.1.0"
|
|
| 13 |
[[package]]
|
| 14 |
name = "kaysentinel-hash"
|
| 15 |
version = "0.1.0"
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
[[package]]
|
| 18 |
name = "kaysentinel-ssr"
|
|
@@ -25,3 +84,9 @@ version = "0.1.0"
|
|
| 25 |
[[package]]
|
| 26 |
name = "kaysentinel-verify"
|
| 27 |
version = "0.1.0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
# It is not intended for manual editing.
|
| 3 |
version = 3
|
| 4 |
|
| 5 |
+
[[package]]
|
| 6 |
+
name = "arrayref"
|
| 7 |
+
version = "0.3.9"
|
| 8 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 9 |
+
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
|
| 10 |
+
|
| 11 |
+
[[package]]
|
| 12 |
+
name = "arrayvec"
|
| 13 |
+
version = "0.7.8"
|
| 14 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 15 |
+
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
|
| 16 |
+
|
| 17 |
+
[[package]]
|
| 18 |
+
name = "blake3"
|
| 19 |
+
version = "1.5.0"
|
| 20 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 21 |
+
checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87"
|
| 22 |
+
dependencies = [
|
| 23 |
+
"arrayref",
|
| 24 |
+
"arrayvec",
|
| 25 |
+
"cc",
|
| 26 |
+
"cfg-if",
|
| 27 |
+
"constant_time_eq",
|
| 28 |
+
]
|
| 29 |
+
|
| 30 |
+
[[package]]
|
| 31 |
+
name = "cc"
|
| 32 |
+
version = "1.2.67"
|
| 33 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 34 |
+
checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38"
|
| 35 |
+
dependencies = [
|
| 36 |
+
"find-msvc-tools",
|
| 37 |
+
"shlex",
|
| 38 |
+
]
|
| 39 |
+
|
| 40 |
+
[[package]]
|
| 41 |
+
name = "cfg-if"
|
| 42 |
+
version = "1.0.4"
|
| 43 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 44 |
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
| 45 |
+
|
| 46 |
+
[[package]]
|
| 47 |
+
name = "constant_time_eq"
|
| 48 |
+
version = "0.3.1"
|
| 49 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 50 |
+
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
| 51 |
+
|
| 52 |
+
[[package]]
|
| 53 |
+
name = "find-msvc-tools"
|
| 54 |
+
version = "0.1.9"
|
| 55 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 56 |
+
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
| 57 |
+
|
| 58 |
[[package]]
|
| 59 |
name = "kaysentinel-builder"
|
| 60 |
version = "0.1.0"
|
| 61 |
+
dependencies = [
|
| 62 |
+
"kaysentinel-cse",
|
| 63 |
+
]
|
| 64 |
|
| 65 |
[[package]]
|
| 66 |
name = "kaysentinel-cse"
|
|
|
|
| 69 |
[[package]]
|
| 70 |
name = "kaysentinel-hash"
|
| 71 |
version = "0.1.0"
|
| 72 |
+
dependencies = [
|
| 73 |
+
"blake3",
|
| 74 |
+
]
|
| 75 |
|
| 76 |
[[package]]
|
| 77 |
name = "kaysentinel-ssr"
|
|
|
|
| 84 |
[[package]]
|
| 85 |
name = "kaysentinel-verify"
|
| 86 |
version = "0.1.0"
|
| 87 |
+
|
| 88 |
+
[[package]]
|
| 89 |
+
name = "shlex"
|
| 90 |
+
version = "2.0.1"
|
| 91 |
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
| 92 |
+
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
runtime/hash/Cargo.toml
CHANGED
|
@@ -5,3 +5,4 @@ edition.workspace = true
|
|
| 5 |
license.workspace = true
|
| 6 |
|
| 7 |
[dependencies]
|
|
|
|
|
|
| 5 |
license.workspace = true
|
| 6 |
|
| 7 |
[dependencies]
|
| 8 |
+
blake3 = "=1.5.0"
|
runtime/hash/src/lib.rs
CHANGED
|
@@ -1 +1,210 @@
|
|
| 1 |
-
//!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//! # Kaysentinel Hash
|
| 2 |
+
//!
|
| 3 |
+
//! Reference implementation of `SPECIFICATION.md` (Normative Cryptographic Protocol,
|
| 4 |
+
//! RC1): domain-separated BLAKE3 commitments over canonical byte sequences.
|
| 5 |
+
//!
|
| 6 |
+
//! `Digest := BLAKE3(DomainBytes || CanonicalBytes)` — unkeyed BLAKE3, no
|
| 7 |
+
//! `derive_key` mode, no XOF, no padding or framing bytes between the domain
|
| 8 |
+
//! identifier and the canonical serialization.
|
| 9 |
+
|
| 10 |
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
| 11 |
+
pub enum DigestError {
|
| 12 |
+
InvalidLength,
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
impl std::fmt::Display for DigestError {
|
| 16 |
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
| 17 |
+
match self {
|
| 18 |
+
Self::InvalidLength => write!(f, "Invalid digest length: must be exactly 32 bytes"),
|
| 19 |
+
}
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
impl std::error::Error for DigestError {}
|
| 24 |
+
|
| 25 |
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
| 26 |
+
pub struct Digest([u8; 32]);
|
| 27 |
+
|
| 28 |
+
impl Digest {
|
| 29 |
+
/// Creates a Digest wrapper from an explicit 32-byte array.
|
| 30 |
+
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
|
| 31 |
+
Self(bytes)
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
/// Returns the raw 32-byte digest.
|
| 35 |
+
pub const fn to_bytes(&self) -> [u8; 32] {
|
| 36 |
+
self.0
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/// Lowercase hex encoding of the digest, matching the vector-file convention.
|
| 40 |
+
pub fn to_hex(&self) -> String {
|
| 41 |
+
self.0.iter().map(|b| format!("{:02x}", b)).collect()
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
impl AsRef<[u8]> for Digest {
|
| 46 |
+
fn as_ref(&self) -> &[u8] {
|
| 47 |
+
&self.0
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
impl std::borrow::Borrow<[u8]> for Digest {
|
| 52 |
+
fn borrow(&self) -> &[u8] {
|
| 53 |
+
&self.0
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
impl From<[u8; 32]> for Digest {
|
| 58 |
+
fn from(bytes: [u8; 32]) -> Self {
|
| 59 |
+
Self::from_bytes(bytes)
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
impl TryFrom<&[u8]> for Digest {
|
| 64 |
+
type Error = DigestError;
|
| 65 |
+
|
| 66 |
+
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
|
| 67 |
+
if slice.len() == 32 {
|
| 68 |
+
let mut bytes = [0u8; 32];
|
| 69 |
+
bytes.copy_from_slice(slice);
|
| 70 |
+
Ok(Self::from_bytes(bytes))
|
| 71 |
+
} else {
|
| 72 |
+
Err(DigestError::InvalidLength)
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
/// Hash Domain Registry (Version: 1). Existing tags are permanently immutable once
|
| 78 |
+
/// published — new domains may be appended, but nothing here may be renamed,
|
| 79 |
+
/// reinterpreted, or recycled.
|
| 80 |
+
#[non_exhaustive]
|
| 81 |
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
| 82 |
+
pub enum Domain {
|
| 83 |
+
TrieLeaf,
|
| 84 |
+
TrieBranch,
|
| 85 |
+
LifeCert,
|
| 86 |
+
SsrRoot,
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
impl Domain {
|
| 90 |
+
pub const fn as_bytes(&self) -> &'static [u8] {
|
| 91 |
+
match self {
|
| 92 |
+
Domain::TrieLeaf => b"KAY_TRIE_LEAF",
|
| 93 |
+
Domain::TrieBranch => b"KAY_TRIE_BRANCH",
|
| 94 |
+
Domain::LifeCert => b"KAY_LIFE_CERT",
|
| 95 |
+
Domain::SsrRoot => b"KAY_SSR_ROOT",
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
/// Total function that computes the cryptographic commitment per the specification:
|
| 101 |
+
/// BLAKE3(DomainBytes || CanonicalBytes). Infallible once `canonical_bytes` exists —
|
| 102 |
+
/// there is no failure mode once you have valid input bytes.
|
| 103 |
+
pub fn derive_commitment(domain: Domain, canonical_bytes: &[u8]) -> Digest {
|
| 104 |
+
let mut hasher = blake3::Hasher::new();
|
| 105 |
+
hasher.update(domain.as_bytes());
|
| 106 |
+
hasher.update(canonical_bytes);
|
| 107 |
+
Digest::from_bytes(hasher.finalize().into())
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
#[cfg(test)]
|
| 111 |
+
mod tests {
|
| 112 |
+
use super::*;
|
| 113 |
+
|
| 114 |
+
#[test]
|
| 115 |
+
fn commitment_is_deterministic() {
|
| 116 |
+
let bytes = [1u8, 2, 3, 4];
|
| 117 |
+
let a = derive_commitment(Domain::TrieLeaf, &bytes);
|
| 118 |
+
let b = derive_commitment(Domain::TrieLeaf, &bytes);
|
| 119 |
+
assert_eq!(a, b);
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
#[test]
|
| 123 |
+
fn different_domains_produce_different_digests() {
|
| 124 |
+
let bytes = [1u8, 2, 3, 4];
|
| 125 |
+
let leaf = derive_commitment(Domain::TrieLeaf, &bytes);
|
| 126 |
+
let branch = derive_commitment(Domain::TrieBranch, &bytes);
|
| 127 |
+
assert_ne!(leaf, branch);
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
#[test]
|
| 131 |
+
fn different_canonical_bytes_produce_different_digests() {
|
| 132 |
+
let a = derive_commitment(Domain::LifeCert, &[1, 2, 3]);
|
| 133 |
+
let b = derive_commitment(Domain::LifeCert, &[1, 2, 4]);
|
| 134 |
+
assert_ne!(a, b);
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
#[test]
|
| 138 |
+
fn digest_try_from_rejects_wrong_length() {
|
| 139 |
+
let short = [0u8; 16];
|
| 140 |
+
assert_eq!(Digest::try_from(&short[..]), Err(DigestError::InvalidLength));
|
| 141 |
+
|
| 142 |
+
let exact = [0u8; 32];
|
| 143 |
+
assert!(Digest::try_from(&exact[..]).is_ok());
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
#[test]
|
| 147 |
+
fn digest_round_trips_through_bytes() {
|
| 148 |
+
let bytes = [0xABu8; 32];
|
| 149 |
+
let digest = Digest::from_bytes(bytes);
|
| 150 |
+
assert_eq!(digest.to_bytes(), bytes);
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
/// Computes the real BLAKE3 digest for the candidate vector in
|
| 154 |
+
/// `vectors/candidate.json` (domain = KAY_TRIE_LEAF, canonical_bytes = 01020304)
|
| 155 |
+
/// and prints it, so the actual output can be copied into `vectors/normative.json`
|
| 156 |
+
/// rather than trusting a hand-typed value. Run with `cargo test -- --nocapture`.
|
| 157 |
+
#[test]
|
| 158 |
+
fn print_candidate_vector_1_digest() {
|
| 159 |
+
let canonical_bytes = [0x01u8, 0x02, 0x03, 0x04];
|
| 160 |
+
let digest = derive_commitment(Domain::TrieLeaf, &canonical_bytes);
|
| 161 |
+
println!("candidate_vector_1 digest_hex = {}", digest.to_hex());
|
| 162 |
+
// 32 bytes -> 64 hex characters, always.
|
| 163 |
+
assert_eq!(digest.to_hex().len(), 64);
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
/// Regression pin for candidate_vector_1. The doc that specified this vector
|
| 167 |
+
/// claimed a digest_hex value that was actually 66 hex characters long — an
|
| 168 |
+
/// impossible length for a 32-byte BLAKE3 digest, so it was never real. This
|
| 169 |
+
/// pins the value this implementation actually computes, so any future change
|
| 170 |
+
/// to the hashing logic (or the blake3 dependency version) that silently
|
| 171 |
+
/// changes output is caught immediately.
|
| 172 |
+
#[test]
|
| 173 |
+
fn candidate_vector_1_matches_pinned_digest() {
|
| 174 |
+
let canonical_bytes = [0x01u8, 0x02, 0x03, 0x04];
|
| 175 |
+
let digest = derive_commitment(Domain::TrieLeaf, &canonical_bytes);
|
| 176 |
+
assert_eq!(
|
| 177 |
+
digest.to_hex(),
|
| 178 |
+
"80522501585b8ebf1831439e57add0002d22fe75521120104b000cc707b5a34a"
|
| 179 |
+
);
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
/// One pinned regression vector per registered domain, all against the same
|
| 183 |
+
/// canonical bytes — proves domain separation holds and gives every domain a
|
| 184 |
+
/// real, computed (not hand-typed) reference value.
|
| 185 |
+
#[test]
|
| 186 |
+
fn per_domain_vectors_are_pinned_and_distinct() {
|
| 187 |
+
let canonical_bytes = [0x01u8, 0x02, 0x03, 0x04];
|
| 188 |
+
let leaf = derive_commitment(Domain::TrieLeaf, &canonical_bytes).to_hex();
|
| 189 |
+
let branch = derive_commitment(Domain::TrieBranch, &canonical_bytes).to_hex();
|
| 190 |
+
let cert = derive_commitment(Domain::LifeCert, &canonical_bytes).to_hex();
|
| 191 |
+
let ssr = derive_commitment(Domain::SsrRoot, &canonical_bytes).to_hex();
|
| 192 |
+
|
| 193 |
+
println!("leaf = {leaf}");
|
| 194 |
+
println!("branch = {branch}");
|
| 195 |
+
println!("cert = {cert}");
|
| 196 |
+
println!("ssr = {ssr}");
|
| 197 |
+
|
| 198 |
+
assert_eq!(leaf, "80522501585b8ebf1831439e57add0002d22fe75521120104b000cc707b5a34a");
|
| 199 |
+
assert_eq!(branch, "51614b8a4a5f5a2b5b7b858e626cd19b710d670ebad2e0b9b296bf282b4bfe93");
|
| 200 |
+
assert_eq!(cert, "59fd735ed0c0923c4316ad197223aed69b6667f6a00a228aae1638a521d1f62f");
|
| 201 |
+
assert_eq!(ssr, "1007c1368120e75ff7f8a47d6933f1440e1a6e0fd1b5e10f8721329dd5d47617");
|
| 202 |
+
|
| 203 |
+
let all = [&leaf, &branch, &cert, &ssr];
|
| 204 |
+
for i in 0..all.len() {
|
| 205 |
+
for j in (i + 1)..all.len() {
|
| 206 |
+
assert_ne!(all[i], all[j], "domain separation violated between vectors {i} and {j}");
|
| 207 |
+
}
|
| 208 |
+
}
|
| 209 |
+
}
|
| 210 |
+
}
|
runtime/hash/vectors/candidate.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"name": "candidate_vector_1",
|
| 4 |
+
"domain": "KAY_TRIE_LEAF",
|
| 5 |
+
"canonical_hex": "01020304",
|
| 6 |
+
"digest_hex": "80522501585b8ebf1831439e57add0002d22fe75521120104b000cc707b5a34a",
|
| 7 |
+
"status": "candidate"
|
| 8 |
+
},
|
| 9 |
+
{
|
| 10 |
+
"name": "candidate_vector_2",
|
| 11 |
+
"domain": "KAY_TRIE_BRANCH",
|
| 12 |
+
"canonical_hex": "01020304",
|
| 13 |
+
"digest_hex": "51614b8a4a5f5a2b5b7b858e626cd19b710d670ebad2e0b9b296bf282b4bfe93",
|
| 14 |
+
"status": "candidate"
|
| 15 |
+
},
|
| 16 |
+
{
|
| 17 |
+
"name": "candidate_vector_3",
|
| 18 |
+
"domain": "KAY_LIFE_CERT",
|
| 19 |
+
"canonical_hex": "01020304",
|
| 20 |
+
"digest_hex": "59fd735ed0c0923c4316ad197223aed69b6667f6a00a228aae1638a521d1f62f",
|
| 21 |
+
"status": "candidate"
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"name": "candidate_vector_4",
|
| 25 |
+
"domain": "KAY_SSR_ROOT",
|
| 26 |
+
"canonical_hex": "01020304",
|
| 27 |
+
"digest_hex": "1007c1368120e75ff7f8a47d6933f1440e1a6e0fd1b5e10f8721329dd5d47617",
|
| 28 |
+
"status": "candidate"
|
| 29 |
+
}
|
| 30 |
+
]
|