455 lines
15 KiB
Rust
Raw Normal View History

use std::{io::Write as _, path::PathBuf, str::FromStr};
2025-12-09 15:18:48 -03:00
2026-03-04 18:42:33 +03:00
use anyhow::{Context as _, Result};
fix(wallet): use cryptographically secure entropy for mnemonic generation The mnemonic/wallet generation was using a constant zero-byte array for entropy ([0u8; 32]), making all wallets deterministic based solely on the password. This commit introduces proper random entropy using OsRng and enables users to back up their recovery phrase. Changes: - SeedHolder::new_mnemonic() now uses OsRng for 256-bit random entropy and returns the generated mnemonic - Added SeedHolder::from_mnemonic() to recover a wallet from an existing mnemonic phrase - WalletChainStore::new_storage() returns the mnemonic for user backup - Added WalletChainStore::restore_storage() for recovery from a mnemonic - WalletCore::new_init_storage() now returns the mnemonic - Renamed reset_storage to restore_storage, which accepts a mnemonic for recovery - CLI displays the recovery phrase when a new wallet is created - RestoreKeys command now prompts for the mnemonic phrase via read_mnemonic_from_stdin() Note: The password parameter is retained for future storage encryption but is no longer used in seed derivation (empty passphrase is used instead). This means the mnemonic alone is sufficient to recover accounts. Usage: On first wallet initialization, users will see: IMPORTANT: Write down your recovery phrase and store it securely. This is the only way to recover your wallet if you lose access. Recovery phrase: word1 word2 word3 ... word24 To restore keys: wallet restore-keys --depth 5 Input recovery phrase: <24 words> Input password: <password>
2026-01-20 16:47:34 +01:00
use bip39::Mnemonic;
use clap::{Parser, Subcommand};
use common::{HashType, transaction::LeeTransaction};
use derive_more::Display;
2026-03-13 22:56:14 +03:00
use futures::TryFutureExt as _;
use lee::{ProgramDeploymentTransaction, program::Program};
use sequencer_service_rpc::RpcClient as _;
2025-10-13 17:25:36 +03:00
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
pub use crate::helperfunctions::{read_mnemonic, read_pin};
use crate::{
WalletCore,
account::{AccountIdWithPrivacy, Label},
cli::{
account::AccountSubcommand,
chain::ChainSubcommand,
config::ConfigSubcommand,
group::GroupSubcommand,
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
keycard::KeycardSubcommand,
programs::{
amm::AmmProgramAgnosticSubcommand, ata::AtaSubcommand,
native_token_transfer::AuthTransferSubcommand, pinata::PinataProgramAgnosticSubcommand,
2026-06-05 21:34:00 +03:00
token::TokenProgramAgnosticSubcommand, vault::VaultSubcommand,
},
},
storage::Storage,
};
2025-10-13 17:25:36 +03:00
2025-10-20 10:01:54 +03:00
pub mod account;
2025-10-14 15:29:18 +03:00
pub mod chain;
2025-11-03 15:45:50 +02:00
pub mod config;
pub mod group;
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
pub mod keycard;
pub mod programs;
2025-10-13 17:25:36 +03:00
pub(crate) trait WalletSubcommand {
async fn handle_subcommand(self, wallet_core: &mut WalletCore)
-> Result<SubcommandReturnValue>;
}
2026-03-10 00:17:43 +03:00
/// Represents CLI command for a wallet.
#[derive(Subcommand, Debug, Clone)]
#[clap(about)]
pub enum Command {
2026-03-10 00:17:43 +03:00
/// Authenticated transfer subcommand.
#[command(subcommand)]
AuthTransfer(AuthTransferSubcommand),
2026-03-10 00:17:43 +03:00
/// Generic chain info subcommand.
#[command(subcommand)]
ChainInfo(ChainSubcommand),
2026-03-10 00:17:43 +03:00
/// Account view and sync subcommand.
#[command(subcommand)]
Account(AccountSubcommand),
2026-03-10 00:17:43 +03:00
/// Pinata program interaction subcommand.
#[command(subcommand)]
Pinata(PinataProgramAgnosticSubcommand),
2026-03-10 00:17:43 +03:00
/// Token program interaction subcommand.
#[command(subcommand)]
Token(TokenProgramAgnosticSubcommand),
2026-03-10 00:17:43 +03:00
/// AMM program interaction subcommand.
2025-12-16 14:05:34 +02:00
#[command(subcommand)]
AMM(AmmProgramAgnosticSubcommand),
/// Associated Token Account program interaction subcommand.
#[command(subcommand)]
Ata(AtaSubcommand),
2026-06-05 21:34:00 +03:00
/// Vault program interaction subcommand.
#[command(subcommand)]
Vault(VaultSubcommand),
/// Group key management (create, invite, join, derive keys).
#[command(subcommand)]
Group(GroupSubcommand),
/// Check the wallet can connect to the node and builtin local programs
2026-03-10 00:17:43 +03:00
/// match the remote versions.
2026-03-04 18:42:33 +03:00
CheckHealth,
2026-03-10 00:17:43 +03:00
/// Command to setup config, get and set config fields.
#[command(subcommand)]
Config(ConfigSubcommand),
2026-03-10 00:17:43 +03:00
/// Restoring keys from given password at given `depth`.
2025-12-03 13:10:07 +02:00
///
2026-03-10 00:17:43 +03:00
/// !!!WARNING!!! will rewrite current storage.
RestoreKeys {
#[arg(short, long)]
2025-12-03 13:10:07 +02:00
/// Indicates, how deep in tree accounts may be. Affects command complexity.
depth: u32,
},
2026-03-10 00:17:43 +03:00
/// Deploy a program.
DeployProgram { binary_filepath: PathBuf },
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
/// Keycard hardware wallet management.
#[command(subcommand)]
Keycard(KeycardSubcommand),
}
/// To execute commands, env var `LEE_WALLET_HOME_DIR` must be set into directory with config.
///
2025-12-08 18:26:35 +03:00
/// All account addresses must be valid 32 byte base58 strings.
///
2026-03-03 23:21:08 +03:00
/// All account `account_ids` must be provided as {`privacy_prefix}/{account_id`},
2026-03-10 00:17:43 +03:00
/// where valid options for `privacy_prefix` is `Public` and `Private`.
#[derive(Parser, Debug)]
#[clap(version, about)]
pub struct Args {
2026-03-10 00:17:43 +03:00
/// Continious run flag.
#[arg(short, long)]
pub continuous_run: bool,
2026-03-10 00:17:43 +03:00
/// Basic authentication in the format `user` or `user:password`.
2025-12-08 18:26:35 +03:00
#[arg(long)]
pub auth: Option<String>,
2026-03-10 00:17:43 +03:00
/// Wallet command.
#[command(subcommand)]
2025-12-03 13:10:07 +02:00
pub command: Option<Command>,
}
#[derive(Debug, Clone)]
pub enum SubcommandReturnValue {
PrivacyPreservingTransfer { tx_hash: HashType },
RegisterAccount { account_id: lee::AccountId },
Account(lee::Account),
Empty,
SyncedToBlock(u64),
}
#[derive(Debug, Display, Clone, PartialEq, Eq, Hash)]
pub enum CliAccountMention {
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
#[display("{_0}")]
Id(AccountIdWithPrivacy),
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
#[display("{_0}")]
Label(Label),
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
#[display("{_0}")]
KeyPath(String),
}
impl CliAccountMention {
pub fn resolve(&self, storage: &Storage) -> Result<AccountIdWithPrivacy> {
match self {
Self::Id(account_id) => Ok(*account_id),
Self::Label(label) => storage
.resolve_label(label)
.ok_or_else(|| anyhow::anyhow!("No account found for label `{label}`")),
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
Self::KeyPath(path) => {
let pin = read_pin()?;
let id_str =
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
keycard_wallet::KeycardWallet::get_public_account_id_for_path_with_connect(
&pin, path,
)
.map_err(anyhow::Error::from)?;
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
AccountIdWithPrivacy::from_str(&id_str)
.map_err(|e| anyhow::anyhow!("Invalid account id from keycard: {e}"))
}
}
}
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_use]
pub fn key_path(&self) -> Option<&str> {
match self {
Self::KeyPath(path) => Some(path),
Self::Id(_) | Self::Label(_) => None,
}
}
#[must_use]
pub fn into_public_identity(self, account_id: lee::AccountId) -> crate::AccountIdentity {
match self {
Self::KeyPath(key_path) => crate::AccountIdentity::PublicKeycard {
account_id,
key_path,
},
Self::Id(_) | Self::Label(_) => crate::AccountIdentity::Public(account_id),
}
}
}
impl FromStr for CliAccountMention {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
if s.starts_with("m/") {
return Ok(Self::KeyPath(s.to_owned()));
}
AccountIdWithPrivacy::from_str(s).map_or_else(
|_| Ok(Self::Label(Label::new(s.to_owned()))),
|account_id| Ok(Self::Id(account_id)),
)
}
}
impl From<Label> for CliAccountMention {
fn from(label: Label) -> Self {
Self::Label(label)
}
}
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
impl Default for CliAccountMention {
fn default() -> Self {
Self::Label(Label::new(String::new()))
}
}
pub async fn execute_subcommand(
wallet_core: &mut WalletCore,
2025-12-08 18:26:35 +03:00
command: Command,
) -> Result<SubcommandReturnValue> {
let subcommand_ret = match command {
Command::AuthTransfer(transfer_subcommand) => {
transfer_subcommand.handle_subcommand(wallet_core).await?
}
Command::ChainInfo(chain_subcommand) => {
chain_subcommand.handle_subcommand(wallet_core).await?
}
Command::Account(account_subcommand) => {
account_subcommand.handle_subcommand(wallet_core).await?
}
Command::Pinata(pinata_subcommand) => {
pinata_subcommand.handle_subcommand(wallet_core).await?
}
2026-03-04 18:42:33 +03:00
Command::CheckHealth => {
let remote_program_ids = wallet_core
.sequencer_client
.get_program_ids()
.await
.expect("Error fetching program ids");
let Some(authenticated_transfer_id) = remote_program_ids.get("authenticated_transfer")
else {
panic!("Missing authenticated transfer ID from remote");
};
2026-03-03 23:21:08 +03:00
assert!(
authenticated_transfer_id == &Program::authenticated_transfer_program().id(),
"Local ID for authenticated transfer program is different from remote"
);
let Some(token_id) = remote_program_ids.get("token") else {
panic!("Missing token program ID from remote");
};
2026-03-03 23:21:08 +03:00
assert!(
token_id == &Program::token().id(),
"Local ID for token program is different from remote"
);
let Some(circuit_id) = remote_program_ids.get("privacy_preserving_circuit") else {
panic!("Missing privacy preserving circuit ID from remote");
};
2026-03-03 23:21:08 +03:00
assert!(
circuit_id == &lee::PRIVACY_PRESERVING_CIRCUIT_ID,
2026-03-03 23:21:08 +03:00
"Local ID for privacy preserving circuit is different from remote"
);
let Some(amm_id) = remote_program_ids.get("amm") else {
panic!("Missing AMM program ID from remote");
};
2026-03-03 23:21:08 +03:00
assert!(
amm_id == &Program::amm().id(),
"Local ID for AMM program is different from remote"
);
2026-03-04 18:42:33 +03:00
println!("\u{2705}All looks good!");
SubcommandReturnValue::Empty
}
Command::Token(token_subcommand) => token_subcommand.handle_subcommand(wallet_core).await?,
Command::AMM(amm_subcommand) => amm_subcommand.handle_subcommand(wallet_core).await?,
Command::Ata(ata_subcommand) => ata_subcommand.handle_subcommand(wallet_core).await?,
2026-06-05 21:34:00 +03:00
Command::Vault(vault_subcommand) => vault_subcommand.handle_subcommand(wallet_core).await?,
Command::Group(group_subcommand) => group_subcommand.handle_subcommand(wallet_core).await?,
feat(wallet): add keycard support for public tx for auth-transfer (#451) * 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 * 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 * ci and comments * addressed comments * comment fixes * fixes from merging main * 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 * fmt * merge main and shift keycard tests * deny fix * artifacts fix * remove keycard scripts from root * tps fix * fmt
2026-05-21 20:46:13 -04:00
Command::Keycard(keycard_subcommand) => {
keycard_subcommand.handle_subcommand(wallet_core).await?
}
Command::Config(config_subcommand) => {
config_subcommand.handle_subcommand(wallet_core).await?
}
2025-12-03 13:10:07 +02:00
Command::RestoreKeys { depth } => {
fix(wallet): use cryptographically secure entropy for mnemonic generation The mnemonic/wallet generation was using a constant zero-byte array for entropy ([0u8; 32]), making all wallets deterministic based solely on the password. This commit introduces proper random entropy using OsRng and enables users to back up their recovery phrase. Changes: - SeedHolder::new_mnemonic() now uses OsRng for 256-bit random entropy and returns the generated mnemonic - Added SeedHolder::from_mnemonic() to recover a wallet from an existing mnemonic phrase - WalletChainStore::new_storage() returns the mnemonic for user backup - Added WalletChainStore::restore_storage() for recovery from a mnemonic - WalletCore::new_init_storage() now returns the mnemonic - Renamed reset_storage to restore_storage, which accepts a mnemonic for recovery - CLI displays the recovery phrase when a new wallet is created - RestoreKeys command now prompts for the mnemonic phrase via read_mnemonic_from_stdin() Note: The password parameter is retained for future storage encryption but is no longer used in seed derivation (empty passphrase is used instead). This means the mnemonic alone is sufficient to recover accounts. Usage: On first wallet initialization, users will see: IMPORTANT: Write down your recovery phrase and store it securely. This is the only way to recover your wallet if you lose access. Recovery phrase: word1 word2 word3 ... word24 To restore keys: wallet restore-keys --depth 5 Input recovery phrase: <24 words> Input password: <password>
2026-01-20 16:47:34 +01:00
let mnemonic = read_mnemonic_from_stdin()?;
2025-12-03 13:10:07 +02:00
let password = read_password_from_stdin()?;
fix(wallet): use cryptographically secure entropy for mnemonic generation The mnemonic/wallet generation was using a constant zero-byte array for entropy ([0u8; 32]), making all wallets deterministic based solely on the password. This commit introduces proper random entropy using OsRng and enables users to back up their recovery phrase. Changes: - SeedHolder::new_mnemonic() now uses OsRng for 256-bit random entropy and returns the generated mnemonic - Added SeedHolder::from_mnemonic() to recover a wallet from an existing mnemonic phrase - WalletChainStore::new_storage() returns the mnemonic for user backup - Added WalletChainStore::restore_storage() for recovery from a mnemonic - WalletCore::new_init_storage() now returns the mnemonic - Renamed reset_storage to restore_storage, which accepts a mnemonic for recovery - CLI displays the recovery phrase when a new wallet is created - RestoreKeys command now prompts for the mnemonic phrase via read_mnemonic_from_stdin() Note: The password parameter is retained for future storage encryption but is no longer used in seed derivation (empty passphrase is used instead). This means the mnemonic alone is sufficient to recover accounts. Usage: On first wallet initialization, users will see: IMPORTANT: Write down your recovery phrase and store it securely. This is the only way to recover your wallet if you lose access. Recovery phrase: word1 word2 word3 ... word24 To restore keys: wallet restore-keys --depth 5 Input recovery phrase: <24 words> Input password: <password>
2026-01-20 16:47:34 +01:00
wallet_core.restore_storage(&mnemonic, &password)?;
execute_keys_restoration(wallet_core, depth).await?;
2025-12-03 13:10:07 +02:00
SubcommandReturnValue::Empty
}
2025-12-05 17:54:51 -03:00
Command::DeployProgram { binary_filepath } => {
let bytecode: Vec<u8> = std::fs::read(&binary_filepath).context(format!(
"Failed to read program binary at {}",
binary_filepath.display()
))?;
let message = lee::program_deployment_transaction::Message::new(bytecode);
2025-12-05 17:54:51 -03:00
let transaction = ProgramDeploymentTransaction::new(message);
let _response = wallet_core
2025-12-05 17:54:51 -03:00
.sequencer_client
.send_transaction(LeeTransaction::ProgramDeployment(transaction))
2025-12-05 17:54:51 -03:00
.await
2026-02-24 19:41:01 +03:00
.context("Transaction submission error")?;
2025-12-05 17:54:51 -03:00
SubcommandReturnValue::Empty
}
};
Ok(subcommand_ret)
}
pub async fn execute_continuous_run(wallet_core: &mut WalletCore) -> Result<()> {
loop {
wallet_core.sync_to_latest_block().await?;
tokio::time::sleep(wallet_core.config().seq_poll_timeout).await;
}
}
2025-12-03 13:10:07 +02:00
pub fn read_password_from_stdin() -> Result<String> {
let mut password = String::new();
print!("Input password: ");
std::io::stdout().flush()?;
std::io::stdin().read_line(&mut password)?;
2026-03-04 18:42:33 +03:00
Ok(password.trim().to_owned())
2025-12-03 13:10:07 +02:00
}
/// Parse a keys file exported by `wallet account show-keys`.
///
/// The file format is two lines:
/// - Line 1: npk as hex (64 chars, 32 bytes).
/// - Line 2: vpk as hex (2368 chars, 1184 bytes).
///
/// Returns `(npk_bytes, vpk_bytes)`.
pub fn read_keys_file(path: &str) -> Result<(Vec<u8>, Vec<u8>)> {
let content = std::fs::read_to_string(path).with_context(|| {
format!("wallet::cli::read_keys_file: failed to read keys file: {path}")
})?;
let mut lines = content.lines().filter(|l| !l.trim().is_empty());
let npk_hex = lines.next().ok_or_else(|| {
anyhow::anyhow!("wallet::cli::read_keys_file: keys file is missing npk (line 1)")
})?;
let vpk_hex = lines.next().ok_or_else(|| {
anyhow::anyhow!("wallet::cli::read_keys_file: keys file is missing vpk (line 2)")
})?;
let npk = hex::decode(npk_hex.trim())
.context("wallet::cli::read_keys_file: npk in keys file must be valid hex")?;
let vpk = hex::decode(vpk_hex.trim())
.context("wallet::cli::read_keys_file: vpk in keys file must be valid hex")?;
Ok((npk, vpk))
}
fix(wallet): use cryptographically secure entropy for mnemonic generation The mnemonic/wallet generation was using a constant zero-byte array for entropy ([0u8; 32]), making all wallets deterministic based solely on the password. This commit introduces proper random entropy using OsRng and enables users to back up their recovery phrase. Changes: - SeedHolder::new_mnemonic() now uses OsRng for 256-bit random entropy and returns the generated mnemonic - Added SeedHolder::from_mnemonic() to recover a wallet from an existing mnemonic phrase - WalletChainStore::new_storage() returns the mnemonic for user backup - Added WalletChainStore::restore_storage() for recovery from a mnemonic - WalletCore::new_init_storage() now returns the mnemonic - Renamed reset_storage to restore_storage, which accepts a mnemonic for recovery - CLI displays the recovery phrase when a new wallet is created - RestoreKeys command now prompts for the mnemonic phrase via read_mnemonic_from_stdin() Note: The password parameter is retained for future storage encryption but is no longer used in seed derivation (empty passphrase is used instead). This means the mnemonic alone is sufficient to recover accounts. Usage: On first wallet initialization, users will see: IMPORTANT: Write down your recovery phrase and store it securely. This is the only way to recover your wallet if you lose access. Recovery phrase: word1 word2 word3 ... word24 To restore keys: wallet restore-keys --depth 5 Input recovery phrase: <24 words> Input password: <password>
2026-01-20 16:47:34 +01:00
pub fn read_mnemonic_from_stdin() -> Result<Mnemonic> {
let mut phrase = String::new();
print!("Input recovery phrase: ");
std::io::stdout().flush()?;
std::io::stdin().read_line(&mut phrase)?;
Mnemonic::from_str(phrase.trim()).context("Invalid mnemonic phrase")
}
pub async fn execute_keys_restoration(wallet_core: &mut WalletCore, depth: u32) -> Result<()> {
wallet_core
.storage
.key_chain_mut()
.generate_trees_for_depth(depth);
println!(
"Public tree generated\n\
Private tree generated"
);
wallet_core.sync_to_latest_block().await?;
wallet_core
.storage
.key_chain_mut()
.cleanup_trees_remove_uninit_layered(depth, |account_id| {
wallet_core
.sequencer_client
.get_account(account_id)
.map_err(Into::into)
})
.await?;
println!(
"Public tree cleaned up\n\
Private tree cleaned up"
);
wallet_core.store_persistent_data()?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_keys_file_roundtrip() {
let npk = [0xab_u8; 32];
let vpk = [0xcd_u8; 1184];
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.keys");
// Simulate what `wallet account show-keys` writes.
std::fs::write(
&path,
format!("{}\n{}\n", hex::encode(npk), hex::encode(vpk)),
)
.unwrap();
let (parsed_npk, parsed_vpk) = read_keys_file(path.to_str().unwrap()).unwrap();
assert_eq!(parsed_npk, npk, "npk must round-trip through the keys file");
assert_eq!(
parsed_vpk,
vpk.to_vec(),
"vpk must round-trip through the keys file"
);
}
#[test]
fn read_keys_file_missing_vpk_returns_error() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("incomplete.keys");
std::fs::write(&path, format!("{}\n", hex::encode([0xab_u8; 32]))).unwrap();
let result = read_keys_file(path.to_str().unwrap());
assert!(result.is_err(), "missing vpk line must return an error");
assert!(
result.unwrap_err().to_string().contains("missing vpk"),
"error must mention missing vpk"
);
}
#[test]
fn read_keys_file_invalid_hex_returns_error() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("badhex.keys");
std::fs::write(&path, "not-hex\nalso-not-hex\n").unwrap();
let result = read_keys_file(path.to_str().unwrap());
assert!(result.is_err(), "invalid hex must return an error");
}
#[test]
fn read_keys_file_ignores_blank_lines() {
let npk = [0x11_u8; 32];
let vpk = [0x22_u8; 1184];
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("blanks.keys");
// Extra blank lines around the data should be tolerated.
std::fs::write(
&path,
format!("\n{}\n\n{}\n\n", hex::encode(npk), hex::encode(vpk)),
)
.unwrap();
let (parsed_npk, parsed_vpk) = read_keys_file(path.to_str().unwrap()).unwrap();
assert_eq!(parsed_npk, npk);
assert_eq!(parsed_vpk, vpk.to_vec());
}
}