open-standards/smart-byte/conformance/README.md
Transaction Science f9fbd5a3af
Some checks failed
smart-byte conformance gate / Validate conformance JSON files (pull_request) Successful in 4s
smart-byte conformance gate / Second implementation reproduces every vector (pull_request) Successful in 5s
smart-byte conformance gate / Spec changes require vector-pack updates (pull_request) Successful in 4s
smart-byte conformance gate / VERSIONS.md and README.md must exist (pull_request) Successful in 3s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 4s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m51s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m24s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m41s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m10s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m38s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 7m45s
standards workspaces build + test / aac-rs (pull_request) Successful in 4m3s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 1m51s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 2m3s
standards workspaces build + test / map-rs (pull_request) Successful in 5m27s
standards workspaces build + test / mesh-rs (pull_request) Successful in 2m19s
standards workspaces build + test / openpay (pull_request) Successful in 10m49s
standards workspaces build + test / arl-rs (pull_request) Successful in 47m4s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 46m44s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 42m33s
standards workspaces build + test / joulehook-rs (pull_request) Failing after 18m22s
standards workspaces build + test / sense-rs (pull_request) Successful in 10m25s
standards workspaces build + test / macOS energy backend (jcp-energy) (pull_request) Successful in 4m56s
smart-byte: governance, burns and transfers between clusters, with vectors
smart-byte-state implements cluster_state.md:
- governance: Change and Proposal, electorates, the quorum, the time lock;
- burns: Burn and the retired record;
- exports: ExportStatement and Certificate;
- the import checks.

smart-byte-core: Transition::sign_export and the destination member. The
envelope compares frames within a cluster only.

smart-byte-lockstep: the committing nodes sign each committed frame's export
certificates. The certificates are built from a committing node's report,
never from a node whose state diverged.

state_vectors.json grows to 23 vectors (state-013 to state-023 new,
state-011 extended) and is regenerated in place; v1 is DRAFT. Every new check
has a vector that fails an implementation leaving it out. The README
documents the 14 new error classes, and the regeneration log records the
change.

crosscheck/verify_v1.py implements governance, burns, exports and imports
from the spec text. It reproduces every vector, 117 of them, both plain and
under -O.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 11:48:44 -04:00

295 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Smart Byte Conformance Vectors
This directory contains the conformance test-vector pack for the Smart Byte
substrate specification. Any third-party implementation, in any language, can
consume these vectors to self-certify against the canonical wire format
*without* trusting the reference implementation.
The vectors are the social contract. The spec text in `../spec/` says what the
substrate is; the vectors here say what the substrate's wire format *looks
like, byte for byte*, on the inputs the spec text describes. An implementation
that passes the vectors is canonical. An implementation that fails the vectors
is not.
The vectors are licensed CC-BY-4.0, consistent with the spec directory. They
contain no `$ref` pointers, no external URLs, and no runtime dependencies.
## What is in the pack
Each spec version has its own subdirectory (`v1/`, `v2/`, ...). Today only
`v1/` exists; it corresponds to the spec text in `../spec/`
(`treatise_v1_parts_I_II_III_combined.md`, `identity_and_key_rotation.md`,
`envelope_wire_format.md` and `cluster_state.md`).
A pack for a given version contains eight JSON files plus `regeneration.md`:
| File | Tests |
| -------------------------------- | --------------------------------------------------------------------- |
| `canonicalisation_vectors.json` | Canonical CBOR: each encoding rule, accepted and refused (identity §2.3) |
| `said_vectors.json` | SAID derivation and verification (identity §2.1–§2.5; treatise §8.2) |
| `signature_vectors.json` | Ed25519 over the messages Smart Byte signs, verified strictly (RFC 8032) |
| `kel_vectors.json` | Key-event logs: inception, rotation, interaction, thresholds (identity §3, §4) |
| `envelope_vectors.json` | Envelopes decoded and verified against the logs' key states (envelope wire format) |
| `state_vectors.json` | The cluster state machine: frames of inputs, their results, the state and its hash — governance, burns and transfers between clusters included (cluster state) |
| `lockstep_vectors.json` | Supermajority commit, frame outcomes, dissent log (treatise §13, §14) |
| `gossip_vectors.json` | Receiving SAID-addressed objects (identity §2.6; treatise §11.4, §21.4) |
Every value in the pack is generated by the Rust reference implementation
(`../smart-byte-rs`, crate `smart-byte-conformance`) and reproduced by an
independent second implementation, `crosscheck/verify_v1.py`.
`regeneration.md` documents both, and records every change to the pack.
## How to consume the vectors
Each JSON file is a top-level array of vector objects. Vector objects share
the common shape:
```json
{
"name": "short-stable-identifier",
"description": "human-readable description of what is being tested",
...test-specific fields...
}
```
The conventional flow for an implementation under test (IUT):
1. Load the JSON file for the relevant spec version.
2. For each vector, run the IUT against the `input` (or equivalent field)
and compare its output to the `expected_*` field.
3. A vector passes if the IUT's output is byte-for-byte equal to the
`expected_*` field (or, for negative vectors, the IUT rejects the input
with the documented `error` class).
4. The IUT is canonical for a spec version iff every vector in that
version's pack passes.
There is no scoring, no "mostly conforms" status, and no waiver process.
A vector pack is a hard gate.
### Byte strings in JSON
JSON has no byte-string type, so an `input` writes a CBOR byte string as an
object whose only member is `$bytes`, holding lowercase hex:
`{"$bytes": "00ff"}`. `$bytes` is reserved: no map in the pack has a member
of that name. Every other JSON value maps to CBOR directly: object → map with
text keys, array → array, string → text string, integer → integer,
`true` / `false` / `null` → the simple values. The pack contains no JSON number
with a fraction or exponent, because identity spec §2.3 forbids floats, and no
object names a member twice.
Where a vector supplies raw bytes instead (`*_cbor_hex`, `input_cbor_hex`),
they are lowercase hex.
### Fields, file by file
- **`canonicalisation_vectors.json`** — a positive vector has `input` and
`expected_cbor_hex`; a negative one has `input_cbor_hex`, which a strict
decoder must refuse with `error`. Refusing means not decoding: an IUT must
never re-encode non-canonical input into canonical form and accept it.
- **`said_vectors.json`** — `said_field` names the member that carries the
SAID (`d`). A positive vector's `input` holds the 53-`#` placeholder there.
`canonical_cbor_hex` is the canonical encoding of `input` (the bytes that
are hashed), `blake3_hex` is their BLAKE3-256, `expected_said` is that
digest written `E` + lowercase unpadded base32, and `said_cbor_hex` is the
object with its SAID in place, which must verify. A negative vector's
`input_cbor_hex` must fail SAID verification with `error`. The adversarial
vectors also give, for illustration, the SAIDs a lenient verifier would
compute (`said_of_received_bytes`, `said_after_reencoding`).
- **`signature_vectors.json`** — the Ed25519 rules every Smart Byte signature
follows: RFC 8032, with strict decoding of keys and scalars. The message is
`message_hex`, the canonical encoding of what is signed: sig-001's is an
issuer's attestation, sig-002's a key event. `verifying_key` is the key
handed to the verifier; when `signing_key_seed` is not null, it is the key
that seed derives. Verify `signature` under `verifying_key`: the result must
be `expected_verify`, failing with `error` when false. A positive vector's
`signature` is the one deterministic signature its seed makes.
- **`kel_vectors.json`** — `events` is one controller's log, in order. Each
event gives its bytes (`cbor_hex`), the same event as typed JSON (`event`)
when the bytes are canonical CBOR, and its `signatures`: each a key `index`
and a `signature` — the key's algorithm identifier, `01` for Ed25519,
followed by the signature over the event's bytes (identity §3.5). A
signature with a `seed` is the one that seed's key makes. Replay the log as
a v1 verifier (identity §4). An accepted log gives `expected`: the
controller AID, each event's SAID, the key state each establishment event
put in force (`sequence`, `keys` in hex, `threshold`), and the next-key
commitment in force at the end. A refused log gives the `error` class and
`error_at`, the position of the event refused; no later event is
considered.
- **`envelope_vectors.json`** — `cbor_hex` is an envelope, also given as
typed JSON (`envelope`) when the bytes are canonical CBOR. `key_event_logs`
names accepted vectors of `kel_vectors.json`; signatures are checked
against the key states their replay establishes. Decode and verify the
envelope (envelope wire format §8). An accepted envelope gives `expected`:
its identity, snapshot SAID, issuer, current owner, history hash (hex) and
number of transfers. A refused one gives the `error` class.
- **`state_vectors.json`** — `genesis` is a genesis state: as typed JSON
(`state`), as bytes (`cbor_hex`), and its `state_hash`. Apply each of
`frames` in order, as the cluster state spec (§4) describes: `inputs` are
the frame's inputs as submitted, each as bytes (`cbor_hex`) and, when they
are canonical CBOR, typed JSON (`input`). `expected_results` gives one
result per distinct input, in the order they apply: the input's BLAKE3
digest, the `class` it names (null if none), and `outcome` — `applied`,
or `dropped` with the `error` class. Then the state must be
`expected_state_cbor_hex`, also given as typed JSON, with hash
`expected_state_hash`.
- **`lockstep_vectors.json`** — `node_count` is the cluster size n.
`node_reported_hashes` are the opaque 32-byte state hashes each node
broadcast, and `node_computed_hashes` the ones each computed; they differ
only for a node that lies on the wire (treatise §13.4). Hashes are compared
only for equality. The IUT computes `expected_threshold` (floor(2n/3)+1),
then `expected_commit` and `expected_state_hash` from the reported hashes —
the frame's public record. It also computes each node's `expected_outcomes`
(`clean_commit`, `commit_with_dissent`, `minority_divergence` or
`cluster_split`) and `expected_dissent_logs` from that node's own view: its
peers' reported hashes plus the hash it computed.
- **`gossip_vectors.json`** — `deliveries` arrive at `receiver` in order, each
with the SAID it was announced or requested under (`claimed_said`) and its
bytes (`cbor_hex`). Each result is `accepted` or `duplicate` with the SAID,
or `rejected` with an error class, per identity spec §2.6. `expected_held`
lists what the receiver holds afterwards, in acceptance order: each SAID
with exactly the bytes delivered.
### Error classes
| Class | Meaning |
| ------------------------- | ---------------------------------------------------------- |
| `non_shortest_integer` | an integer not written with its shortest head |
| `non_shortest_length` | a string, array or map length not written with its shortest head |
| `unsorted_map_keys` | map keys not in the bytewise order of their encodings |
| `duplicate_map_key` | a key appears twice in one map |
| `float_forbidden` | a floating-point value |
| `simple_value_forbidden` | a simple value other than `false`, `true` and `null` |
| `tag_forbidden` | a CBOR tag |
| `indefinite_length` | an indefinite-length item |
| `invalid_utf8` | a text string that is not UTF-8 |
| `trailing_bytes` | bytes after the one top-level item |
| `said_mismatch` | content that does not hash to the SAID it states or arrived under |
| `signature_invalid` | a signature that does not verify, including a non-canonical S |
| `malformed_signature` | an Ed25519 signature that is not 64 bytes |
| `malformed_verifying_key` | a key that is not 32 bytes, or encodes y outside the field |
| `unsupported_algorithm` | a key and signature in an algorithm other than Ed25519 (`01`) |
| `algorithm_mismatch` | a signature whose algorithm identifier differs from its key's |
| `key_index_out_of_range` | a signature by a key index outside the key set |
| `duplicate_key_index` | one key's signature listed twice |
| `below_threshold` | fewer valid signatures than the key state's threshold |
| `unknown_key_state` | a signature naming an establishment event no log establishes |
| `malformed_event` | canonical CBOR that is not exactly a v1 event: an undefined member, a member of the wrong type or form, an unknown type or an extension v1 does not define; or an event out of place, such as a log that does not begin with an inception or an inception with anchors |
| `unsupported_version` | an event whose `v` is not `SBYTE10JSON` |
| `unsupported_event_type` | a `rec` event, which v1 reserves, or a `dip`, which v1 does not define |
| `out_of_sequence` | an event whose `s` does not follow the previous event's |
| `prior_mismatch` | an event whose `p` is not the previous event's `d`, or an inception's that is not empty |
| `controller_mismatch` | an event whose `i` is not its log's controller, or an inception's that is not the AID its SAID names |
| `pre_rotation_mismatch` | a rotation revealing keys other than those committed to |
| `rotation_not_permitted` | a rotation after an establishment event that committed to no next keys |
| `interaction_changes_keys`| an interaction whose key fields differ from those in force |
| `invalid_threshold` | a `kt` outside 1 to the number of keys, or an `nt` that is not 0 for no next keys and 1 to their number otherwise |
| `duplicate_key` | a key or next-key digest listed twice in one event |
| `not_an_envelope` | canonical CBOR that is not exactly an envelope: an undefined member, or a member of the wrong type or form |
| `broken_history_chain` | a transition that does not continue the history it follows |
| `not_current_owner` | a transition not signed away by the byte's current owner |
| `frame_not_increasing` | a transition in the same frame as the one before it, or an earlier one |
| `malformed_input` | canonical CBOR that is not exactly a frame input of a class there is |
| `controller_exists` | an inception for a controller the cluster already holds |
| `unknown_controller` | a key event or signature of a controller whose log the cluster does not hold |
| `establishment_not_in_force` | a signature naming an establishment event other than the one in force |
| `issuer_not_registered` | a mint by an issuer the registry does not list for the cargo's type |
| `duplicate_byte` | a mint of a byte the cluster already holds |
| `unknown_byte` | a transfer of a byte the cluster does not hold |
| `wrong_frame` | a transfer or burn naming a frame other than the one applied |
| `byte_retired` | an input acting on a byte that was burned |
| `byte_exported` | an input acting on a byte that left for another cluster |
| `wrong_cluster` | an input addressed to another cluster, or a cluster named as its own peer |
| `unknown_cluster` | a byte sent to, or imported from, a cluster that is not a peer |
| `duplicate_import` | an export certificate the cluster has imported already |
| `certificate_mismatch` | an import whose envelope is not the byte its certificate certifies |
| `stale_sequence` | a proposal made against a governance sequence number other than the cluster's |
| `already_registered` | a proposal to add an issuer already registered for the cargo type |
| `not_registered` | a proposal to remove an issuer not registered for the cargo type |
| `not_a_peer` | a proposal to remove a cluster that is not a peer |
| `no_electorate` | a proposal no issuer can approve |
| `not_an_elector` | an approval by an issuer with no vote on the proposal |
| `duplicate_approver` | one issuer's approval listed twice |
| `below_quorum` | fewer approvals than a supermajority of the electorate |
The reference implementation also reports `truncated`, `malformed`,
`too_deep`, `not_a_map`, `missing_said_field` and `malformed_said` for
inputs no vector exercises, and its extensions outside spec v1 have classes of
their own. An input with several defects reports the first in stream order;
a key-event log reports the first event refused.
### Completeness
A pack is checked whole, not only vector by vector. Every file holds at least
one vector, every vector the identity spec cites is present, and every error
class in the table above is exercised by some vector — and no vector names a
class the table does not list. Both checkers enforce this, so a pack cannot
pass by losing vectors.
## Versioning rule
Vectors are versioned **per spec version**, not per implementation, not per
calendar release of the conformance pack.
- `v1/` corresponds to spec v1. Once spec v1 is frozen, `v1/` is append-only
in scope (additional vectors may be added that test the *same* canonical
wire format) and never modifies existing vectors' `expected_*` outputs.
- `v2/` will appear when (and only when) the spec moves to a new version
that introduces an incompatible wire-format change. The compatibility
matrix lives in `VERSIONS.md`.
- In a frozen version, a bug in a vector pack (e.g. an `expected_*` field
that disagrees with the spec) is fixed by superseding the vector with a
new one whose `name` carries a suffix (`-fixed`, `-corrected`); the
original is retained with a `"superseded_by"` field so historical IUTs
that pinned to the old vector can detect the supersession. A DRAFT pack is
regenerated in place instead, and every change is recorded in its
`regeneration.md`.
## CI gate
`../../.github/workflows/conformance.yml` is the repository-level CI gate.
Every PR that touches `smart-byte/spec/` or `smart-byte/conformance/` must
pass the gate. The gate validates:
- Every JSON file in `v*/` parses as valid JSON.
- The second implementation, `crosscheck/verify_v1.py`, agrees with every
vector, and finds the pack complete (see Completeness).
- Every spec change is accompanied by a vector-pack update (a PR that
modifies `smart-byte/spec/` without modifying `smart-byte/conformance/`
is rejected at the gate).
Separately, `standards-build` runs `cargo test` in `smart-byte-rs` on every
push. That checks every vector against the reference implementation, requires
the published files to equal the generator's output byte for byte, and
requires identity spec §2.4 to print the worked example's real values.
The gate's purpose is procedural: a spec change without a vector change is
either a clarifying edit (which should declare itself by also touching the
vector pack's `regeneration.md` to record the editorial reason) or a
silent wire-format change (which is prohibited). Either way, both
directories move together.
## What the pack does not cover
The pack is the wire-format gate. It does not cover:
- Application-layer cargo schemas. Cargo is opaque to the substrate; what
a USD-byte or a joule-byte or a sensor-reading-byte *means* is a
layer-2 protocol concern. Layer-2 protocols ship their own vectors.
- Performance. The pack tests correctness, not throughput, latency, or
energy cost. Performance is documented in the reference implementation's
benchmarks.
- Operational concerns. Cluster topology choice, key custody, hardware
procurement, regulatory compliance for specific cargo types — all out
of scope for the substrate's conformance pack.
- Witnessing, delegation and recovery. Spec v1 defines none of them, and a
v1 verifier refuses events that use them; the reference implementation
tests its extensions for them in its own suite.
- Cluster membership: how nodes join and leave a cluster, and how a cluster
splits or merges (treatise §11.2, §11.3.2). The state machine starts where
membership is settled: a cluster's own nodes are outside its state, and
its peers' nodes are in it.
## License
The vector pack is licensed CC-BY-4.0. See `../../LICENSE` for the
repository-level license terms.