open-standards/smart-byte/conformance
Repository files (latest commit first)
Filename Latest commit message Latest commit date
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
..
crosscheck
v1
README.md
VERSIONS.md

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:

{
  "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.