logos-execution-zone/lez/wallet/src/account_manager.rs

785 lines
27 KiB
Rust
Raw Normal View History

2026-06-03 14:51:15 +03:00
use core::fmt;
2025-12-16 14:05:34 +02:00
use anyhow::Result;
refactor(keycard_wallet): replace keycard-py with keycard-rs (#595) * refactor(keycard_wallet): replace keycard-py (pyo3) with native keycard-rs Rewrites the Keycard integration to talk to the LEE-flavored applet directly from Rust via keycard-rs (pinned git dependency), dropping the pyo3 shim, python_path.rs, and the vendored keycard-py Python library entirely. Verified end-to-end against real hardware: pairing, PIN verification, mnemonic loading, BIP340 Schnorr signing, and transfers between keycard and public/private accounts. Also cleans up the pyo3 usage that had leaked into wallet's signing path (signing.rs, account_manager.rs) beyond the keycard CLI itself, and trims wallet_with_keycard.sh's Python setup down to just pyscard, which is only needed by the force_unpower.py test helper now. * Updated to v2 secure communication * ci: install libpcsclite-dev for pcsc crate builds * docs(keycard): clarify personalization is mandatory and document default CA Reinstalling the applet via `./gradlew install` wipes any existing personalization regardless of firmware source, which wasn't previously called out and is a common source of "card not available" confusion. Also state plainly that personalization is required before any command works, and document keycard-rs's actual baked-in default CA public key instead of only describing the override mechanism. Export KEYCARD_CA_PUBLIC_KEY in all keycard test scripts so they work against the dev/test-CA personalization flow they rely on. * fix(keycard): address PR #595 review comments - Propagate the load_mnemonic error instead of swallowing it behind is_ok(), matching every other command handler in this file. - Add deny.toml allowing the keycard-rs git source, fixing the source-not-allowed failure in `cargo deny check`. Licenses and advisories checks still have pre-existing, separate failures not addressed here. - Drop the pinned rev for the keycard-rs git dependency so it tracks the upstream default branch instead. * chore(deny): trim deny.toml to essentials and allow all in-use licenses Replace the full cargo-deny init template with just the two sections that matter: the keycard-rs git-source allowance, and an explicit license allow-list covering every license currently in the dependency tree. cargo deny check now passes on sources, bans, and licenses; advisories still fails on a pre-existing, unrelated RUSTSEC advisory. * fix(deny): remove second config and add source to the original one * chore: pin keycard-rs dependency and fix factory-reset debug-gate doc * chore: regenerate test fixture and lockfile after rebase onto dev --------- Co-authored-by: Daniil Polyakov <arjentix@gmail.com>
2026-07-23 15:50:30 -04:00
use keycard_wallet::KeycardWallet;
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
use lee::{AccountId, PrivateKey, PublicKey, Signature};
use lee_core::{
Commitment, CommitmentSetDigest, DummyInput, Identifier, InputAccountIdentity, MembershipProof,
NullifierPublicKey, NullifierSecretKey, PrivateAccountKind, SharedSecretKey,
account::{Account, AccountWithMetadata, Nonce},
compute_digest_for_path,
encryption::{
Ciphertext, EncryptedAccountData, MlKem768EncapsulationKey, ViewTag, ViewingPublicKey,
},
};
2026-06-19 22:37:36 +04:00
use rand::{RngCore as _, rngs::OsRng};
use crate::{ExecutionFailureKind, WalletCore};
2026-06-03 14:51:15 +03:00
#[derive(Clone, PartialEq, Eq)]
2026-05-20 18:55:39 +03:00
pub enum AccountIdentity {
Public(AccountId),
2026-05-19 17:54:25 +03:00
/// A public account without signing. Would not try to sign, even if account is owned.
PublicNoSign(AccountId),
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
/// A public account from keycard. Mandatory signing.
PublicKeycard {
account_id: AccountId,
key_path: String,
},
PrivateOwned(AccountId),
PrivateForeign {
npk: NullifierPublicKey,
2026-01-21 17:27:23 -05:00
vpk: ViewingPublicKey,
identifier: Identifier,
},
2026-05-07 13:21:05 -03:00
/// An owned private PDA: wallet holds the nsk/npk; `account_id` was derived via
2026-05-08 21:41:48 -03:00
/// [`AccountId::for_private_pda`].
PrivatePdaOwned(AccountId),
/// A foreign private PDA: wallet knows the recipient's npk/vpk but not their nsk.
2026-05-08 21:41:48 -03:00
/// Uses a default (uninitialised) account.
PrivatePdaForeign {
account_id: AccountId,
npk: NullifierPublicKey,
vpk: ViewingPublicKey,
identifier: Identifier,
},
/// A shared regular private account with externally-provided keys (e.g. from GMS).
2026-05-07 22:48:32 +02:00
/// Uses standard `AccountId = from((&npk, identifier))` with authorized/unauthorized private
/// paths. Works with `authenticated_transfer` and all existing programs out of the box.
PrivateShared {
nsk: NullifierSecretKey,
npk: NullifierPublicKey,
vpk: ViewingPublicKey,
identifier: Identifier,
},
2026-05-11 20:26:03 -03:00
/// A shared private PDA with externally-provided keys (e.g. from GMS).
/// `account_id` was derived via [`AccountId::for_private_pda`].
PrivatePdaShared {
account_id: AccountId,
nsk: NullifierSecretKey,
npk: NullifierPublicKey,
vpk: ViewingPublicKey,
identifier: Identifier,
},
}
2026-06-03 14:51:15 +03:00
impl fmt::Debug for AccountIdentity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Public(id) => f.debug_tuple("Public").field(id).finish(),
Self::PublicNoSign(id) => f.debug_tuple("PublicNoSign").field(id).finish(),
Self::PublicKeycard {
account_id,
key_path: _,
} => f
.debug_struct("PublicKeycard")
.field("account_id", account_id)
.field("key_path", &"<redacted>")
.finish(),
2026-06-03 14:51:15 +03:00
Self::PrivateOwned(id) => f.debug_tuple("PrivateOwned").field(id).finish(),
Self::PrivateForeign {
npk,
vpk,
identifier,
} => f
.debug_struct("PrivateForeign")
.field("npk", npk)
.field("vpk", vpk)
.field("identifier", identifier)
.finish(),
Self::PrivatePdaOwned(id) => f.debug_tuple("PrivatePdaOwned").field(id).finish(),
Self::PrivatePdaForeign {
account_id,
npk,
vpk,
identifier,
} => f
.debug_struct("PrivatePdaForeign")
.field("account_id", account_id)
.field("npk", npk)
.field("vpk", vpk)
.field("identifier", identifier)
.finish(),
Self::PrivateShared {
npk,
vpk,
identifier,
..
} => f
.debug_struct("PrivateShared")
.field("nsk", &"<redacted>")
.field("npk", npk)
.field("vpk", vpk)
.field("identifier", identifier)
.finish(),
Self::PrivatePdaShared {
account_id,
npk,
vpk,
identifier,
..
} => f
.debug_struct("PrivatePdaShared")
.field("account_id", account_id)
.field("nsk", &"<redacted>")
.field("npk", npk)
.field("vpk", vpk)
.field("identifier", identifier)
.finish(),
}
}
}
2026-05-20 18:55:39 +03:00
impl AccountIdentity {
2026-03-03 23:21:08 +03:00
#[must_use]
2026-05-21 13:02:18 +03:00
/// Note: `PublicNoSign` still counts as public, the variant just suppresses the signing-key
/// lookup.
2026-03-09 18:27:56 +03:00
pub const fn is_public(&self) -> bool {
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
matches!(
&self,
Self::Public(_) | Self::PublicNoSign(_) | Self::PublicKeycard { .. }
)
}
/// Returns the `AccountId` for public variants. Used by facades that need the raw ID
/// for derived-address computation alongside the identity.
#[must_use]
pub const fn public_account_id(&self) -> Option<lee::AccountId> {
match self {
Self::Public(id) | Self::PublicNoSign(id) => Some(*id),
Self::PublicKeycard { account_id, .. } => Some(*account_id),
Self::PrivateOwned(_)
| Self::PrivateForeign { .. }
| Self::PrivatePdaOwned(_)
| Self::PrivatePdaForeign { .. }
| Self::PrivateShared { .. }
| Self::PrivatePdaShared { .. } => None,
}
2025-12-16 14:05:34 +02:00
}
2026-03-03 23:21:08 +03:00
#[must_use]
2026-03-09 18:27:56 +03:00
pub const fn is_private(&self) -> bool {
2025-12-16 14:05:34 +02:00
matches!(
&self,
2026-04-19 23:13:51 -03:00
Self::PrivateOwned(_)
| Self::PrivateForeign { .. }
| Self::PrivatePdaOwned(_)
| Self::PrivatePdaForeign { .. }
| Self::PrivateShared { .. }
2026-05-11 20:26:03 -03:00
| Self::PrivatePdaShared { .. }
2025-12-16 14:05:34 +02:00
)
}
}
pub struct PrivateAccountKeys {
pub ssk: SharedSecretKey,
}
enum State {
Public {
account: AccountWithMetadata,
sk: Option<PrivateKey>,
},
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
PublicKeycard {
account: AccountWithMetadata,
key_path: String,
},
Private(AccountPreparedData),
}
pub struct AccountManager {
states: Vec<State>,
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
pin: Option<String>,
refactor(privacy_preserving_circuit): introduce helper functions to shorten long functions (#545) * refactor(privacy_preserving_circuit): extract functions for readability * refactor(privacy_preserving_circuit): address PR review comments Bundle shared handle_* arguments into PrivateOutputHandler struct in output.rs and fix misplaced docstring on resolve_external_seed in execution_state.rs. * feat: update commitment mechanism for new private account (#546) * refactor(privacy_preserving_circuit): extract functions for readability * feat: update commitment mechanism for new private accounts Allow init accounts to optionally use a real membership proof for DUMMY_COMMITMENT instead of hardcoding DUMMY_COMMITMENT_HASH as the CommitmentSetDigest. The wallet fetches the proof from the sequencer and passes it through the circuit. * fix: address clippy lints and fix integration test visibility * add tests * refactor: removed duplicated code * refactor: simplify init nullifier mechanism Replace Option<MembershipProof> with Option<CommitmentSetDigest> on init variants (PrivateAuthorizedInit, PrivateUnauthorized, PrivatePdaInit). The circuit now receives the commitment tree root directly instead of recomputing it from a Merkle proof. * refactor: use CommitmentSetDigest directly instead of Option for init commitment root Address PR #546 review feedback: the circuit now accepts CommitmentSetDigest directly on init variants (PrivateAuthorizedInit, PrivateUnauthorized, PrivatePdaInit), with callers providing DUMMY_COMMITMENT_HASH as the default. Also fixes duplicate resolve_external_seed from rebase and rebuilds artifacts. * style: run cargo +nightly fmt
2026-07-01 10:14:32 -04:00
dummy_commitment_root: CommitmentSetDigest,
}
impl AccountManager {
/// The private-account count that every privacy-preserving transaction is padded up to with
/// dummy inputs via the default interface.
///
/// The value is selected based on the largest account number per-tx currently supported
/// (it is 7 for AMM). It is recommended to reassess this value per new actively supported
/// application and that all users share the value for a larger anonymity set.
const MAX_PRIVATE_ACCOUNTS: usize = 7;
pub async fn new(
wallet: &WalletCore,
2026-05-20 18:55:39 +03:00
accounts: Vec<AccountIdentity>,
) -> Result<Self, ExecutionFailureKind> {
let mut states = Vec::with_capacity(accounts.len());
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
let mut pin = None;
for account in accounts {
let state = match account {
2026-05-20 18:55:39 +03:00
AccountIdentity::Public(account_id) => {
let acc = wallet
.get_account_public(account_id)
.await
2026-03-04 18:42:33 +03:00
.map_err(ExecutionFailureKind::SequencerError)?;
let sk = wallet.get_account_public_signing_key(account_id).cloned();
let account = AccountWithMetadata::new(acc.clone(), sk.is_some(), account_id);
2026-05-19 17:54:25 +03:00
State::Public { account, sk }
}
2026-05-20 18:55:39 +03:00
AccountIdentity::PublicNoSign(account_id) => {
2026-05-19 17:54:25 +03:00
let acc = wallet
.get_account_public(account_id)
.await
.map_err(ExecutionFailureKind::SequencerError)?;
let sk = None;
let account = AccountWithMetadata::new(acc.clone(), sk.is_some(), account_id);
State::Public { account, sk }
}
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
AccountIdentity::PublicKeycard {
account_id,
key_path,
} => {
let acc = wallet
.get_account_public(account_id)
.await
.map_err(ExecutionFailureKind::SequencerError)?;
let account = AccountWithMetadata::new(acc.clone(), true, account_id);
if pin.is_none() {
pin = Some(
crate::helperfunctions::read_pin()
refactor(keycard_wallet): replace keycard-py with keycard-rs (#595) * refactor(keycard_wallet): replace keycard-py (pyo3) with native keycard-rs Rewrites the Keycard integration to talk to the LEE-flavored applet directly from Rust via keycard-rs (pinned git dependency), dropping the pyo3 shim, python_path.rs, and the vendored keycard-py Python library entirely. Verified end-to-end against real hardware: pairing, PIN verification, mnemonic loading, BIP340 Schnorr signing, and transfers between keycard and public/private accounts. Also cleans up the pyo3 usage that had leaked into wallet's signing path (signing.rs, account_manager.rs) beyond the keycard CLI itself, and trims wallet_with_keycard.sh's Python setup down to just pyscard, which is only needed by the force_unpower.py test helper now. * Updated to v2 secure communication * ci: install libpcsclite-dev for pcsc crate builds * docs(keycard): clarify personalization is mandatory and document default CA Reinstalling the applet via `./gradlew install` wipes any existing personalization regardless of firmware source, which wasn't previously called out and is a common source of "card not available" confusion. Also state plainly that personalization is required before any command works, and document keycard-rs's actual baked-in default CA public key instead of only describing the override mechanism. Export KEYCARD_CA_PUBLIC_KEY in all keycard test scripts so they work against the dev/test-CA personalization flow they rely on. * fix(keycard): address PR #595 review comments - Propagate the load_mnemonic error instead of swallowing it behind is_ok(), matching every other command handler in this file. - Add deny.toml allowing the keycard-rs git source, fixing the source-not-allowed failure in `cargo deny check`. Licenses and advisories checks still have pre-existing, separate failures not addressed here. - Drop the pinned rev for the keycard-rs git dependency so it tracks the upstream default branch instead. * chore(deny): trim deny.toml to essentials and allow all in-use licenses Replace the full cargo-deny init template with just the two sections that matter: the keycard-rs git-source allowance, and an explicit license allow-list covering every license currently in the dependency tree. cargo deny check now passes on sources, bans, and licenses; advisories still fails on a pre-existing, unrelated RUSTSEC advisory. * fix(deny): remove second config and add source to the original one * chore: pin keycard-rs dependency and fix factory-reset debug-gate doc * chore: regenerate test fixture and lockfile after rebase onto dev --------- Co-authored-by: Daniil Polyakov <arjentix@gmail.com>
2026-07-23 15:50:30 -04:00
.map_err(ExecutionFailureKind::SignError)?
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
.as_str()
.to_owned(),
);
}
State::PublicKeycard { account, key_path }
}
2026-05-20 18:55:39 +03:00
AccountIdentity::PrivateOwned(account_id) => {
let pre = private_key_tree_acc_preparation(wallet, account_id, false)?;
State::Private(pre)
}
2026-05-20 18:55:39 +03:00
AccountIdentity::PrivateForeign {
2026-04-19 23:13:51 -03:00
npk,
vpk,
identifier,
} => {
let acc = lee_core::account::Account::default();
refactor: `PrivateUnauthorized` authorization changed to true (#621) * refactor: rename PrivateUnauthorized to PrivateForeignInit The account_identity's is_authorized flag no longer determines authorization for this variant, so keep the name tied to what actually distinguishes it: no nsk, only npk (a foreign account init). * chore: rebuild guest artifacts and bump spin to clear yanked advisory Regenerate ELF artifacts after the PrivateForeignInit rename in lee_core (compiled into every guest program), and update spin 0.9.8 -> 0.9.9 since 0.9.8 was yanked from crates.io, per cargo deny check advisories. * test: align is_authorized with PrivateForeignInit's flipped semantics Recipient pre-states built for PrivateForeignInit now need is_authorized: true to match the assertion in output.rs. Also rewrites the boundary test that checked the old invalid case to check the new one, and updates stale "unauthorized" wording left over from the PrivateUnauthorized name. * chore: rebuild guest artifacts Reproducible across repeated local builds; likely toolchain drift since the prior artifact commit rather than a source change, since no guest-relevant source or Cargo.lock changed in between. * fix(tests): align integration tests with PrivateForeignInit and regenerate fixture prove_init_with_commitment_root (private.rs) and build_privacy_transaction (tps.rs) still built PrivateForeignInit recipients with is_authorized: false, same stale-semantics bug fixed earlier in the lee crate's own tests. The prebuilt sequencer DB dump embeds program IDs derived from guest ELF bytes, which shifted once the PrivateForeignInit rename changed lee_core (compiled into every guest program). The stale dump caused widespread "Unknown program" failures across integration test suites that exercise deployed programs (wallet_ffi, auth_transfer, bridge, amm, token, pinata, ata, indexer state-consistency checks). Regenerated via `just regenerate-test-fixture`. * fix(tests): rename leftover PrivateUnauthorized to PrivateForeignInit and regenerate fixture * test: align is_authorized with PrivateForeignInit's flipped semantics * chore: regenerate test fixture after rebase onto dev * chore: regenerate test fixture after rebase onto dev Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 17:32:01 -04:00
let auth_acc = AccountWithMetadata::new(acc, true, (&npk, &vpk, identifier));
let random_seed = random_bytes();
let pre = AccountPreparedData {
nsk: None,
npk,
identifier,
2026-01-21 17:27:23 -05:00
vpk,
2025-12-11 20:59:37 -03:00
pre_state: auth_acc,
proof: None,
random_seed,
is_pda: false,
};
2026-05-05 12:37:54 +02:00
State::Private(pre)
}
2026-05-20 18:55:39 +03:00
AccountIdentity::PrivatePdaOwned(account_id) => {
let pre = private_key_tree_acc_preparation(wallet, account_id, true)?;
State::Private(pre)
}
2026-05-20 18:55:39 +03:00
AccountIdentity::PrivatePdaForeign {
account_id,
npk,
vpk,
identifier,
} => {
let acc = lee_core::account::Account::default();
let auth_acc = AccountWithMetadata::new(acc, false, account_id);
let random_seed = random_bytes();
let pre = AccountPreparedData {
nsk: None,
npk,
identifier,
vpk,
pre_state: auth_acc,
proof: None,
random_seed,
is_pda: true,
};
State::Private(pre)
}
2026-05-20 18:55:39 +03:00
AccountIdentity::PrivateShared {
nsk,
npk,
vpk,
identifier,
} => {
2026-06-19 22:37:36 +04:00
let account_id = lee::AccountId::from((&npk, &vpk, identifier));
2026-05-11 20:26:03 -03:00
let pre = private_shared_acc_preparation(
wallet, account_id, nsk, npk, vpk, identifier, false,
);
2026-05-11 20:26:03 -03:00
State::Private(pre)
}
2026-05-20 18:55:39 +03:00
AccountIdentity::PrivatePdaShared {
2026-05-11 20:26:03 -03:00
account_id,
nsk,
npk,
vpk,
identifier,
} => {
let pre = private_shared_acc_preparation(
wallet, account_id, nsk, npk, vpk, identifier, true,
);
State::Private(pre)
}
};
states.push(state);
}
let dummy_commitment_root = fetch_private_proofs_and_root(wallet, &mut states).await?;
refactor(privacy_preserving_circuit): introduce helper functions to shorten long functions (#545) * refactor(privacy_preserving_circuit): extract functions for readability * refactor(privacy_preserving_circuit): address PR review comments Bundle shared handle_* arguments into PrivateOutputHandler struct in output.rs and fix misplaced docstring on resolve_external_seed in execution_state.rs. * feat: update commitment mechanism for new private account (#546) * refactor(privacy_preserving_circuit): extract functions for readability * feat: update commitment mechanism for new private accounts Allow init accounts to optionally use a real membership proof for DUMMY_COMMITMENT instead of hardcoding DUMMY_COMMITMENT_HASH as the CommitmentSetDigest. The wallet fetches the proof from the sequencer and passes it through the circuit. * fix: address clippy lints and fix integration test visibility * add tests * refactor: removed duplicated code * refactor: simplify init nullifier mechanism Replace Option<MembershipProof> with Option<CommitmentSetDigest> on init variants (PrivateAuthorizedInit, PrivateUnauthorized, PrivatePdaInit). The circuit now receives the commitment tree root directly instead of recomputing it from a Merkle proof. * refactor: use CommitmentSetDigest directly instead of Option for init commitment root Address PR #546 review feedback: the circuit now accepts CommitmentSetDigest directly on init variants (PrivateAuthorizedInit, PrivateUnauthorized, PrivatePdaInit), with callers providing DUMMY_COMMITMENT_HASH as the default. Also fixes duplicate resolve_external_seed from rebase and rebuilds artifacts. * style: run cargo +nightly fmt
2026-07-01 10:14:32 -04:00
Ok(Self {
states,
pin,
dummy_commitment_root,
})
}
pub fn pre_states(&self) -> Vec<AccountWithMetadata> {
self.states
.iter()
.map(|state| match state {
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
State::Public { account, .. } | State::PublicKeycard { account, .. } => {
account.clone()
}
2025-12-11 20:59:37 -03:00
State::Private(pre) => pre.pre_state.clone(),
})
.collect()
}
pub fn public_account_nonces(&self) -> Vec<Nonce> {
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
// Must match the signature order produced by sign_message(): local accounts first,
// keycard accounts second.
let local = self.states.iter().filter_map(|state| match state {
State::Public { account, sk } => sk.as_ref().map(|_| account.account.nonce),
State::PublicKeycard { .. } | State::Private(_) => None,
});
let keycard = self.states.iter().filter_map(|state| match state {
State::PublicKeycard { account, .. } => Some(account.account.nonce),
State::Public { .. } | State::Private(_) => None,
});
local.chain(keycard).collect()
}
pub fn private_account_keys(&self) -> Vec<PrivateAccountKeys> {
self.states
.iter()
.filter_map(|state| match state {
2026-06-19 22:37:36 +04:00
State::Private(pre) => Some(pre),
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
State::Public { .. } | State::PublicKeycard { .. } => None,
})
.map(|pre| {
let nonce = if pre.proof.is_some() {
pre.pre_state.account.nonce.private_account_nonce_increment(
pre.nsk.as_ref().expect("update variant must have nsk"),
)
} else {
lee_core::account::Nonce::private_account_nonce_init(&pre.pre_state.account_id)
};
let esk = lee_core::EphemeralSecretKey::new(
&pre.pre_state.account_id,
&pre.random_seed,
&nonce,
);
PrivateAccountKeys {
ssk: SharedSecretKey::encapsulate_deterministic(&pre.vpk, &esk).0,
}
2026-06-19 22:37:36 +04:00
})
.collect()
}
/// Given a count, generate that many dummy inputs with randomized seeds and notes.
/// Uses the given commitment root from the account.
pub fn dummy_inputs(&self, count: usize) -> Vec<DummyInput> {
std::iter::repeat_with(|| DummyInput {
nullifier_seed: random_bytes(),
commitment_seed: random_bytes(),
note: random_dummy_note(),
commitment_root: self.dummy_commitment_root,
})
.take(count)
.collect()
}
/// Generate the dummy inputs that pad this transaction's private-account count up to
/// `MAX_PRIVATE_ACCOUNTS`.
pub fn dummy_inputs_default(&self) -> Vec<DummyInput> {
let private_count = self
.states
.iter()
.filter(|state| matches!(state, State::Private(_)))
.count();
self.dummy_inputs(Self::MAX_PRIVATE_ACCOUNTS.saturating_sub(private_count))
}
/// Build the per-account input vec for the privacy-preserving circuit. Each variant carries
/// exactly the fields the circuit's code path for that account needs, with the ephemeral
/// keys (`ssk`) drawn from the cached values that `private_account_keys` and the message
/// construction also use, so all three views agree on the same ephemeral key.
pub fn account_identities(&self) -> Vec<InputAccountIdentity> {
self.states
.iter()
.map(|state| match state {
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
State::Public { .. } | State::PublicKeycard { .. } => InputAccountIdentity::Public,
2026-05-07 01:41:35 -03:00
State::Private(pre) if pre.is_pda => match (pre.nsk, pre.proof.clone()) {
(Some(nsk), Some(membership_proof)) => InputAccountIdentity::PrivatePdaUpdate {
2026-06-19 22:37:36 +04:00
vpk: pre.vpk.clone(),
random_seed: pre.random_seed,
view_tag: random_view_tag(),
2026-05-07 01:41:35 -03:00
nsk,
membership_proof,
identifier: pre.identifier,
seed: None,
2026-05-07 01:41:35 -03:00
},
_ => InputAccountIdentity::PrivatePdaInit {
2026-06-19 22:37:36 +04:00
vpk: pre.vpk.clone(),
random_seed: pre.random_seed,
2026-05-07 01:41:35 -03:00
npk: pre.npk,
identifier: pre.identifier,
refactor(privacy_preserving_circuit): introduce helper functions to shorten long functions (#545) * refactor(privacy_preserving_circuit): extract functions for readability * refactor(privacy_preserving_circuit): address PR review comments Bundle shared handle_* arguments into PrivateOutputHandler struct in output.rs and fix misplaced docstring on resolve_external_seed in execution_state.rs. * feat: update commitment mechanism for new private account (#546) * refactor(privacy_preserving_circuit): extract functions for readability * feat: update commitment mechanism for new private accounts Allow init accounts to optionally use a real membership proof for DUMMY_COMMITMENT instead of hardcoding DUMMY_COMMITMENT_HASH as the CommitmentSetDigest. The wallet fetches the proof from the sequencer and passes it through the circuit. * fix: address clippy lints and fix integration test visibility * add tests * refactor: removed duplicated code * refactor: simplify init nullifier mechanism Replace Option<MembershipProof> with Option<CommitmentSetDigest> on init variants (PrivateAuthorizedInit, PrivateUnauthorized, PrivatePdaInit). The circuit now receives the commitment tree root directly instead of recomputing it from a Merkle proof. * refactor: use CommitmentSetDigest directly instead of Option for init commitment root Address PR #546 review feedback: the circuit now accepts CommitmentSetDigest directly on init variants (PrivateAuthorizedInit, PrivateUnauthorized, PrivatePdaInit), with callers providing DUMMY_COMMITMENT_HASH as the default. Also fixes duplicate resolve_external_seed from rebase and rebuilds artifacts. * style: run cargo +nightly fmt
2026-07-01 10:14:32 -04:00
commitment_root: self.dummy_commitment_root,
seed: None,
2026-05-07 01:41:35 -03:00
},
},
State::Private(pre) => match (pre.nsk, pre.proof.clone()) {
(Some(nsk), Some(membership_proof)) => {
InputAccountIdentity::PrivateAuthorizedUpdate {
2026-06-19 22:37:36 +04:00
vpk: pre.vpk.clone(),
random_seed: pre.random_seed,
view_tag: random_view_tag(),
nsk,
membership_proof,
identifier: pre.identifier,
}
}
(Some(nsk), None) => InputAccountIdentity::PrivateAuthorizedInit {
2026-06-19 22:37:36 +04:00
vpk: pre.vpk.clone(),
random_seed: pre.random_seed,
nsk,
identifier: pre.identifier,
refactor(privacy_preserving_circuit): introduce helper functions to shorten long functions (#545) * refactor(privacy_preserving_circuit): extract functions for readability * refactor(privacy_preserving_circuit): address PR review comments Bundle shared handle_* arguments into PrivateOutputHandler struct in output.rs and fix misplaced docstring on resolve_external_seed in execution_state.rs. * feat: update commitment mechanism for new private account (#546) * refactor(privacy_preserving_circuit): extract functions for readability * feat: update commitment mechanism for new private accounts Allow init accounts to optionally use a real membership proof for DUMMY_COMMITMENT instead of hardcoding DUMMY_COMMITMENT_HASH as the CommitmentSetDigest. The wallet fetches the proof from the sequencer and passes it through the circuit. * fix: address clippy lints and fix integration test visibility * add tests * refactor: removed duplicated code * refactor: simplify init nullifier mechanism Replace Option<MembershipProof> with Option<CommitmentSetDigest> on init variants (PrivateAuthorizedInit, PrivateUnauthorized, PrivatePdaInit). The circuit now receives the commitment tree root directly instead of recomputing it from a Merkle proof. * refactor: use CommitmentSetDigest directly instead of Option for init commitment root Address PR #546 review feedback: the circuit now accepts CommitmentSetDigest directly on init variants (PrivateAuthorizedInit, PrivateUnauthorized, PrivatePdaInit), with callers providing DUMMY_COMMITMENT_HASH as the default. Also fixes duplicate resolve_external_seed from rebase and rebuilds artifacts. * style: run cargo +nightly fmt
2026-07-01 10:14:32 -04:00
commitment_root: self.dummy_commitment_root,
},
refactor: `PrivateUnauthorized` authorization changed to true (#621) * refactor: rename PrivateUnauthorized to PrivateForeignInit The account_identity's is_authorized flag no longer determines authorization for this variant, so keep the name tied to what actually distinguishes it: no nsk, only npk (a foreign account init). * chore: rebuild guest artifacts and bump spin to clear yanked advisory Regenerate ELF artifacts after the PrivateForeignInit rename in lee_core (compiled into every guest program), and update spin 0.9.8 -> 0.9.9 since 0.9.8 was yanked from crates.io, per cargo deny check advisories. * test: align is_authorized with PrivateForeignInit's flipped semantics Recipient pre-states built for PrivateForeignInit now need is_authorized: true to match the assertion in output.rs. Also rewrites the boundary test that checked the old invalid case to check the new one, and updates stale "unauthorized" wording left over from the PrivateUnauthorized name. * chore: rebuild guest artifacts Reproducible across repeated local builds; likely toolchain drift since the prior artifact commit rather than a source change, since no guest-relevant source or Cargo.lock changed in between. * fix(tests): align integration tests with PrivateForeignInit and regenerate fixture prove_init_with_commitment_root (private.rs) and build_privacy_transaction (tps.rs) still built PrivateForeignInit recipients with is_authorized: false, same stale-semantics bug fixed earlier in the lee crate's own tests. The prebuilt sequencer DB dump embeds program IDs derived from guest ELF bytes, which shifted once the PrivateForeignInit rename changed lee_core (compiled into every guest program). The stale dump caused widespread "Unknown program" failures across integration test suites that exercise deployed programs (wallet_ffi, auth_transfer, bridge, amm, token, pinata, ata, indexer state-consistency checks). Regenerated via `just regenerate-test-fixture`. * fix(tests): rename leftover PrivateUnauthorized to PrivateForeignInit and regenerate fixture * test: align is_authorized with PrivateForeignInit's flipped semantics * chore: regenerate test fixture after rebase onto dev * chore: regenerate test fixture after rebase onto dev Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 17:32:01 -04:00
(None, _) => InputAccountIdentity::PrivateForeignInit {
2026-06-19 22:37:36 +04:00
vpk: pre.vpk.clone(),
random_seed: pre.random_seed,
npk: pre.npk,
identifier: pre.identifier,
refactor(privacy_preserving_circuit): introduce helper functions to shorten long functions (#545) * refactor(privacy_preserving_circuit): extract functions for readability * refactor(privacy_preserving_circuit): address PR review comments Bundle shared handle_* arguments into PrivateOutputHandler struct in output.rs and fix misplaced docstring on resolve_external_seed in execution_state.rs. * feat: update commitment mechanism for new private account (#546) * refactor(privacy_preserving_circuit): extract functions for readability * feat: update commitment mechanism for new private accounts Allow init accounts to optionally use a real membership proof for DUMMY_COMMITMENT instead of hardcoding DUMMY_COMMITMENT_HASH as the CommitmentSetDigest. The wallet fetches the proof from the sequencer and passes it through the circuit. * fix: address clippy lints and fix integration test visibility * add tests * refactor: removed duplicated code * refactor: simplify init nullifier mechanism Replace Option<MembershipProof> with Option<CommitmentSetDigest> on init variants (PrivateAuthorizedInit, PrivateUnauthorized, PrivatePdaInit). The circuit now receives the commitment tree root directly instead of recomputing it from a Merkle proof. * refactor: use CommitmentSetDigest directly instead of Option for init commitment root Address PR #546 review feedback: the circuit now accepts CommitmentSetDigest directly on init variants (PrivateAuthorizedInit, PrivateUnauthorized, PrivatePdaInit), with callers providing DUMMY_COMMITMENT_HASH as the default. Also fixes duplicate resolve_external_seed from rebase and rebuilds artifacts. * style: run cargo +nightly fmt
2026-07-01 10:14:32 -04:00
commitment_root: self.dummy_commitment_root,
},
},
})
.collect()
}
pub fn public_account_ids(&self) -> Vec<AccountId> {
self.states
.iter()
.filter_map(|state| match state {
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
State::Public { account, .. } | State::PublicKeycard { account, .. } => {
Some(account.account_id)
}
2026-03-03 23:21:08 +03:00
State::Private(_) => None,
})
.collect()
}
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
pub fn public_non_keycard_account_auth(&self) -> Vec<&PrivateKey> {
self.states
.iter()
.filter_map(|state| match state {
State::Public { sk, .. } => sk.as_ref(),
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
State::PublicKeycard { .. } | State::Private(_) => None,
})
.collect()
}
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
pub fn sign_message(&self, message_hash: [u8; 32]) -> Result<Vec<(Signature, PublicKey)>> {
let mut sigs: Vec<(Signature, PublicKey)> = self
.public_non_keycard_account_auth()
.into_iter()
.map(|key| {
(
Signature::new(key, &message_hash),
PublicKey::new_from_private_key(key),
)
})
.collect();
let keycard_paths: Vec<&str> = self
.states
.iter()
.filter_map(|state| match state {
State::PublicKeycard { key_path, .. } => Some(key_path.as_str()),
State::Private(_) | State::Public { .. } => None,
})
.collect();
if let Some(pin) = self.pin.clone() {
refactor(keycard_wallet): replace keycard-py with keycard-rs (#595) * refactor(keycard_wallet): replace keycard-py (pyo3) with native keycard-rs Rewrites the Keycard integration to talk to the LEE-flavored applet directly from Rust via keycard-rs (pinned git dependency), dropping the pyo3 shim, python_path.rs, and the vendored keycard-py Python library entirely. Verified end-to-end against real hardware: pairing, PIN verification, mnemonic loading, BIP340 Schnorr signing, and transfers between keycard and public/private accounts. Also cleans up the pyo3 usage that had leaked into wallet's signing path (signing.rs, account_manager.rs) beyond the keycard CLI itself, and trims wallet_with_keycard.sh's Python setup down to just pyscard, which is only needed by the force_unpower.py test helper now. * Updated to v2 secure communication * ci: install libpcsclite-dev for pcsc crate builds * docs(keycard): clarify personalization is mandatory and document default CA Reinstalling the applet via `./gradlew install` wipes any existing personalization regardless of firmware source, which wasn't previously called out and is a common source of "card not available" confusion. Also state plainly that personalization is required before any command works, and document keycard-rs's actual baked-in default CA public key instead of only describing the override mechanism. Export KEYCARD_CA_PUBLIC_KEY in all keycard test scripts so they work against the dev/test-CA personalization flow they rely on. * fix(keycard): address PR #595 review comments - Propagate the load_mnemonic error instead of swallowing it behind is_ok(), matching every other command handler in this file. - Add deny.toml allowing the keycard-rs git source, fixing the source-not-allowed failure in `cargo deny check`. Licenses and advisories checks still have pre-existing, separate failures not addressed here. - Drop the pinned rev for the keycard-rs git dependency so it tracks the upstream default branch instead. * chore(deny): trim deny.toml to essentials and allow all in-use licenses Replace the full cargo-deny init template with just the two sections that matter: the keycard-rs git-source allowance, and an explicit license allow-list covering every license currently in the dependency tree. cargo deny check now passes on sources, bans, and licenses; advisories still fails on a pre-existing, unrelated RUSTSEC advisory. * fix(deny): remove second config and add source to the original one * chore: pin keycard-rs dependency and fix factory-reset debug-gate doc * chore: regenerate test fixture and lockfile after rebase onto dev --------- Co-authored-by: Daniil Polyakov <arjentix@gmail.com>
2026-07-23 15:50:30 -04:00
let mut wallet = KeycardWallet::new()?;
wallet.connect(&pin)?;
for path in keycard_paths {
sigs.push(wallet.sign_message_for_path(path, &message_hash)?);
}
feat(wallet): add keycard support for public accounts for public/privacy txs for program facades (#461) * feat: add basic commands for communicating with keycard * initialize changes * reorganization * add script file for easier wallet access * update commands * fixes * fixed load for non continuous run * Updates for signatures with keycard * fix BIP-340 signatures for fixed sized messages * fmt * refactor and add pin support to program facades * fix unit test * fixes * Revert "fixes" This reverts commit 41f34f4ff4145b7abb60fd9bec168ae4b60f23b4. * fixes * fixes * Removed privacy keycard calls * Revert "Removed privacy keycard calls" This reverts commit d70ef505a1f40b87159099761f5fce5a31e3f17b. * Add domain separators * Removed privacy txs for keycard * CI fixes * CI fixes * addressed some comments * fix ci * initialize branch * ci fixes * fix integration test issue and updated keycard firmware * addressed more comments * fixed deny * remove keycard-py * fixed from earlier merge * add hash_message tests * add test * fix deny * CI fixes * fixed integration tests * Update public.rs * update artifacts * privacy command fixes * ci and comments * addressed comments * comment fixes * fixes from merging main * adding support to other programs * expanded support * ci fixes * ci and add private account keys test * some fixes and setup notes * Ci fixes * ci fixes * update key paths to avoid collisions in tests * added separated files for keycard_tests_2.sh * first round of comments * Revert "Merge branch 'main' into marvin/keycard-commands" This reverts commit 3fce53f663a3996938dddf77680854570063ca21, reversing changes made to e7b42a5177641455a8917bd2e29db20afd9690e5. * python comments * addressed comments * compile error fixed * fix artifacts * fix main merge error * adjust signer logic workflow * updating logic * fmt * refactored * clippy fix * minor fix * addressing comments * minor fix * ci fix * addressed deferred comments * clean up * minor cleanup * ci fixes * fmt fix * feat!(wallet): Merged `SigningGroup` with `AccountManager` (#500) * feat: account manager extension * feat(wallet): added unified way of sending public transactions to all facades * fix(wallet): no sign option added * fix(deny): deny fix * fix(wallet): suggestion 1 * fix(wallet): suggestion fix 1 * feat!: Add new path for externally provided seed to the circuit. BREAKING CHANGE: add identity variants to the circuit and change semantics for `Claim::Authorized` for private PDAs * feat(ci): use separate job per each integration tests module * feat(ci): cache rust artifacts * feat(ci): build integration tests binary once and reuse it * fix(wallet): fmt * ci: add bench-regression workflow with criterion-compare for crypto_primitives_bench * fix(wallet): merge postfix * feat!(wallet): SigningGroup merged with AccountManager * fix(ci): deny and artifacts fix * fix(deny): deny fix * fix keycard and lint --------- Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Co-authored-by: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> * addressed comments * minor comments * Rebase to main * CI fixes --------- Co-authored-by: Pravdyvy <46261001+Pravdyvy@users.noreply.github.com> Co-authored-by: Sergio Chouhy <sergio.chouhy@gmail.com> Co-authored-by: Daniil Polyakov <arjentix@gmail.com> Co-authored-by: Moudy <m.ellaz@hotmail.com> Co-authored-by: Sergio Chouhy <41742639+schouhy@users.noreply.github.com>
2026-06-05 17:35:10 -04:00
}
Ok(sigs)
}
}
2025-12-11 21:15:57 -03:00
struct AccountPreparedData {
nsk: Option<NullifierSecretKey>,
npk: NullifierPublicKey,
2026-04-15 01:09:17 -03:00
identifier: Identifier,
2026-01-21 17:27:23 -05:00
vpk: ViewingPublicKey,
2025-12-11 21:15:57 -03:00
pre_state: AccountWithMetadata,
proof: Option<MembershipProof>,
random_seed: [u8; 32],
/// True when this account is a private PDA (owned or foreign). Used by `account_identities()`
/// to select `PrivatePdaInit`/`PrivatePdaUpdate` rather than the standalone private variants.
is_pda: bool,
}
fn private_key_tree_acc_preparation(
wallet: &WalletCore,
account_id: AccountId,
is_pda: bool,
) -> Result<AccountPreparedData, ExecutionFailureKind> {
let Some(from_acc) = wallet.storage.key_chain().private_account(account_id) else {
return Err(ExecutionFailureKind::KeyNotFoundError);
};
let from_identifier = from_acc.kind.identifier();
let from_keys = &from_acc.key_chain;
2026-05-11 20:26:03 -03:00
let nsk = from_keys.private_key_holder.nullifier_secret_key;
let from_npk = from_keys.nullifier_public_key;
let from_vpk = from_keys.viewing_public_key.clone();
2026-05-11 20:26:03 -03:00
// TODO: Technically we could allow unauthorized owned accounts, but currently we don't have
// support from that in the wallet.
let sender_pre = AccountWithMetadata::new(from_acc.account.clone(), true, account_id);
let random_seed = random_bytes();
2026-05-05 12:37:54 +02:00
Ok(AccountPreparedData {
2026-05-11 20:26:03 -03:00
nsk: Some(nsk),
npk: from_npk,
identifier: from_identifier,
vpk: from_vpk,
pre_state: sender_pre,
proof: None,
random_seed,
2026-05-11 20:26:03 -03:00
is_pda,
})
}
fn private_shared_acc_preparation(
wallet: &WalletCore,
2026-05-11 20:26:03 -03:00
account_id: AccountId,
nsk: NullifierSecretKey,
npk: NullifierPublicKey,
vpk: ViewingPublicKey,
identifier: Identifier,
2026-05-11 20:26:03 -03:00
is_pda: bool,
) -> AccountPreparedData {
let acc = wallet
.storage()
.key_chain()
.shared_private_account(account_id)
.map(|e| e.account.clone())
.unwrap_or_default();
2026-05-12 17:17:58 -03:00
let pre_state = AccountWithMetadata::new(acc, true, account_id);
let random_seed = random_bytes();
AccountPreparedData {
2026-05-12 17:17:58 -03:00
nsk: Some(nsk),
npk,
identifier,
vpk,
pre_state,
proof: None,
random_seed,
2026-05-11 20:26:03 -03:00
is_pda,
}
}
async fn fetch_private_proofs_and_root(
wallet: &WalletCore,
states: &mut [State],
) -> Result<CommitmentSetDigest, ExecutionFailureKind> {
let (mut private, commitments): (Vec<&mut AccountPreparedData>, Vec<Commitment>) = states
.iter_mut()
.filter_map(|state| match state {
State::Private(pre) => {
let commitment = wallet.get_private_account_commitment(pre.pre_state.account_id)?;
Some((pre, commitment))
}
State::Public { .. } | State::PublicKeycard { .. } => None,
})
.unzip();
let (proofs, root) = wallet
2026-07-21 18:01:04 +03:00
.get_proofs_and_root(&commitments)
.await
.map_err(ExecutionFailureKind::SequencerError)?;
validate_proofs_against_root(&commitments, &proofs, root)?;
for (pre, proof) in private.iter_mut().zip(proofs) {
pre.proof = proof;
}
Ok(root)
}
fn validate_proofs_against_root(
commitments: &[Commitment],
proofs: &[Option<MembershipProof>],
root: CommitmentSetDigest,
) -> Result<(), ExecutionFailureKind> {
if proofs.len() != commitments.len() {
return Err(ExecutionFailureKind::SequencerError(anyhow::anyhow!(
"Sequencer returned {} proofs for {} commitments.",
proofs.len(),
commitments.len(),
)));
}
for (commitment, proof) in commitments.iter().zip(proofs) {
if let Some(proof) = proof
&& compute_digest_for_path(commitment, proof) != root
{
return Err(ExecutionFailureKind::SequencerError(anyhow::anyhow!(
"Membership proof for {commitment:?} does not reproduce the appropriate root {root:?}.",
)));
}
}
Ok(())
}
/// Generate random byte using OS randomness.
fn random_view_tag() -> ViewTag {
let mut byte: [u8; 1] = [0; 1];
OsRng.fill_bytes(&mut byte);
byte[0]
}
fn random_bytes() -> [u8; 32] {
let mut bytes = [0; 32];
OsRng.fill_bytes(&mut bytes);
bytes
}
fn random_vec(len: usize) -> Vec<u8> {
let mut bytes = vec![0; len];
OsRng.fill_bytes(&mut bytes);
bytes
}
/// Generates a dummy note: random bytes sized to a default-account ciphertext, a real
/// ML-KEM ciphertext epk toward a throwaway key, and a random view tag.
fn random_dummy_note() -> EncryptedAccountData {
// Sized to a default-account ciphertext; matching real data sizes is a separate issue.
let ciphertext_len = PrivateAccountKind::HEADER_LEN
.checked_add(Account::default().to_bytes().len())
.expect("dummy ciphertext length fits in usize");
let throwaway_ek = MlKem768EncapsulationKey::from_seed(&random_bytes(), &random_bytes());
let (_, epk) = SharedSecretKey::encapsulate(&throwaway_ek);
EncryptedAccountData {
ciphertext: Ciphertext::from_inner(random_vec(ciphertext_len)),
epk,
view_tag: random_view_tag(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn private_shared_is_private() {
2026-05-20 18:55:39 +03:00
let acc = AccountIdentity::PrivateShared {
nsk: [0; 32],
npk: NullifierPublicKey([1; 32]),
vpk: ViewingPublicKey::from_seed(&[2_u8; 32], &[3_u8; 32]),
identifier: 42,
};
assert!(acc.is_private());
assert!(!acc.is_public());
}
fn private_state() -> State {
let npk = NullifierPublicKey([0; 32]);
let vpk = ViewingPublicKey::from_seed(&[0; 32], &[0; 32]);
let pre_state = AccountWithMetadata::new(Account::default(), false, (&npk, &vpk, 0));
State::Private(AccountPreparedData {
nsk: None,
npk,
identifier: 0,
vpk,
pre_state,
proof: None,
random_seed: [0; 32],
is_pda: false,
})
}
fn public_state() -> State {
let npk = NullifierPublicKey([0; 32]);
let vpk = ViewingPublicKey::from_seed(&[0; 32], &[0; 32]);
let account = AccountWithMetadata::new(Account::default(), false, (&npk, &vpk, 0));
State::Public { account, sk: None }
}
fn manager(states: Vec<State>) -> AccountManager {
AccountManager {
states,
pin: None,
dummy_commitment_root: [0; 32],
}
}
#[test]
fn dummy_inputs_default_pads_private_count_to_max() {
let max = AccountManager::MAX_PRIVATE_ACCOUNTS;
// Empty txs get padded to the max.
assert_eq!(manager(vec![]).dummy_inputs_default().len(), max);
// In a padded transaction, the padding amount depends on
// the amount of private accounts used.
assert_eq!(
manager(vec![private_state(), private_state()])
.dummy_inputs_default()
.len(),
max - 2
);
assert_eq!(
manager(vec![private_state(), public_state(), private_state()])
.dummy_inputs_default()
.len(),
max - 2
);
// If the private accounts in the transaction exceed the max, no padding
// is done.
let full: Vec<State> = std::iter::repeat_with(private_state).take(max).collect();
assert_eq!(manager(full).dummy_inputs_default().len(), 0);
let over: Vec<State> = std::iter::repeat_with(private_state)
.take(max + 2)
.collect();
assert_eq!(manager(over).dummy_inputs_default().len(), 0);
}
}