karanjahpn
Add SSZ-V1 profile with working reference encoder; fix 174/178-byte discrepancy; compute real Layer 2 hashes
b0951d6 | # KAY Sentinel: Validation Vector Specification (v1.0.0) | |
| > **Status:** normative test-vector specification. This supersedes the | |
| > informal vector blueprint sketched in `semantic_contract.md` with a strict | |
| > JSON Schema (`../schemas/validation_vector_schema.v1.json`) and a fixed | |
| > vocabulary of journal mutation events. Like the rest of this repo, this is | |
| > a design/test-authoring artifact, not a working implementation: no | |
| > extractor exists yet to produce these mutation streams or hashes from a | |
| > real client, so every vector here is hand-authored against the spec, not | |
| > machine-verified against Geth/Reth. | |
| ## 1. Core Semantic Invariants | |
| Every conforming extractor **SHALL** satisfy: | |
| - **Minimality Invariant:** no identity deltas survive normalization. If an | |
| account property or storage slot returns to its exact pre-state value at | |
| the transaction boundary, it MUST be culled from the output. | |
| - **Determinism Invariant:** identical raw state inputs under equivalent | |
| environment contexts SHALL yield identical normalized SSRs, regardless of | |
| client-internal architecture. | |
| - **Ordering Invariant:** account arrays and storage updates MUST follow | |
| strict ascending unsigned byte-wise lexicographical order (Β§2.2). | |
| - **Rollback Invariant:** mutations accumulated in a sub-context frame that | |
| later hits `FRAME_REVERT` SHALL become entirely non-observable in the | |
| finalized SSR. | |
| - **Isolation Invariant:** `TRANSIENT_MUTATION` events MUST NOT bleed into | |
| the finalized persistent storage update array. | |
| - **Fork Invariant:** lifecycle mutation and destruction tracking SHALL | |
| adapt to the behavior defined by the active `fork_identifier` (e.g. | |
| EIP-6780 SELFDESTRUCT semantics only apply from CANCUN onward). | |
| ## 2. Layer 1 β Mutation Typings & Semantics | |
| ### 2.1 Normative Event Definitions | |
| A test harness ingests a `journal_mutation_sequence`: a stream of explicit, | |
| observable state transitions. **The ordering of elements in that array is | |
| illustrative/input-only** β extractors MUST be invariant to the relative | |
| interleaving of orthogonal client mutations; only the final per-address, | |
| per-slot values at the transaction boundary matter for the output SSR. | |
| | Action | Meaning | | |
| |---|---| | |
| | `NONCE_MUTATION` | account's nonce counter changes; carries `new_nonce` | | |
| | `BALANCE_MUTATION` | native balance changes; carries `new_balance` | | |
| | `CODE_MUTATION` | bytecode deployed/wiped; carries `new_code` | | |
| | `STORAGE_MUTATION` | persistent storage slot write; carries `slot`, `new_value` | | |
| | `TRANSIENT_MUTATION` | ephemeral (transient-storage) write; carries `slot`, `new_value`; excluded from the SSR per the Isolation Invariant | | |
| | `FRAME_REVERT` | the current `context_depth`'s uncommitted mutations are discarded | | |
| Note: there is no dedicated `CREATE`/`SELFDESTRUCT` action. Account | |
| lifecycle (creation, destruction, resurrection) is inferred from the | |
| combination of `NONCE_MUTATION` / `CODE_MUTATION` / `BALANCE_MUTATION` | |
| events on the same address, in the same way a real client journal would | |
| expose it. See `KS-LC-001` in the fixtures repo for a worked example and the | |
| gap this leaves in the create-then-destroy omission rule. | |
| ### 2.2 Canonical Byte-Wise Ordering Rules | |
| - **Account deltas:** compared as fixed 20-byte sequences, ascending. | |
| - **Storage updates:** compared as fixed 32-byte sequences, ascending. | |
| Ordering is computed on raw bytes, not on the hex string representation, to | |
| avoid divergence between implementations that differ in case-folding or | |
| locale-aware string comparison. | |
| ## 3. Layer 1 Schema | |
| The full JSON Schema is at | |
| [`../schemas/validation_vector_schema.v1.json`](../schemas/validation_vector_schema.v1.json). | |
| Every vector under `validation_vectors/` in the fixtures repo validates | |
| against it β this is checked mechanically, not just asserted (see | |
| `scripts/validate_vectors.py`, TODO in the implementation roadmap). | |
| `vector_id` follows `KS-(ST|RB|LC|TS|OR)-NNN`, where the category code maps | |
| to the `category` field: **ST**=Storage, **RB**=Rollback, **LC**=Lifecycle, | |
| **TS**=Transient, **OR**=Ordering. | |
| A note on the account-existence representation: since this schema has no | |
| explicit null/"account does not exist" type, a nonexistent account is | |
| represented with `nonce: 0`, `balance: "0"`, and `code_hash` equal to | |
| `EMPTY_CODE_HASH` (`keccak256("")` = | |
| `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`). This | |
| matches Ethereum's own state-clearing convention (EIP-161): an account with | |
| those exact values is indistinguishable from one that was never created. | |
| ## 4. Primary Conformance Vectors | |
| Vectors live in the fixtures repo under `validation_vectors/`: | |
| - `KS-ST-001` β storage identity-loop pruning (Minimality Invariant) | |
| - `KS-LC-001` β same-tx create β destroy β recreate ("resurrection"); tests | |
| the gap in the create-then-destroy omission rule | |
| - `KS-OR-001` β cross-account and cross-slot byte-wise ordering with | |
| deliberately out-of-order input | |
| ## 5. Layer 2 β Serialization Profile (`SSZ-V1`) | |
| - **Serialization spec:** SSZ (fixed-width elements sequential; variable-width | |
| lists via offsets). | |
| - **Hash engine:** SHA-256 (standard SSZ `hash_tree_root`). | |
| **Status: computable, and computed.** [`scripts/ssz_reference.py`](../scripts/ssz_reference.py) | |
| is a runnable reference encoder/merkleizer (built on the existing | |
| `remerkleable` SSZ library, not a from-scratch reimplementation) that | |
| produces real `serialized_length` and `expected_root_hash` values for every | |
| vector in `validation_vectors/`. The current values are recorded in | |
| [`schemas/ssz_v1_conformance_manifest.json`](../schemas/ssz_v1_conformance_manifest.json) | |
| and are reproducible by anyone: `pip install -r scripts/requirements.txt`, | |
| then run the script against the fixtures repo's vectors. | |
| This closes the "no fabricated hashes" gap for the encoding layer β these | |
| are real, checkable values, not placeholders. It does **not** mean the | |
| project has a working extractor: these hashes are computed from the | |
| hand-authored `expected_normalized_ssr` in each fixture, not from a real | |
| Geth or Reth execution trace. Whether a real client actually produces that | |
| `expected_normalized_ssr` in the first place is still open β that's Phase | |
| 2/3 of the implementation roadmap. | |