Skip to content

feat(payments): add CEP-8 canonical invocation identity#103

Merged
ContextVM-org merged 1 commit into
ContextVM:mainfrom
harsh04044:feat/cep8-canonical-identity
Jul 21, 2026
Merged

feat(payments): add CEP-8 canonical invocation identity#103
ContextVM-org merged 1 commit into
ContextVM:mainfrom
harsh04044:feat/cep8-canonical-identity

Conversation

@harsh04044

Copy link
Copy Markdown

Part of #100

Second piece of CEP-8, after the payment primitives in #102. This adds the canonical invocation identity: the hash that lets a paid authorization match a later retry of the same call.

The hash is SHA-256 over the RFC 8785 (JCS) canonicalization of { method, params }, with the top-level params._meta stripped. _meta is per-request MCP metadata (progressToken, stream) that the client regenerates on every call, so it must not affect identity. Same algorithm as the ts-sdk's computeCanonicalInvocationHash.

Still pure: no store, no middleware, no transport wiring. Just the two functions, the identity struct, and the error variant.

What's here

  • compute_canonical_invocation_hash / _identity and CanonicalInvocationIdentity in src/payments/canonical.rs
  • PaymentError::Canonicalize
  • tools/gen-canonical-vectors/, a bun script that generates the golden fixture, plus the committed tests/fixtures/canonical_vectors.json (34 vectors)

Note:

  • Why pin json-canon to exactly =0.1.3? Its output bytes gate cross-SDK grant matching, so a silent minor bump could break interop. It also has an i64::MIN bug (its bounds check does .abs(), which overflows), so canonical.rs runs its own safe-range pre-scan first and rejects any integer with |n| > 2^53-1 as PaymentError::Canonicalize before it reaches the engine. Hostile input fails closed instead of computing a hash a JS peer won't agree with.
  • Where's the fixture from? The generator imports the ts-sdk's real computeCanonicalInvocationHash (not a copy, so it can't drift) against pinned canonicalize@2.1.0 + @noble/hashes@2.2.0. A golden test asserts our Rust output matches every vector byte-for-byte. It runs under bun because it imports a .ts file; the generator is offline only, and the crate and CI just read the committed JSON.
  • No FFI change? The existing Error::Payment(_) wildcard arm already absorbs the new error variant, so the E0004 gate stays green and the C tests are untouched.

Heads up
Adding json-canon turns on serde_json's float_roundtrip feature crate-wide (Cargo feature unification). It's strictly more precise and is what float parity needs, it doesn't change integer or string parsing.

@ContextVM-org
ContextVM-org merged commit c3ffbda into ContextVM:main Jul 21, 2026
9 of 10 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