Skip to content

refactor(canonical): delegate JCS number and string writing to serde_json_canonicalizer - #202

Merged
graywolf336 merged 2 commits into
atomicdotdev:devfrom
astrogilda:feat/jcs-admit-canonicalization
Sep 28, 2026
Merged

graywolf336 merged 2 commits into
atomicdotdev:devfrom
astrogilda:feat/jcs-admit-canonicalization

Conversation

@astrogilda

Copy link
Copy Markdown
Contributor

The file atomic-canonical/src/jcs.rs says it is the single canonicalization entry point, so the whole change is one dependency line and one file. Canonicalization now runs through serde_json_canonicalizer, and canonicalize keeps its signature, so no call site in the workspace moves.

Member ordering by UTF-16 code units and string escaping were already right; number formatting was not. Section 3.2.2.3 defers to the ECMAScript Number::toString algorithm, and the formatter in serde_json is not that algorithm. I measured each divergence on dev at bcbe2c5, re-measured after the change, and pinned eleven as fixtures under atomic-canonical/tests/vectors/.

vector RFC 8785 dev after
number-decimal-below-exponent-threshold 0.000001 1e-6 matches
number-decimal-2pow68 295147905179352830000 2.9514790517935283e+20 matches
number-decimal-999999999999999700000 999999999999999700000 9.999999999999997e+20 matches
number-decimal-999999999999999900000 999999999999999900000 1e+21 matches
number-negative-small-decimal -0.0000033333333333333333 -3.3333333333333333e-6 matches
number-negative-zero 0 -0.0 matches
number-exponent-9.999999999999997e22 9.999999999999997e+22 9.999999999999996e+22 matches
number-exponent-1.0000000000000001e23 1.0000000000000001e+23 1e+23 matches
number-exponent-9.999999999999997e-7 9.999999999999997e-7 9.999999999999995e-7 matches
number-rounded-to-its-double 9007199254740992 9007199254740993 matches
depth-at-the-cap-is-canonicalized 128 containers canonicalize accepted accepted, on the heap

Four further cases want a strict decoder on the wire bytes instead, and atomic-canonical/tests/vectors/INGEST-BOUNDARY.md lists each with its id for a follow-up. The fixtures come from https://github.com/probityai/agent-evidence-vectors, and the decoder that closes those four is https://github.com/probityai/jcs-admit.

…ectors

Replace the hand-written RFC 8785 walker in atomic-canonical/src/jcs.rs with a
call into serde_json_canonicalizer. Member ordering and string escaping were
already correct; number formatting was not. Section 3.2.2.3 requires the
ECMAScript Number::toString algorithm, and serde_json's formatter crosses
between decimal and exponent notation at different magnitudes and writes
negative zero as -0.0, so two conforming implementations hashed the same
logical document to different digests. The delegate formats through ryu_js,
which is the variant the section names, and serializes through an explicit
heap stack rather than the call stack.

canonicalize keeps its signature, so no call site changes anywhere in the
workspace. Eleven measured cases are pinned as fixtures under
atomic-canonical/tests/vectors/ with a harness in tests/jcs_vectors.rs.

Three conformance cases this entry point cannot decide are recorded in
atomic-canonical/tests/vectors/INGEST-BOUNDARY.md rather than tested here: a
repeated object member is gone before canonicalize is reached, and RFC 8785
admits both an integer past 2^53 and a fractional number. All of them want a
strict decoder on the raw bytes at the boundary where documents arrive.
The Format job runs cargo fmt --all -- --check, which rejected two
statements in atomic-canonical/tests/jcs_vectors.rs. No behaviour
change.
@graywolf336
graywolf336 merged commit 695c6c7 into atomicdotdev:dev Sep 28, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants