diff --git a/Cargo.lock b/Cargo.lock index 1cbf1843..78b97777 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4011,6 +4011,43 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "keycard_wallet" +version = "0.1.0" +dependencies = [ + "amm_core", + "anyhow", + "async-stream", + "ata_core", + "base58", + "bip39", + "clap", + "common", + "env_logger", + "futures", + "hex", + "humantime", + "humantime-serde", + "indicatif", + "itertools 0.14.0", + "key_protocol", + "log", + "nssa", + "nssa_core", + "optfield", + "pyo3", + "rand 0.8.5", + "sequencer_service_rpc", + "serde", + "serde_json", + "sha2", + "testnet_initial_state", + "thiserror 2.0.18", + "token_core", + "tokio", + "url", +] + [[package]] name = "lazy-regex" version = "3.6.0" @@ -9107,6 +9144,7 @@ dependencies = [ "indicatif", "itertools 0.14.0", "key_protocol", + "keycard_wallet", "log", "nssa", "nssa_core", diff --git a/Cargo.toml b/Cargo.toml index 5514c300..5621b827 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ members = [ "examples/program_deployment/methods/guest", "bedrock_client", "testnet_initial_state", + "keycard_wallet", ] [workspace.dependencies] @@ -67,6 +68,7 @@ ata_program = { path = "programs/associated_token_account" } test_program_methods = { path = "test_program_methods" } bedrock_client = { path = "bedrock_client" } testnet_initial_state = { path = "testnet_initial_state" } +keycard_wallet = { path = "keycard_wallet" } tokio = { version = "1.50", features = [ "net", diff --git a/integration_tests/tests/amm.rs b/integration_tests/tests/amm.rs index dde9e7f5..2c3c404e 100644 --- a/integration_tests/tests/amm.rs +++ b/integration_tests/tests/amm.rs @@ -134,6 +134,8 @@ async fn amm_public() -> Result<()> { to_npk: None, to_vpk: None, amount: 7, + from_pin: None, + from_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -163,6 +165,8 @@ async fn amm_public() -> Result<()> { to_npk: None, to_vpk: None, amount: 7, + from_pin: None, + from_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -551,6 +555,8 @@ async fn amm_new_pool_using_labels() -> Result<()> { to_npk: None, to_vpk: None, amount: 5, + from_pin: None, + from_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; @@ -575,6 +581,8 @@ async fn amm_new_pool_using_labels() -> Result<()> { to_npk: None, to_vpk: None, amount: 5, + from_pin: None, + from_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; diff --git a/integration_tests/tests/ata.rs b/integration_tests/tests/ata.rs index c0918635..608b15d4 100644 --- a/integration_tests/tests/ata.rs +++ b/integration_tests/tests/ata.rs @@ -269,6 +269,8 @@ async fn transfer_and_burn_via_ata() -> Result<()> { to_npk: None, to_vpk: None, amount: fund_amount, + from_pin: None, + from_key_path: None, }), ) .await?; @@ -501,6 +503,8 @@ async fn transfer_via_ata_private_owner() -> Result<()> { to_npk: None, to_vpk: None, amount: fund_amount, + from_pin: None, + from_key_path: None, }), ) .await?; @@ -615,6 +619,8 @@ async fn burn_via_ata_private_owner() -> Result<()> { to_npk: None, to_vpk: None, amount: fund_amount, + from_pin: None, + from_key_path: None, }), ) .await?; diff --git a/integration_tests/tests/auth_transfer/private.rs b/integration_tests/tests/auth_transfer/private.rs index cf02d0ac..2661084b 100644 --- a/integration_tests/tests/auth_transfer/private.rs +++ b/integration_tests/tests/auth_transfer/private.rs @@ -31,6 +31,8 @@ async fn private_transfer_to_owned_account() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -72,6 +74,8 @@ async fn private_transfer_to_foreign_account() -> Result<()> { to_npk: Some(to_npk_string), to_vpk: Some(hex::encode(to_vpk.0)), amount: 100, + pin: None, + key_path: None, }); let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -122,6 +126,8 @@ async fn deshielded_transfer_to_public_account() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -187,6 +193,8 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> { to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)), to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)), amount: 100, + pin: None, + key_path: None, }); let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -237,6 +245,8 @@ async fn shielded_transfer_to_owned_private_account() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -281,6 +291,8 @@ async fn shielded_transfer_to_foreign_account() -> Result<()> { to_npk: Some(to_npk_string), to_vpk: Some(hex::encode(to_vpk.0)), amount: 100, + pin: None, + key_path: None, }); let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -353,6 +365,8 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> { to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)), to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)), amount: 100, + pin: None, + key_path: None, }); let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -399,6 +413,8 @@ async fn initialize_private_account() -> Result<()> { let command = Command::AuthTransfer(AuthTransferSubcommand::Init { account_id: Some(format_private_account_id(account_id)), account_label: None, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -456,6 +472,8 @@ async fn private_transfer_using_from_label() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -499,6 +517,8 @@ async fn initialize_private_account_using_label() -> Result<()> { let command = Command::AuthTransfer(AuthTransferSubcommand::Init { account_id: None, account_label: Some(label), + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; diff --git a/integration_tests/tests/auth_transfer/public.rs b/integration_tests/tests/auth_transfer/public.rs index 416c4490..617bce53 100644 --- a/integration_tests/tests/auth_transfer/public.rs +++ b/integration_tests/tests/auth_transfer/public.rs @@ -24,6 +24,8 @@ async fn successful_transfer_to_existing_account() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -82,6 +84,8 @@ pub async fn successful_transfer_to_new_account() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -120,6 +124,8 @@ async fn failed_transfer_with_insufficient_balance() -> Result<()> { to_npk: None, to_vpk: None, amount: 1_000_000, + pin: None, + key_path: None, }); let failed_send = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await; @@ -160,6 +166,8 @@ async fn two_consecutive_successful_transfers() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -194,6 +202,8 @@ async fn two_consecutive_successful_transfers() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -238,6 +248,8 @@ async fn initialize_public_account() -> Result<()> { let command = Command::AuthTransfer(AuthTransferSubcommand::Init { account_id: Some(format_public_account_id(account_id)), account_label: None, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -279,6 +291,8 @@ async fn successful_transfer_using_from_label() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -326,6 +340,8 @@ async fn successful_transfer_using_to_label() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; diff --git a/integration_tests/tests/indexer.rs b/integration_tests/tests/indexer.rs index 0aef4a42..0e3e4d4d 100644 --- a/integration_tests/tests/indexer.rs +++ b/integration_tests/tests/indexer.rs @@ -113,6 +113,8 @@ async fn indexer_state_consistency() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -149,6 +151,8 @@ async fn indexer_state_consistency() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -235,6 +239,8 @@ async fn indexer_state_consistency_with_labels() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; diff --git a/integration_tests/tests/keys_restoration.rs b/integration_tests/tests/keys_restoration.rs index 8dca027c..60c10cbe 100644 --- a/integration_tests/tests/keys_restoration.rs +++ b/integration_tests/tests/keys_restoration.rs @@ -76,6 +76,8 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> { to_npk: Some(hex::encode(to_keys.nullifier_public_key.0)), to_vpk: Some(hex::encode(to_keys.viewing_public_key.0)), amount: 100, + pin: None, + key_path: None, }); let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -152,6 +154,8 @@ async fn restore_keys_from_seed() -> Result<()> { to_npk: None, to_vpk: None, amount: 100, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -164,6 +168,8 @@ async fn restore_keys_from_seed() -> Result<()> { to_npk: None, to_vpk: None, amount: 101, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -204,6 +210,8 @@ async fn restore_keys_from_seed() -> Result<()> { to_npk: None, to_vpk: None, amount: 102, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -216,6 +224,8 @@ async fn restore_keys_from_seed() -> Result<()> { to_npk: None, to_vpk: None, amount: 103, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -281,6 +291,8 @@ async fn restore_keys_from_seed() -> Result<()> { to_npk: None, to_vpk: None, amount: 10, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -292,6 +304,8 @@ async fn restore_keys_from_seed() -> Result<()> { to_npk: None, to_vpk: None, amount: 11, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; diff --git a/integration_tests/tests/pinata.rs b/integration_tests/tests/pinata.rs index 77c4a646..bdd4233f 100644 --- a/integration_tests/tests/pinata.rs +++ b/integration_tests/tests/pinata.rs @@ -54,6 +54,8 @@ async fn claim_pinata_to_uninitialized_public_account_fails_fast() -> Result<()> Command::Pinata(PinataProgramAgnosticSubcommand::Claim { to: Some(winner_account_id_formatted), to_label: None, + pin: None, + key_path: None, }), ) .await; @@ -109,6 +111,8 @@ async fn claim_pinata_to_uninitialized_private_account_fails_fast() -> Result<() Command::Pinata(PinataProgramAgnosticSubcommand::Claim { to: Some(winner_account_id_formatted), to_label: None, + pin: None, + key_path: None, }), ) .await; @@ -141,6 +145,8 @@ async fn claim_pinata_to_existing_public_account() -> Result<()> { let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { to: Some(format_public_account_id(ctx.existing_public_accounts()[0])), to_label: None, + pin: None, + key_path: None, }); let pinata_balance_pre = ctx @@ -182,6 +188,8 @@ async fn claim_pinata_to_existing_private_account() -> Result<()> { ctx.existing_private_accounts()[0], )), to_label: None, + pin: None, + key_path: None, }); let pinata_balance_pre = ctx @@ -247,6 +255,8 @@ async fn claim_pinata_to_new_private_account() -> Result<()> { let command = Command::AuthTransfer(AuthTransferSubcommand::Init { account_id: Some(winner_account_id_formatted.clone()), account_label: None, + pin: None, + key_path: None, }); wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; @@ -263,6 +273,8 @@ async fn claim_pinata_to_new_private_account() -> Result<()> { let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { to: Some(winner_account_id_formatted), to_label: None, + pin: None, + key_path: None, }); let pinata_balance_pre = ctx diff --git a/integration_tests/tests/token.rs b/integration_tests/tests/token.rs index e40e27c8..45e9a639 100644 --- a/integration_tests/tests/token.rs +++ b/integration_tests/tests/token.rs @@ -135,6 +135,8 @@ async fn create_and_transfer_public_token() -> Result<()> { to_npk: None, to_vpk: None, amount: transfer_amount, + from_pin: None, + from_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -180,6 +182,8 @@ async fn create_and_transfer_public_token() -> Result<()> { holder: Some(format_public_account_id(recipient_account_id)), holder_label: None, amount: burn_amount, + holder_pin: None, + holder_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -228,6 +232,8 @@ async fn create_and_transfer_public_token() -> Result<()> { holder_npk: None, holder_vpk: None, amount: mint_amount, + holder_pin: None, + holder_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -373,6 +379,8 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> { to_npk: None, to_vpk: None, amount: transfer_amount, + from_pin: None, + from_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -400,6 +408,8 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> { holder: Some(format_private_account_id(recipient_account_id)), holder_label: None, amount: burn_amount, + holder_pin: None, + holder_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -567,6 +577,8 @@ async fn create_token_with_private_definition() -> Result<()> { holder_npk: None, holder_vpk: None, amount: mint_amount_public, + holder_pin: None, + holder_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -615,6 +627,8 @@ async fn create_token_with_private_definition() -> Result<()> { holder_npk: None, holder_vpk: None, amount: mint_amount_private, + holder_pin: None, + holder_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -757,6 +771,8 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> { to_npk: None, to_vpk: None, amount: transfer_amount, + from_pin: None, + from_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -888,6 +904,8 @@ async fn shielded_token_transfer() -> Result<()> { to_npk: None, to_vpk: None, amount: transfer_amount, + from_pin: None, + from_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -1014,6 +1032,8 @@ async fn deshielded_token_transfer() -> Result<()> { to_npk: None, to_vpk: None, amount: transfer_amount, + from_pin: None, + from_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -1149,6 +1169,8 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> { holder_npk: Some(hex::encode(holder_keys.nullifier_public_key.0)), holder_vpk: Some(hex::encode(holder_keys.viewing_public_key.0)), amount: mint_amount, + holder_pin: None, + holder_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -1352,6 +1374,8 @@ async fn transfer_token_using_from_label() -> Result<()> { to_npk: None, to_vpk: None, amount: transfer_amount, + from_pin: None, + from_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; diff --git a/nssa/src/privacy_preserving_transaction/witness_set.rs b/nssa/src/privacy_preserving_transaction/witness_set.rs index 82b83079..2149ca0c 100644 --- a/nssa/src/privacy_preserving_transaction/witness_set.rs +++ b/nssa/src/privacy_preserving_transaction/witness_set.rs @@ -31,7 +31,11 @@ impl WitnessSet { } #[must_use] - pub fn from_list(proof: Proof, signatures: &Vec, public_keys: &Vec) -> Self { + pub fn from_list( + proof: Proof, + signatures: &Vec, + public_keys: &Vec, + ) -> Self { assert_eq!(signatures.len(), public_keys.len()); let signatures_and_public_keys = signatures diff --git a/python/keycard_wallet.py b/python/keycard_wallet.py index f1f27164..1a85af06 100644 --- a/python/keycard_wallet.py +++ b/python/keycard_wallet.py @@ -106,7 +106,7 @@ class KeycardWallet: public_only = True, keypath = path ) - # TODO (marvin) clean this up + public_key = public_key.public_key public_key = VerifyingKey.from_string(public_key[1:], curve=SECP256k1) public_key = public_key.to_string("compressed")[1:] @@ -117,11 +117,12 @@ class KeycardWallet: print(f"Error getting public key: {e}") return None + def sign_message_for_path(self, message: bytes = b"DefaultMessageTestDefaultMessage", path: str = "m/44'/60'/0'/0/0") -> bytes | None: try: if not self.card.is_secure_channel_open or not self.card.is_pin_verified: return None - + signature = self.card.sign_with_path( digest = message, path = path, diff --git a/wallet-ffi/src/transfer.rs b/wallet-ffi/src/transfer.rs index 739832ae..e65fa473 100644 --- a/wallet-ffi/src/transfer.rs +++ b/wallet-ffi/src/transfer.rs @@ -72,7 +72,7 @@ pub unsafe extern "C" fn wallet_ffi_transfer_public( let transfer = NativeTokenTransfer(&wallet); - match block_on(transfer.send_public_transfer(from_id, to_id, amount)) { + match block_on(transfer.send_public_transfer(from_id, to_id, amount, &None, &None)) { Ok(tx_hash) => { let tx_hash = CString::new(tx_hash.to_string()) .map_or(ptr::null_mut(), std::ffi::CString::into_raw); @@ -566,7 +566,7 @@ pub unsafe extern "C" fn wallet_ffi_register_public_account( let transfer = NativeTokenTransfer(&wallet); - match block_on(transfer.register_account(account_id)) { + match block_on(transfer.register_account(account_id, &None, &None)) { Ok(tx_hash) => { let tx_hash = CString::new(tx_hash.to_string()) .map_or(ptr::null_mut(), std::ffi::CString::into_raw); diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 4edb4523..8e94ec2f 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -40,3 +40,4 @@ async-stream.workspace = true indicatif = { version = "0.18.3", features = ["improved_unicode"] } optfield = "0.4.0" url.workspace = true +keycard_wallet.workspace = true \ No newline at end of file diff --git a/wallet/src/cli/account.rs b/wallet/src/cli/account.rs index 86ae7e35..cd03d25c 100644 --- a/wallet/src/cli/account.rs +++ b/wallet/src/cli/account.rs @@ -32,12 +32,15 @@ pub enum AccountSubcommand { short, long, conflicts_with = "account_label", - required_unless_present = "account_label" )] account_id: Option, /// Account label (alternative to --account-id). #[arg(long, conflicts_with = "account_id")] account_label: Option, + #[arg(long, conflicts_with = "account_id", conflicts_with = "account_id")] + pin: Option, + #[arg(long, conflicts_with = "account_id", conflicts_with = "account_id")] + key_path: Option, }, /// Produce new public or private account. #[command(subcommand)] @@ -191,12 +194,16 @@ impl WalletSubcommand for AccountSubcommand { keys, account_id, account_label, + pin, + key_path, } => { let resolved = resolve_id_or_label( account_id, account_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &pin, + &key_path, )?; let (account_id_str, addr_kind) = parse_addr_with_privacy_prefix(&resolved)?; @@ -407,6 +414,8 @@ impl WalletSubcommand for AccountSubcommand { account_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let (account_id_str, _) = parse_addr_with_privacy_prefix(&resolved)?; diff --git a/wallet/src/cli/keycard.rs b/wallet/src/cli/keycard.rs index 809d0e85..8c825458 100644 --- a/wallet/src/cli/keycard.rs +++ b/wallet/src/cli/keycard.rs @@ -1,10 +1,11 @@ use anyhow::Result; use clap::Subcommand; +use keycard_wallet::{KeycardWallet, python_path}; use pyo3::prelude::*; use crate::{ WalletCore, - cli::{SubcommandReturnValue, WalletSubcommand, keycard_wallet::KeycardWallet, python_path}, + cli::{SubcommandReturnValue, WalletSubcommand}, }; /// Represents generic chain CLI subcommand. diff --git a/wallet/src/cli/mod.rs b/wallet/src/cli/mod.rs index cade0a7c..1948db24 100644 --- a/wallet/src/cli/mod.rs +++ b/wallet/src/cli/mod.rs @@ -27,9 +27,7 @@ pub mod account; pub mod chain; pub mod config; pub mod keycard; -pub mod keycard_wallet; pub mod programs; -pub mod python_path; pub(crate) trait WalletSubcommand { async fn handle_subcommand(self, wallet_core: &mut WalletCore) diff --git a/wallet/src/cli/programs/amm.rs b/wallet/src/cli/programs/amm.rs index 0f8a0fff..b11efc78 100644 --- a/wallet/src/cli/programs/amm.rs +++ b/wallet/src/cli/programs/amm.rs @@ -51,7 +51,7 @@ pub enum AmmProgramAgnosticSubcommand { #[arg(long)] balance_a: u128, #[arg(long)] - balance_b: u128, + balance_b: u128, }, /// Swap specifying exact input amount. /// @@ -216,18 +216,24 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let user_holding_b = resolve_id_or_label( user_holding_b, user_holding_b_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let user_holding_lp = resolve_id_or_label( user_holding_lp, user_holding_lp_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; @@ -282,12 +288,16 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let user_holding_b = resolve_id_or_label( user_holding_b, user_holding_b_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; @@ -368,18 +378,24 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let user_holding_b = resolve_id_or_label( user_holding_b, user_holding_b_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let user_holding_lp = resolve_id_or_label( user_holding_lp, user_holding_lp_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; @@ -437,18 +453,24 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let user_holding_b = resolve_id_or_label( user_holding_b, user_holding_b_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let user_holding_lp = resolve_id_or_label( user_holding_lp, user_holding_lp_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; diff --git a/wallet/src/cli/programs/native_token_transfer.rs b/wallet/src/cli/programs/native_token_transfer.rs index d3f93f40..97ad46df 100644 --- a/wallet/src/cli/programs/native_token_transfer.rs +++ b/wallet/src/cli/programs/native_token_transfer.rs @@ -1,12 +1,13 @@ use anyhow::Result; use clap::Subcommand; use common::transaction::NSSATransaction; +use keycard_wallet::KeycardWallet; use nssa::AccountId; use crate::{ AccDecodeData::Decode, WalletCore, - cli::{SubcommandReturnValue, WalletSubcommand, keycard_wallet::KeycardWallet}, + cli::{SubcommandReturnValue, WalletSubcommand}, helperfunctions::{ AccountPrivacyKind, parse_addr_with_privacy_prefix, resolve_account_label, resolve_id_or_label, @@ -85,16 +86,15 @@ impl WalletSubcommand for AuthTransferSubcommand { pin, key_path, } => { - let resolved = if pin.is_none() { - resolve_id_or_label( + // TODO: I'm not sure if the string is correct... + let resolved = resolve_id_or_label( account_id, account_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - )? - } else { - String::default() - }; + &pin, + &key_path + )?; let (account_id, addr_privacy) = if pin.is_none() { parse_addr_with_privacy_prefix(&resolved)? @@ -104,10 +104,14 @@ impl WalletSubcommand for AuthTransferSubcommand { match addr_privacy { AccountPrivacyKind::Public => { + // TODO: crucial (Marvin) let account_id = if pin.is_none() { account_id.parse()? } else { - KeycardWallet::get_account_id_for_path_with_connect(&pin.as_ref().expect("TODO"), &key_path.as_ref().expect("TODO")) + KeycardWallet::get_account_id_for_path_with_connect( + &pin.as_ref().expect("TODO"), + &key_path.as_ref().expect("TODO"), + ) }; let tx_hash = NativeTokenTransfer(wallet_core) @@ -159,18 +163,15 @@ impl WalletSubcommand for AuthTransferSubcommand { pin, key_path, } => { - let from = if pin.is_none() { - resolve_id_or_label( - from, - from_label, - &wallet_core.storage.labels, - &wallet_core.storage.user_data, - )? - } else { - KeycardWallet::get_account_id_for_path_with_connect(&pin.as_ref().expect("TODO"), &key_path.as_ref().expect("TODO")).to_string() - }; + let from = resolve_id_or_label( + from, + from_label, + &wallet_core.storage.labels, + &wallet_core.storage.user_data, + &pin, + &key_path, + )?; - let to = match (to, to_label) { (v, None) => v, (None, Some(label)) => Some(resolve_account_label( @@ -202,7 +203,13 @@ impl WalletSubcommand for AuthTransferSubcommand { match (from_privacy, to_privacy) { (AccountPrivacyKind::Public, AccountPrivacyKind::Public) => { - NativeTokenTransferProgramSubcommand::Public { from, to, amount, pin, key_path } + NativeTokenTransferProgramSubcommand::Public { + from, + to, + amount, + pin, + key_path, + } } (AccountPrivacyKind::Private, AccountPrivacyKind::Private) => { NativeTokenTransferProgramSubcommand::Private( @@ -255,7 +262,7 @@ impl WalletSubcommand for AuthTransferSubcommand { to_vpk, amount, pin, - key_path + key_path, }, ) } @@ -474,7 +481,13 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { wallet_core: &mut WalletCore, ) -> Result { match self { - Self::ShieldedOwned { from, to, amount , pin: _, key_path: _} => { + Self::ShieldedOwned { + from, + to, + amount, + pin: _, + key_path: _, + } => { let from: AccountId = from.parse().unwrap(); let to: AccountId = to.parse().unwrap(); @@ -505,7 +518,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { to_vpk, amount, pin: _, - key_path: _ + key_path: _, } => { let from: AccountId = from.parse().unwrap(); @@ -571,8 +584,13 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } - Self::Public { from, to, amount, pin, key_path } => { - + Self::Public { + from, + to, + amount, + pin, + key_path, + } => { let from: AccountId = from.parse().unwrap(); let to: AccountId = to.parse().unwrap(); diff --git a/wallet/src/cli/programs/pinata.rs b/wallet/src/cli/programs/pinata.rs index 6171a0f2..793797d3 100644 --- a/wallet/src/cli/programs/pinata.rs +++ b/wallet/src/cli/programs/pinata.rs @@ -26,6 +26,10 @@ pub enum PinataProgramAgnosticSubcommand { /// To account label (alternative to --to). #[arg(long, conflicts_with = "to")] to_label: Option, + #[arg(long, conflicts_with = "to", conflicts_with = "to_label")] + pin: Option, + #[arg(long, conflicts_with = "to", conflicts_with = "to_label")] + key_path: Option, }, } @@ -35,15 +39,19 @@ impl WalletSubcommand for PinataProgramAgnosticSubcommand { wallet_core: &mut WalletCore, ) -> Result { let underlying_subcommand = match self { - Self::Claim { to, to_label } => { + Self::Claim { to, to_label , pin, key_path} => { + let to = resolve_id_or_label( to, to_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &pin, + &key_path, )?; + let (to, to_addr_privacy) = parse_addr_with_privacy_prefix(&to)?; - + // TODO: (Marvin) does privacy get marked correctly? match to_addr_privacy { AccountPrivacyKind::Public => { PinataProgramSubcommand::Public(PinataProgramSubcommandPublic::Claim { diff --git a/wallet/src/cli/programs/token.rs b/wallet/src/cli/programs/token.rs index 0575da09..47e526a0 100644 --- a/wallet/src/cli/programs/token.rs +++ b/wallet/src/cli/programs/token.rs @@ -76,6 +76,10 @@ pub enum TokenProgramAgnosticSubcommand { /// amount - amount of balance to move. #[arg(long)] amount: u128, + #[arg(long, conflicts_with = "from", conflicts_with = "from_label")] + from_pin: Option, + #[arg(long, conflicts_with = "from", conflicts_with = "from_label")] + from_key_path: Option, }, /// Burn tokens on `holder`, modify `definition`. /// @@ -107,6 +111,10 @@ pub enum TokenProgramAgnosticSubcommand { /// amount - amount of balance to burn. #[arg(long)] amount: u128, + #[arg(long, conflicts_with = "holder", conflicts_with = "holder_label")] + holder_pin: Option, + #[arg(long, conflicts_with = "holder", conflicts_with = "holder_label")] + holder_key_path: Option, }, /// Mint tokens on `holder`, modify `definition`. /// @@ -142,6 +150,10 @@ pub enum TokenProgramAgnosticSubcommand { /// amount - amount of balance to mint. #[arg(long)] amount: u128, + #[arg(long, conflicts_with = "holder", conflicts_with = "holder_label")] + holder_pin: Option, + #[arg(long, conflicts_with = "holder", conflicts_with = "holder_label")] + holder_key_path: Option, }, } @@ -164,12 +176,16 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { definition_account_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let supply_account_id = resolve_id_or_label( supply_account_id, supply_account_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let (definition_account_id, definition_addr_privacy) = parse_addr_with_privacy_prefix(&definition_account_id)?; @@ -229,12 +245,16 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { to_npk, to_vpk, amount, + from_pin, + from_key_path } => { let from = resolve_id_or_label( from, from_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &from_pin, + &from_key_path, )?; let to = match (to, to_label) { (v, None) => v, @@ -264,7 +284,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { (Some(to), None, None) => { let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?; let (to, to_privacy) = parse_addr_with_privacy_prefix(&to)?; - + // TODO: (Marvin) return here match (from_privacy, to_privacy) { (AccountPrivacyKind::Public, AccountPrivacyKind::Public) => { TokenProgramSubcommand::Public( @@ -336,24 +356,30 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { holder, holder_label, amount, + holder_pin, + holder_key_path, } => { let definition = resolve_id_or_label( definition, definition_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; let holder = resolve_id_or_label( holder, holder_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &holder_pin, + &holder_key_path, )?; let underlying_subcommand = { let (definition, definition_privacy) = parse_addr_with_privacy_prefix(&definition)?; let (holder, holder_privacy) = parse_addr_with_privacy_prefix(&holder)?; - + // TODO Marvin return here match (definition_privacy, holder_privacy) { (AccountPrivacyKind::Public, AccountPrivacyKind::Public) => { TokenProgramSubcommand::Public( @@ -404,24 +430,26 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { holder_npk, holder_vpk, amount, + holder_pin, + holder_key_path } => { let definition = resolve_id_or_label( definition, definition_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, + &None, + &None, )?; - let holder = match (holder, holder_label) { - (v, None) => v, - (None, Some(label)) => Some(resolve_account_label( - &label, - &wallet_core.storage.labels, - &wallet_core.storage.user_data, - )?), - (Some(_), Some(_)) => { - anyhow::bail!("Provide only one of --holder or --holder-label") - } - }; + let holder = Some(resolve_id_or_label( + holder.clone(), + holder_label.clone(), + &wallet_core.storage.labels, + &wallet_core.storage.user_data, + &holder_pin, + &holder_key_path, + )?); + //TODO return here (Marvin) let underlying_subcommand = match (holder, holder_npk, holder_vpk) { (None, None, None) => { anyhow::bail!( diff --git a/wallet/src/helperfunctions.rs b/wallet/src/helperfunctions.rs index 3e304253..e173eefc 100644 --- a/wallet/src/helperfunctions.rs +++ b/wallet/src/helperfunctions.rs @@ -8,6 +8,8 @@ use nssa_core::account::Nonce; use rand::{RngCore as _, rngs::OsRng}; use serde::Serialize; use testnet_initial_state::{PrivateAccountPrivateInitialData, PublicAccountPrivateInitialData}; +use keycard_wallet::KeycardWallet; + use crate::{ HOME_DIR_ENV_VAR, @@ -60,10 +62,13 @@ pub fn resolve_id_or_label( label: Option, labels: &HashMap, user_data: &NSSAUserData, + pin: &Option, + key_path: &Option, ) -> Result { - match (id, label) { - (Some(id), None) => Ok(id), - (None, Some(label)) => resolve_account_label(&label, labels, user_data), + match (id, label, pin) { + (Some(id), None, None) => Ok(id), + (None, Some(label), None) => resolve_account_label(&label, labels, user_data), + (None, None, Some(pin)) => Ok(KeycardWallet::get_account_id_for_path_with_connect(pin, key_path.as_ref().expect("TODO")).to_string()), _ => anyhow::bail!("provide exactly one of account id or account label"), } } diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 533f4b48..75f5cb4f 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -16,13 +16,14 @@ use chain_storage::WalletChainStore; use common::{HashType, transaction::NSSATransaction}; use config::WalletConfig; use key_protocol::key_management::key_tree::{chain_index::ChainIndex, traits::KeyNode as _}; +use keycard_wallet::KeycardWallet; use log::info; use nssa::{ - PublicKey, - Account, AccountId, PrivacyPreservingTransaction, Signature, privacy_preserving_transaction::{ + Account, AccountId, PrivacyPreservingTransaction, PublicKey, Signature, + privacy_preserving_transaction::{ circuit::{ProgramWithDependencies, Proof}, message::EncryptedAccountData, - } + }, }; use nssa_core::{ Commitment, MembershipProof, SharedSecretKey, account::Nonce, program::InstructionData, @@ -32,7 +33,9 @@ use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuil use tokio::io::AsyncWriteExt as _; use crate::{ - cli::keycard_wallet::KeycardWallet, config::{PersistentStorage, WalletConfigOverrides}, helperfunctions::produce_data_for_storage, poller::TxPoller + config::{PersistentStorage, WalletConfigOverrides}, + helperfunctions::produce_data_for_storage, + poller::TxPoller, }; pub mod chain_storage; @@ -587,23 +590,28 @@ impl WalletCore { message: &nssa::privacy_preserving_transaction::Message, proof: Proof, pin: &String, - key_paths: &[String] + key_paths: &[String], ) -> Result { let mut signatures = Vec::::new(); let mut public_keys = Vec::::new(); - let message_bytes: [u8; 32] = { - let v = message.to_bytes(); - let mut bytes = [0_u8; 32]; - let len = v.len().min(32); - bytes[..len].copy_from_slice(&v[..len]); - bytes - }; + let message_bytes: [u8; 32] = { + let v = message.to_bytes(); + let mut bytes = [0_u8; 32]; + let len = v.len().min(32); + bytes[..len].copy_from_slice(&v[..len]); + bytes + }; for path in key_paths.iter() { - public_keys.push( KeycardWallet::get_public_key_for_path_with_connect(&pin, &path)); - signatures.push( KeycardWallet::sign_message_for_path_with_connection(&pin, &path, &message_bytes).expect("Expect a valid signature")); + public_keys.push(KeycardWallet::get_public_key_for_path_with_connect( + &pin, &path, + )); + signatures.push( + KeycardWallet::sign_message_for_path_with_connect(&pin, &path, &message_bytes) + .expect("Expect a valid signature"), + ); } Ok( diff --git a/wallet/src/program_facades/native_token_transfer/public.rs b/wallet/src/program_facades/native_token_transfer/public.rs index 97d1609c..0add4ee2 100644 --- a/wallet/src/program_facades/native_token_transfer/public.rs +++ b/wallet/src/program_facades/native_token_transfer/public.rs @@ -1,17 +1,14 @@ use common::{HashType, transaction::NSSATransaction}; +use keycard_wallet::KeycardWallet; use nssa::{ AccountId, PublicTransaction, program::Program, public_transaction::{Message, WitnessSet}, }; -use pyo3::Python; use sequencer_service_rpc::RpcClient as _; use super::NativeTokenTransfer; -use crate::{ - ExecutionFailureKind, WalletCore, - cli::{keycard_wallet::KeycardWallet, python_path}, -}; +use crate::{ExecutionFailureKind, WalletCore}; impl NativeTokenTransfer<'_> { pub async fn send_public_transfer( @@ -59,21 +56,22 @@ impl NativeTokenTransfer<'_> { Message::try_new(program_id, account_ids, nonces, balance_to_move).unwrap(); let witness_set = if pin.is_none() { - WalletCore::sign_public_message(self.0, &message, &sign_ids) - .expect("Expect a valid signature") - } else { - // TODO: maybe the issue? (Marvin) - let message_bytes: [u8; 32] = { - let v = message.to_bytes(); - let mut bytes = [0_u8; 32]; - let len = v.len().min(32); - bytes[..len].copy_from_slice(&v[..len]); - bytes - }; - let pub_key = KeycardWallet::get_public_key_for_path_with_connect(&pin.as_ref().expect("TODO"), &key_path.as_ref().expect("TODO")); - let signature = KeycardWallet::sign_message_for_path_with_connection(&pin.as_ref().expect("TODO"), &key_path.as_ref().expect("TODO"), &message_bytes).expect("Expect valid signature"); - WitnessSet::from_list(&[signature], &[pub_key]) - }; + WalletCore::sign_public_message(self.0, &message, &sign_ids) + .expect("Expect a valid signature") + } else { + + let pub_key = KeycardWallet::get_public_key_for_path_with_connect( + &pin.as_ref().expect("TODO"), + &key_path.as_ref().expect("TODO"), + ); + let signature = KeycardWallet::sign_message_for_path_with_connect( + &pin.as_ref().expect("TODO"), + &key_path.as_ref().expect("TODO"), + &message.hash_message(), + ) + .expect("Expect valid signature"); + WitnessSet::from_list(&[signature], &[pub_key]) + }; let tx = PublicTransaction::new(message, witness_set); @@ -102,16 +100,8 @@ impl NativeTokenTransfer<'_> { let instruction: u128 = 0; let account_ids = vec![from]; let program_id = Program::authenticated_transfer_program().id(); - let message = Message::try_new(program_id, account_ids, nonces, instruction).unwrap(); + let message = Message::try_new(program_id, account_ids, nonces, instruction).expect("Expect a valid Message"); - // (Marvin): This really needs to be the ChainIndex - // But, I cannot change that due to Default Accounts. - // Instead, I had introduced a "NEW" sign...which I do not see... - // Correction: I did not need a specific function. Rather, I use `from_list` to combine - // public and signatures together for a WitnessSet. - - // The tricky part is that I NEED to do everything with chain-codes... This won't look nice, - // but is feasible. let witness_set = if pin.is_none() { let signing_key = self.0.storage.user_data.get_pub_account_signing_key(from); @@ -121,42 +111,18 @@ impl NativeTokenTransfer<'_> { WitnessSet::for_message(&message, &[signing_key]) } else { - let witness_set = Python::with_gil(|py| { - python_path::add_python_path(py).expect("keycard_wallet.py not found"); + let pub_key = KeycardWallet::get_public_key_for_path_with_connect( + pin.as_ref().expect("TODO"), + key_path.as_ref().expect("TODO"), + ); - let wallet = KeycardWallet::new(py).expect("Expect keycard wallet"); - - let is_connected = wallet - .setup_communication(py, pin.as_ref().expect("TODO")) - .expect("Expect a Boolean."); - - if is_connected { - println!("\u{2705} Keycard is now connected to wallet."); - } else { - println!("\u{274c} Keycard is not connected to wallet."); - } - // TODO: maybe the issue? (Marvin) - let message: [u8; 32] = { - let v = message.to_bytes(); - let mut bytes = [0_u8; 32]; - let len = v.len().min(32); - bytes[..len].copy_from_slice(&v[..len]); - bytes - }; - - let pub_key = wallet - .get_public_key_for_path(py, key_path.as_ref().expect("TODO")) - .expect("Expect a valid public key"); - - let signature = wallet - .sign_message_for_path(py, key_path.as_ref().expect("TODO"), &message) - .expect("TODO"); - - let _ = wallet.disconnect(py); - - WitnessSet::from_list(&[signature], &[pub_key]) - }); - witness_set + let signature = KeycardWallet::sign_message_for_path_with_connect( + pin.as_ref().as_ref().expect("TODO"), + key_path.as_ref().expect("TODO"), + &message.hash_message(), + ) + .expect("Expect a valid Signature."); + WitnessSet::from_list(&[signature], &[pub_key]) }; let tx = PublicTransaction::new(message, witness_set); diff --git a/wallet_with_keycard.sh b/wallet_with_keycard.sh index e1d96d33..f90d8370 100644 --- a/wallet_with_keycard.sh +++ b/wallet_with_keycard.sh @@ -10,7 +10,7 @@ python3 -m pip install pyaes cd python -git clone --branch lee-schnorr --single-branch https://github.com/bitgamma/keycard-py.git +# git clone --branch lee-schnorr --single-branch https://github.com/bitgamma/keycard-py.git cd keycard-py python3 -m venv venv source venv/bin/activate