Skip to content

Add JCS reference corpus vectors#241

Closed
koriyoshi2041 wants to merge 2 commits into
solana-foundation:mainfrom
koriyoshi2041:rios/jcs-reference-vectors
Closed

Add JCS reference corpus vectors#241
koriyoshi2041 wants to merge 2 commits into
solana-foundation:mainfrom
koriyoshi2041:rios/jcs-reference-vectors

Conversation

@koriyoshi2041

Copy link
Copy Markdown

Summary

  • add a small attributed RFC 8785/JCS reference corpus file under harness/vectors
  • wire the corpus into a focused harness test for canonical JSON and base64url bytes
  • move the TypeScript reference canonicalizer into shared test support so the existing charge vectors and new reference vectors use the same encoder

This is an incremental TypeScript harness starter for #110, not a full cross-SDK import yet.

Test

  • pnpm run typecheck from harness/
  • pnpm exec vitest run test/canonical-json.test.ts test/jcs-rfc8785-reference.test.ts from harness/
  • git diff --check

Note: full harness typecheck in this local checkout required building the local typescript/packages/mpp package first, then refreshing the harness file: dependency install.

@greptile-apps

greptile-apps Bot commented Jul 12, 2026

Copy link
Copy Markdown

Greptile Summary

This PR extracts the inline RFC 8785/JCS canonicalizer into a shared test/support/canonical-json.ts module and wires two attributed reference vectors (from the cyberphone corpus) into a new jcs-rfc8785-reference.test.ts suite, extending the existing canonical-json.test.ts to import from the shared module.

  • Shared canonicalizer: the inline functions from canonical-json.test.ts are moved verbatim into support/canonical-json.ts and re-exported; both test suites now use the same encoder, which is the right direction for cross-SDK conformance harnesses.
  • New vector file: two vectors (arrays, structures) are sourced from cyberphone/json-canonicalization with separate input and canonicalJson source attributions; the base64Url field explicitly notes it is derived by the harness encoder, not from the external corpus.
  • New test file: iterates over the reference vectors and asserts both the canonical JSON string and the base64url encoding, with a runtime shape guard — though the guard is currently called outside an it block (see inline comment).

Confidence Score: 5/5

Safe to merge — the change is additive test infrastructure with no production code modified

All changes are confined to the test harness: extracting shared test helpers and adding two static reference vectors. The canonicalizer logic is moved verbatim without modification, and the new test correctly exercises both the canonical JSON output and the base64url encoding. The one pattern concern (shape guard outside it) has no practical runtime impact given the static JSON vectors.

harness/test/jcs-rfc8785-reference.test.ts — the assertJcsReferenceVectors call placement

Important Files Changed

Filename Overview
harness/test/jcs-rfc8785-reference.test.ts New test file exercising JCS reference vectors; shape guard called outside it block could silently skip all tests on assertion failure
harness/test/support/canonical-json.ts Inline canonicalizer extracted into shared support module; logic is unchanged and correct
harness/test/canonical-json.test.ts Refactored to import helpers from shared support; tests are unchanged and the import is correct
harness/vectors/jcs-rfc8785-reference.json Two attributed JCS reference vectors (arrays, structures) with explicit provenance for both input and output sources; base64Url derivation noted in source field

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["jcs-rfc8785-reference.json\n(reference corpus vectors)"] --> B["jcs-rfc8785-reference.test.ts"]
    C["chargeCanonicalJsonVectors\n(harness/src/contracts)"] --> D["canonical-json.test.ts"]
    E["support/canonical-json.ts\ncanonicalize + base64url"] --> B
    E --> D
    B --> F["assertJcsReferenceVectors()\n called in describe scope"]
    F --> G["for each vector to it()"]
    G --> H["canonicalizeJson(input.value)\nassert canonicalJson\nassert base64Url"]
    D --> I["for each charge vector to it()"]
    I --> H
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["jcs-rfc8785-reference.json\n(reference corpus vectors)"] --> B["jcs-rfc8785-reference.test.ts"]
    C["chargeCanonicalJsonVectors\n(harness/src/contracts)"] --> D["canonical-json.test.ts"]
    E["support/canonical-json.ts\ncanonicalize + base64url"] --> B
    E --> D
    B --> F["assertJcsReferenceVectors()\n called in describe scope"]
    F --> G["for each vector to it()"]
    G --> H["canonicalizeJson(input.value)\nassert canonicalJson\nassert base64Url"]
    D --> I["for each charge vector to it()"]
    I --> H
Loading

Reviews (2): Last reviewed commit: "Clarify JCS vector provenance" | Re-trigger Greptile

Comment on lines +16 to +19
"expect": {
"canonicalJson": "[56,{\"1\":[],\"10\":null,\"d\":true}]",
"base64Url": "WzU2LHsiMSI6W10sIjEwIjpudWxsLCJkIjp0cnVlfV0"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Self-referential base64Url expected values

The cyberphone reference implementation (cyberphone/json-canonicalization) does not produce base64url output — only canonical JSON. The base64Url fields in these vectors therefore cannot have been sourced from the external reference; they must have been precomputed by the same encodeBase64Url function under test. This makes the expect(encodeBase64Url(canonicalJson)).toBe(vector.expect.base64Url) assertion in jcs-rfc8785-reference.test.ts circular: it tests the implementation against its own precomputed output rather than an independent reference. The source field naming only the cyberphone input file makes this non-obvious. Consider removing base64Url from the attributed reference vectors (keeping it only in the charge-specific vectors where it's a first-class project output), or adding an explicit comment clarifying the provenance as "computed from the canonical JSON above".

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +3 to +5
"id": "jcs-cyberphone-arrays",
"source": "cyberphone/json-canonicalization:testdata/input/arrays.json",
"license": "Apache-2.0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 source attribution covers inputs only, not expected outputs

The source field names the cyberphone input files (testdata/input/arrays.json, testdata/input/structures.json). However, the reference repository also ships expected output files (e.g., testdata/output/arrays.json) that serve as the authoritative canonical JSON strings. If expect.canonicalJson was verified against those output files, the output source should be attributed too (e.g., "sourceOutput": "cyberphone/json-canonicalization:testdata/output/arrays.json"). Without this, a future reader cannot tell whether the expected values were independently sourced or derived from the local implementation.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

};

describe("RFC 8785 reference corpus vectors", () => {
for (const vector of vectors as JcsReferenceVector[]) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unsafe cast silently ignores shape mismatches

vectors as JcsReferenceVector[] is an unchecked type cast. TypeScript's inference for imported JSON already infers a structural type, but the cast overrides it entirely — if a vector entry is missing input.value or expect.canonicalJson, the test will fail at runtime with a cryptic error rather than a clear schema mismatch. Consider validating the shape with a Zod schema or at minimum using satisfies to retain structural checking without losing inferred narrowing.

@EfeDurmaz16

Copy link
Copy Markdown
Collaborator

Thanks for putting this together and for adding attributed RFC 8785 vectors. We already have the same area in progress in #219, where JCS vectors are wired into the shared cross-SDK conformance pipeline rather than a TypeScript-only test path, so I'm going to close this PR to avoid parallel implementations and merge conflicts. We appreciate the contribution and the reference corpus work.

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