Skip to content

feat: strong-type PrivacyPreservingCircuitInput with per-account enum#462

Open
moudyellaz wants to merge 5 commits intomainfrom
moudy/feat-strong-type-circuit-input
Open

feat: strong-type PrivacyPreservingCircuitInput with per-account enum#462
moudyellaz wants to merge 5 commits intomainfrom
moudy/feat-strong-type-circuit-input

Conversation

@moudyellaz
Copy link
Copy Markdown
Collaborator

@moudyellaz moudyellaz commented Apr 28, 2026

🎯 Purpose

Refactor PrivacyPreservingCircuitInput from four parallel vectors (visibility_mask, private_account_keys, private_account_nsks, private_account_membership_proofs) into a single Vec<InputAccountIdentity> enum, eliminating implicit lock-step iteration and runtime length-coupling assertions in the privacy circuit guest. Closes #455.

⚙️ Approach

  • Add InputAccountIdentity enum in nssa/core/src/circuit_io.rs with six variants: Public, PrivateAuthorizedInit, PrivateAuthorizedUpdate, PrivateUnauthorized, PrivatePdaInit, PrivatePdaUpdate.
  • Replace parallel-vec fields in PrivacyPreservingCircuitInput with a single account_identities: Vec<...> field aligned 1:1 with the program's pre_states.
  • Rewrite compute_circuit_output and derive_from_outputs in the privacy circuit guest to consume account_identities directly, dropping the four lock-step iterators and trailing length asserts.
  • Rewrite execute_and_prove host signature to take account_identities.
  • Migrate ~50 call sites in nssa/src/state.rs, nssa/src/privacy_preserving_transaction/circuit.rs, sequencer test, and integration test.
  • Refactor AccountManager (wallet/src/privacy_preserving_tx.rs) to expose account_identities() directly, eliminating the parallel-vec accessors and the wallet-side iterator-flatten that would have replicated the same anti-pattern at the wallet/circuit boundary.
  • Rebake privacy_preserving_circuit.bin.

🧪 How to Test

RISC0_DEV_MODE=1 cargo test --release on nssa-core and nssa.

🔗 Dependencies

None.

🔜 Future Work

The "authorized init of a private PDA" sub-case (an authorized PDA pre-state that is still default) is structurally allowed by the guest's private-PDA paths today but is not produced by any wallet code. If a future PR enables it via (seed, owner) side input for top-level reuse, the variant set will need to be revisited.

🗑️ Removed tests

Seven tests have been removed because each one exercised a parallel-vec length mismatch that the new Vec<InputAccountIdentity> makes structurally impossible:

  • circuit_fails_if_insufficient_nonces_are_provided
  • circuit_fails_if_insufficient_keys_are_provided
  • circuit_fails_if_insufficient_commitment_proofs_are_provided
  • circuit_fails_if_insufficient_auth_keys_are_provided
  • circuit_should_fail_with_too_many_nonces
  • circuit_should_fail_with_too_many_private_account_keys
  • circuit_should_fail_with_too_many_private_account_auth_keys

Each variant of InputAccountIdentity carries exactly the fields the corresponding circuit code path needs, so over-supply or under-supply of any per-account field is a compile-time error rather than a runtime panic.

📋 PR Completion Checklist

  • Complete PR description
  • Implement the core functionality
  • Add/update tests
  • Add/update documentation and inline comments

@moudyellaz moudyellaz force-pushed the moudy/feat-strong-type-circuit-input branch 3 times, most recently from 1bd9455 to f734965 Compare April 29, 2026 06:52
@moudyellaz moudyellaz marked this pull request as ready for review April 29, 2026 07:28
Copy link
Copy Markdown
Collaborator

@Pravdyvy Pravdyvy left a comment

Choose a reason for hiding this comment

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

Thank you for PR. Left one suggestion.

Comment thread nssa/core/src/circuit_io.rs Outdated
Comment on lines +24 to +27
/// Per-account input to the privacy-preserving circuit. Each variant carries exactly the fields
/// the guest needs for that account's code path.
#[derive(Serialize, Deserialize, Clone)]
pub enum PrivacyPreservingCircuitInputAccount {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this naming is ambiguous on the size of variable. We have now 2 accounts types, one holds keys, other holds data and balance. I propose to rename this enum into something like PrivacyPreservingCircuitInputAccountIdentity, for example PrivacyPreservingCircuitInputAccountIdentity and name variables of this enum account_identity.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Agree. Also I think we can drop the PrivacyPreservingCircuit prefix as it already is defined inside circuit_io

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done, renamed the enum to InputAccountIdentity, variant-of-this-enum variables to account_identity, and the struct field to account_identities.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done, dropped the PrivacyPreservingCircuit prefix. The type is now InputAccountIdentity, re-exported from nssa_core::lib.rs.

Copy link
Copy Markdown
Collaborator

@schouhy schouhy left a comment

Choose a reason for hiding this comment

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

Thanks for the PR. This is a major improvement for readability of the code. Left minor comments

Comment thread nssa/core/src/circuit_io.rs Outdated
/// Public account. The guest reads pre/post state from `program_outputs` and emits no
/// commitment, ciphertext, or nullifier.
Public,
/// Init of an authorized standalone private account (mask 1, no membership proof). The
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'd remove all mentions to mask as that's now legacy and would be confusing for someone without context of previous implementations

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done.

Comment thread nssa/src/state.rs Outdated
Comment on lines +1876 to +1879
// The four `circuit_fails_if_insufficient_*_are_provided` tests have been removed in this
// refactor: each variant of `PrivacyPreservingCircuitInputAccount` carries exactly the fields
// the corresponding circuit code path needs, so the parallel-vec length mismatches those
// tests exercised are now compile-time impossibilities.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

agreed with removing the tests. But let's not add this comment here. Let's put it in the PR description instead

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done, removed the in-code comments and added a Removed tests section to the PR description.

- Rename PrivacyPreservingCircuitInputAccount to InputAccountIdentity (drop the PrivacyPreservingCircuit prefix; add Identity suffix)
- Rename PrivacyPreservingCircuitInput.accounts to account_identities
- Rename AccountManager.accounts() to account_identities() and loop variables to account_identity
- Drop legacy mask-1/2/3 references from variant doc comments and guest comments
- Remove the explanatory comments about deleted parallel-vec tests; moved to the PR description
- Rebake privacy_preserving_circuit and test program artifacts
@moudyellaz moudyellaz requested a review from Pravdyvy April 30, 2026 22:01
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.

Strong-type PrivacyPreservingCircuitInput with a per-account enum

3 participants