kaysentinel / docs /validation_vector_spec.md
karanjahpn
Add SSZ-V1 profile with working reference encoder; fix 174/178-byte discrepancy; compute real Layer 2 hashes
b0951d6
|
Raw
History Blame Contribute Delete
6.24 kB
# 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.