Skip to content

Confidential Token: specify the client SDK layer - #822

Merged
brozorec merged 17 commits into
mainfrom
worktree-confidential-sdk-spec
Jul 31, 2026
Merged

Confidential Token: specify the client SDK layer#822
brozorec merged 17 commits into
mainfrom
worktree-confidential-sdk-spec

Conversation

@brozorec

@brozorec brozorec commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Adds packages/tokens/src/confidential/docs/SDK.md, filling the SDK (to be added) placeholder in DESIGN.md's Project Documents register, and — in follow-up commits — resolves the protocol questions drafting it surfaced instead of leaving them recorded as open.

SDK.md

The client layer is load-bearing: commitment openings (v, r) exist only off-chain, so a client that loses or mis-derives one makes funds permanently unspendable while the commitment sits visibly on-chain. SDK.md specifies that layer normatively and discharges the requirements previously stranded as obligations on "wallets and SDKs" in documents that don't own the client.

Its main addition is §5, a derivation for sk, which no document specified. The root is keyed to what controls the address — a SEP-0053 signature by the account's ed25519 signer, or raw 32 bytes from the controlling custody mechanism for contract addresses — fed through rejection-sampled HKDF-SHA-512 bound to the deployment (addr_f) and the account (acct_f). Binding acct_f is a privacy MUST: the same sk registered under two addresses publishes identical Y and PVK under both, linking otherwise-unlinkable addresses.

Protocol decisions folded back into the owning documents

  • r_e is derived, not sampled. DESIGN.md §7.6 said sample it; SELECTIVE_DISCLOSURE.md said derive it — and a client built to §7.6 alone permanently forecloses sender-side disclosure. DESIGN.md now specifies r_e = Poseidon(δ_eph, vk, σ) protocol-wide, and §9.4 carries the consequence: a vk holder can reconstruct the openings of every transfer the account originated.
  • DESIGN.md §2.5 is now normative for the Poseidon2 sponge — construction, padding, the single-permutation two-lane squeeze, and one sponge mode per domain tag. Previously the only authority was the circuit source, and an implementer reaching for a general-purpose Poseidon2 hash fails every proof.
  • The receiving-side recovery window is anchored at the last Merge rather than the last checkpoint; the previous anchor reconstructed a zero balance whenever a spend followed an inbound transfer with no intervening merge.
  • Smaller corrections across the set: the per-circuit scalar-multiplication counts in DESIGN_cont.md §10.3 (every total was one low), typed read-method returns and the get_spender_delegation rename, pinned δ-tag values, and a batch of stale cross-references and contradictions.

Code

No longer docs-only, though nothing behavioral changed: stale _x suffixes are removed from circuit and contract identifiers, an address_to_field.json conformance vector is added, and a Rust test pins the contract's address_to_field against it — the one primitive implemented independently on-chain and in every client, previously with no vector holding the two together.

Verification

All 13 sponge-derived vectors in circuits/lib/testdata/ were reproduced from the docs' prose alone, using only a raw permutation and no code from the reference implementation. Every cross-document §N reference and relative link resolves (checked programmatically). Numeric claims were recomputed rather than asserted.

No issue number — this completes an existing documented placeholder rather than introducing a feature.

PR Checklist

  • Documentation
  • Tests — address_to_field parity vectors

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Added a comprehensive Confidential Token SDK specification.
    • Clarified compliance, wallet recovery, indexing, selective disclosure, delegation, and auditor behavior.
    • Added guidance and test vectors for address encoding.
  • Bug Fixes

    • Standardized shared-secret handling across confidential transfer, withdrawal, and spender operations without changing cryptographic behavior.
  • Tests

    • Added address-encoding parity coverage and updated confidential test vectors.

Fills the `SDK (to be added)` placeholder in the design doc's register. The
client is load-bearing here — commitment openings exist only off-chain, so a
client that mis-derives one makes funds permanently unspendable — but three
requirements were stranded as obligations on "wallets and SDKs" in documents
that do not own the client layer, and the seed-to-`sk` derivation the recovery
guarantee depends on was never specified at all.

Also records, in §16, six places where the protocol docs are silent, mutually
inconsistent, or behind the circuits, each with the assumption this document
makes in the meantime.
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ea202e43-b705-4316-bb09-67884e0566fd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

The pull request standardizes ECDH shared-secret scalar names across confidential circuits and tests, adds address-to-field parity vectors, and expands or corrects SDK, design, recovery, compliance, indexer, and selective-disclosure documentation.

Changes

Circuit and test alignment

Layer / File(s) Summary
ECDH scalar naming alignment
packages/tokens/src/confidential/circuits/**
Renames shared-secret variables and parameters from _x forms to scalar names. Calculations, assertions, and outputs remain unchanged.
Address-to-field parity coverage
packages/tokens/src/confidential/storage.rs, packages/tokens/src/confidential/test.rs, packages/tokens/src/confidential/circuits/lib/testdata/*
Makes address_to_field crate-visible and adds account and contract test vectors with Rust parity coverage.

Documentation specification updates

Layer / File(s) Summary
Core SDK and design specification
packages/tokens/src/confidential/README.md, packages/tokens/src/confidential/docs/DESIGN.md, packages/tokens/src/confidential/docs/SDK.md
Adds the SDK specification and updates sponge behavior, scalar derivation, wallet recovery, delegation, and circuit design details.
Interfaces, recovery, and disclosure documentation
packages/tokens/src/confidential/docs/COMPLIANCE.md, DESIGN_cont.md, INDEXER.md, OVERVIEW.md, SELECTIVE_DISCLOSURE.md
Updates typed interfaces, checkpoint-based recovery, auditor capabilities, compliance details, and scalar ECDH notation.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: documentation

Suggested reviewers: ozgunozerk

Poem

I hop through circuits, neat and bright,
Naming shared secrets just right.
Vectors guard each field and key,
Docs unfold recovery.
SDK pages bloom in view—
A tidy burrow, fresh and new!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the primary change: specifying the Confidential Token client SDK layer.
Description check ✅ Passed The description clearly explains the SDK specification, protocol decisions, code changes, and verification, with documentation and tests marked complete.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-confidential-sdk-spec

Comment @coderabbitai help to get the list of available commands.

brozorec added 5 commits July 28, 2026 16:43
Removes amplification, second-register restatements, rhetorical framing, and
paragraph-closing punchlines throughout, and repairs the three inbound §16
references left stale by that section's renumbering.
…_field vector

Distinct domain tags are not sufficient: SpongeSqueeze_2(d, s, sigma)[0] equals
poseidon_with_domain(d, [s, sigma]), so a tag used in both modes over the same
inputs reproduces the other mode's pad. address_to_field is the only primitive
implemented twice, on-chain and per client, and had no conformance vector.
Specifies the sponge construction in §2.5, replaces the n-squeeze phrasing
with the two-lane form, and requires each domain tag to be confined to a
single sponge mode. Also types the read methods' return values and renames
`get_spender` to `get_spender_delegation`.
Replaces the prohibition on the SEP-0005 signing key with two root classes —
the BIP-39 seed and a SEP-0053 signature by a signer on the account — keyed to
custody shape, with the tradeoff comparison in §5.2. Also trims §4 to defer to
DESIGN.md now that the sponge construction is normative there.
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

brozorec added 5 commits July 29, 2026 11:45
Uses RFC 5869's `IKM` throughout §5, pins the exact byte string each root
class supplies (BIP-39 seed output, RFC 8032 `R ‖ S` signature, raw 32 bytes),
and states that it is passed verbatim rather than hashed or re-encoded.
An ed25519-controlled address now derives sk from a SEP-0053 signature by its
signer, and an address with no ed25519 key of its own from raw bytes supplied
by whatever custody mechanism controls it. A seed root added no reach a signer
root lacks and made an irreversible per-client commitment out of a class the
address should determine.
Collapse restated capability, griefing and expiry text into cross-references.
Fix stale names and pointers: get_spender_delegation, a_tilde, the SEP-23
version bytes, the checkpoint event list, delta_disc_bind's role, and the
scope of the drawn-scalar enumeration.
@brozorec
brozorec force-pushed the worktree-confidential-sdk-spec branch from 06257a1 to 9bff6ed Compare July 30, 2026 14:50
brozorec added 4 commits July 30, 2026 17:32
…st merge

Only Merge resets C_receive, so anchoring recovery's receiving side at the
latest checkpoint reconstructed a zero balance whenever a spend followed an
inbound transfer with no merge between them. The window now runs from the last
Merge at or before the checkpoint, and 9.5 carries the resulting security
properties and the two-anchor soundness argument instead of restating the
procedure.
Routes the disclosure ciphertext's shared secret through the two-coordinate
ECDH funnel, which #778 left behind. Corrects the delta_addr in-circuit claim,
the address_to_field fixture status, SetSpender/RevokeSpender lane usage,
confidential_balance's return type, D-balance liveness, the ComplianceConfig
field count, and the compliance constructor's relation to the core interface.
Every total in §10.3 was one low: SetSpender omitted the S12 escrow ECDH,
and the other four dropped their output commitment. The "base + 2 for
auditor ECDH" phrasing left three bases unstated, so the counts could not
be audited against the circuit sources or constraints.baseline.
@brozorec brozorec self-assigned this Jul 31, 2026
@brozorec
brozorec marked this pull request as ready for review July 31, 2026 09:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/tokens/src/confidential/circuits/set_spender/src/tests.nr`:
- Line 975: Update the zero-r_e explanation in the surrounding test comments to
state that only the shared point becomes the identity, while ecdh(0, k_aud_s)
produces the ECDH-domain hash of that identity point rather than literal zero;
describe s_a_s_zero and s_esc_zero as identity-derived scalars.

In `@packages/tokens/src/confidential/circuits/spender_transfer/src/tests.nr`:
- Around line 1025-1026: Update both zero-r_e comments to use scalar names
instead of the obsolete coordinate notation: in
packages/tokens/src/confidential/circuits/spender_transfer/src/tests.nr lines
1025-1026 and packages/tokens/src/confidential/circuits/transfer/src/tests.nr
lines 849-850, replace s_a_{r,s}.x = 0 with s_a_r_zero = 0 and s_a_s_zero = 0.

In `@packages/tokens/src/confidential/docs/DESIGN_cont.md`:
- Around line 512-514: Update the Provenance text to replace the
collision-resistance justification with the required domain-separation or
pseudorandom-independence assumption for distinct Poseidon2 tags. Retain the
normative requirements that all tags are distinct and each is confined to a
single sponge mode, while preserving the existing positional-assignment
explanation.

In `@packages/tokens/src/confidential/docs/DESIGN.md`:
- Around line 371-383: Define self-transfers as applying both roles during
recovery: in packages/tokens/src/confidential/docs/DESIGN.md lines 371-383,
specify the application order so the event’s sender checkpoint initializes
W_spend before its recipient credit updates W_receive; update
packages/tokens/src/confidential/docs/SDK.md lines 377-388 with the same
normative dual-role rule; and classify self-transfers as both sender-side
checkpoints and recipient-side replay events in
packages/tokens/src/confidential/docs/INDEXER.md lines 43-54.

In `@packages/tokens/src/confidential/docs/INDEXER.md`:
- Around line 12-23: The replay window terminology around T_0 currently implies
that the anchor event is replayed, risking double application after resetting
W_receive. Update the T_0 definition and related recovery wording to state that
T_0 is an exclusive reset anchor: initialize from its state and apply only
events strictly after T_0 through the current ledger, while preserving the
registration fallback.

In `@packages/tokens/src/confidential/docs/SDK.md`:
- Around line 25-26: Update the salt-security statement in SDK.md to classify
salt uniqueness as a confidentiality and unlinkability requirement, describing
that reusing σ repeats the deterministic ephemeral scalar and channel masks
rather than enabling circuit-constraint violations. Ensure the classification
guides conformance tests toward the actual failure mode.

In `@packages/tokens/src/confidential/docs/SELECTIVE_DISCLOSURE.md`:
- Around line 487-497: Qualify the recipient-only decryption claim in §13.2:
update the statement around s_disc = ECDH(r_disc, P_R) to acknowledge that the
prover, who samples r_disc, can compute the shared secret, while only the
intended recipient can decrypt after the prover releases the ciphertext.
- Line 348: Resolve the conflicting opening-storage requirements by choosing and
stating one normative rule for whether the wallet may reconstruct the latest
opening from a checkpoint. Update the requirement in section 15.2 and the SDK
requirements to match that rule, while preserving the existing latest-opening
behavior where required.

In `@packages/tokens/src/confidential/README.md`:
- Around line 37-38: Update the documentation tree in the README so OVERVIEW.md
and INDEXER.md use ├── markers, while SDK.md remains the sole final child using
└──.

In `@packages/tokens/src/confidential/test.rs`:
- Around line 868-870: Update the test loop around
token_storage::address_to_field to invoke the internal helper through
e.as_contract using a generated context address, while continuing to pass the
fixed strkey-derived Address as the vector input and preserving the existing
assertion.
- Around line 874-882: Update the expect calls in hex32 so their messages
explain that the committed test vector is guaranteed to contain valid
hexadecimal digits, rather than only stating the expected value. Apply this to
both high- and low-nibble to_digit conversions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e0bff243-8a11-4044-9be2-ebf7c437356d

📥 Commits

Reviewing files that changed from the base of the PR and between 56d6e5b and 778f53f.

📒 Files selected for processing (27)
  • packages/tokens/src/confidential/README.md
  • packages/tokens/src/confidential/circuits/gadgets/sponge_squeeze_2/src/main.nr
  • packages/tokens/src/confidential/circuits/lib/src/lib.nr
  • packages/tokens/src/confidential/circuits/lib/src/tests.nr
  • packages/tokens/src/confidential/circuits/lib/testdata/README.md
  • packages/tokens/src/confidential/circuits/lib/testdata/address_to_field.json
  • packages/tokens/src/confidential/circuits/lib/testdata/encrypt_auditor_sender_balance.json
  • packages/tokens/src/confidential/circuits/lib/testdata/sponge_squeeze_2.json
  • packages/tokens/src/confidential/circuits/revoke_spender/src/main.nr
  • packages/tokens/src/confidential/circuits/revoke_spender/src/tests.nr
  • packages/tokens/src/confidential/circuits/set_spender/src/main.nr
  • packages/tokens/src/confidential/circuits/set_spender/src/tests.nr
  • packages/tokens/src/confidential/circuits/spender_transfer/src/main.nr
  • packages/tokens/src/confidential/circuits/spender_transfer/src/tests.nr
  • packages/tokens/src/confidential/circuits/transfer/src/main.nr
  • packages/tokens/src/confidential/circuits/transfer/src/tests.nr
  • packages/tokens/src/confidential/circuits/withdraw/src/main.nr
  • packages/tokens/src/confidential/circuits/withdraw/src/tests.nr
  • packages/tokens/src/confidential/docs/COMPLIANCE.md
  • packages/tokens/src/confidential/docs/DESIGN.md
  • packages/tokens/src/confidential/docs/DESIGN_cont.md
  • packages/tokens/src/confidential/docs/INDEXER.md
  • packages/tokens/src/confidential/docs/OVERVIEW.md
  • packages/tokens/src/confidential/docs/SDK.md
  • packages/tokens/src/confidential/docs/SELECTIVE_DISCLOSURE.md
  • packages/tokens/src/confidential/storage.rs
  • packages/tokens/src/confidential/test.rs

Comment thread packages/tokens/src/confidential/circuits/set_spender/src/tests.nr Outdated
Comment thread packages/tokens/src/confidential/circuits/spender_transfer/src/tests.nr Outdated
Comment thread packages/tokens/src/confidential/docs/DESIGN_cont.md Outdated
Comment thread packages/tokens/src/confidential/docs/DESIGN.md
Comment thread packages/tokens/src/confidential/docs/INDEXER.md
Comment thread packages/tokens/src/confidential/docs/SELECTIVE_DISCLOSURE.md
Comment thread packages/tokens/src/confidential/docs/SELECTIVE_DISCLOSURE.md
Comment thread packages/tokens/src/confidential/README.md Outdated
Comment thread packages/tokens/src/confidential/test.rs
Comment thread packages/tokens/src/confidential/test.rs
@brozorec
brozorec requested a review from ozgunozerk July 31, 2026 10:09
Specify the dual-role application of self-transfers in recovery, cite the
PRF assumption for domain-tag independence, reclassify salt uniqueness as
a confidentiality requirement, and rewrite the zero-r_e test comments
around identity-derived ECDH scalars. Also fixes the README docs tree.
@brozorec brozorec changed the title docs(confidential): specify the client SDK layer Confidential Token: specify the client SDK layer Jul 31, 2026
@brozorec
brozorec merged commit 9b5ed96 into main Jul 31, 2026
9 checks passed
@brozorec
brozorec deleted the worktree-confidential-sdk-spec branch July 31, 2026 13:57
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