| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
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-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]> |
||
| .. | ||
| 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):
- Load the JSON file for the relevant spec version.
- For each vector, run the IUT against the
input(or equivalent field) and compare its output to theexpected_*field. - 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 documentederrorclass). - 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 hasinputandexpected_cbor_hex; a negative one hasinput_cbor_hex, which a strict decoder must refuse witherror. Refusing means not decoding: an IUT must never re-encode non-canonical input into canonical form and accept it.said_vectors.json—said_fieldnames the member that carries the SAID (d). A positive vector'sinputholds the 53-#placeholder there.canonical_cbor_hexis the canonical encoding ofinput(the bytes that are hashed),blake3_hexis their BLAKE3-256,expected_saidis that digest writtenE+ lowercase unpadded base32, andsaid_cbor_hexis the object with its SAID in place, which must verify. A negative vector'sinput_cbor_hexmust fail SAID verification witherror. 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 ismessage_hex, the canonical encoding of what is signed: sig-001's is an issuer's attestation, sig-002's a key event.verifying_keyis the key handed to the verifier; whensigning_key_seedis not null, it is the key that seed derives. Verifysignatureunderverifying_key: the result must beexpected_verify, failing witherrorwhen false. A positive vector'ssignatureis the one deterministic signature its seed makes.kel_vectors.json—eventsis 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 itssignatures: each a keyindexand asignature— the key's algorithm identifier,01for Ed25519, followed by the signature over the event's bytes (identity §3.5). A signature with aseedis the one that seed's key makes. Replay the log as a v1 verifier (identity §4). An accepted log givesexpected: the controller AID, each event's SAID, the key state each establishment event put in force (sequence,keysin hex,threshold), and the next-key commitment in force at the end. A refused log gives theerrorclass anderror_at, the position of the event refused; no later event is considered.envelope_vectors.json—cbor_hexis an envelope, also given as typed JSON (envelope) when the bytes are canonical CBOR.key_event_logsnames accepted vectors ofkel_vectors.json; signatures are checked against the key states their replay establishes. Decode and verify the envelope (envelope wire format §8). An accepted envelope givesexpected: its identity, snapshot SAID, issuer, current owner, history hash (hex) and number of transfers. A refused one gives theerrorclass.state_vectors.json—genesisis a genesis state: as typed JSON (state), as bytes (cbor_hex), and itsstate_hash. Apply each offramesin order, as the cluster state spec (§4) describes:inputsare the frame's inputs as submitted, each as bytes (cbor_hex) and, when they are canonical CBOR, typed JSON (input).expected_resultsgives one result per distinct input, in the order they apply: the input's BLAKE3 digest, theclassit names (null if none), andoutcome—applied, ordroppedwith theerrorclass. Then the state must beexpected_state_cbor_hex, also given as typed JSON, with hashexpected_state_hash.lockstep_vectors.json—node_countis the cluster size n.node_reported_hashesare the opaque 32-byte state hashes each node broadcast, andnode_computed_hashesthe 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 computesexpected_threshold(floor(2n/3)+1), thenexpected_commitandexpected_state_hashfrom the reported hashes — the frame's public record. It also computes each node'sexpected_outcomes(clean_commit,commit_with_dissent,minority_divergenceorcluster_split) andexpected_dissent_logsfrom that node's own view: its peers' reported hashes plus the hash it computed.gossip_vectors.json—deliveriesarrive atreceiverin order, each with the SAID it was announced or requested under (claimed_said) and its bytes (cbor_hex). Each result isacceptedorduplicatewith the SAID, orrejectedwith an error class, per identity spec §2.6.expected_heldlists 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 inVERSIONS.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 whosenamecarries 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 itsregeneration.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 modifyingsmart-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.