diff --git a/Cargo.lock b/Cargo.lock index 73fbb12a..959d921a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4022,6 +4022,7 @@ version = "0.1.0" dependencies = [ "log", "nssa", + "nssa_core", "pyo3", ] diff --git a/examples/program_deployment/src/bin/run_hello_world_private.rs b/examples/program_deployment/src/bin/run_hello_world_private.rs index 78c11656..148addb3 100644 --- a/examples/program_deployment/src/bin/run_hello_world_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_private.rs @@ -53,7 +53,6 @@ async fn main() { Program::serialize_instruction(greeting).unwrap(), &program.into(), &None, - &None, ) .await .unwrap(); diff --git a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs index e25c4b0e..eb9d3b4c 100644 --- a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs @@ -61,7 +61,6 @@ async fn main() { Program::serialize_instruction(instruction).unwrap(), &program_with_dependencies, &None, - &None, ) .await .unwrap(); diff --git a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs index 89763bd7..81594ac3 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs @@ -107,7 +107,6 @@ async fn main() { Program::serialize_instruction(instruction).unwrap(), &program.into(), &None, - &None, ) .await .unwrap(); @@ -150,7 +149,6 @@ async fn main() { Program::serialize_instruction(instruction).unwrap(), &program.into(), &None, - &None, ) .await .unwrap(); diff --git a/integration_tests/tests/amm.rs b/integration_tests/tests/amm.rs index d1e4f8ee..2d5a7132 100644 --- a/integration_tests/tests/amm.rs +++ b/integration_tests/tests/amm.rs @@ -136,6 +136,7 @@ async fn amm_public() -> Result<()> { to_identifier: Some(0), amount: 7, from_key_path: None, + to_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -167,6 +168,7 @@ async fn amm_public() -> Result<()> { to_identifier: Some(0), amount: 7, from_key_path: None, + to_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -557,6 +559,7 @@ async fn amm_new_pool_using_labels() -> Result<()> { to_identifier: Some(0), amount: 5, from_key_path: None, + to_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; @@ -583,6 +586,7 @@ async fn amm_new_pool_using_labels() -> Result<()> { to_identifier: Some(0), amount: 5, from_key_path: None, + to_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 057b7817..06b0ac3d 100644 --- a/integration_tests/tests/ata.rs +++ b/integration_tests/tests/ata.rs @@ -271,6 +271,7 @@ async fn transfer_and_burn_via_ata() -> Result<()> { to_identifier: Some(0), amount: fund_amount, from_key_path: None, + to_key_path: None, }), ) .await?; @@ -505,6 +506,7 @@ async fn transfer_via_ata_private_owner() -> Result<()> { to_identifier: Some(0), amount: fund_amount, from_key_path: None, + to_key_path: None, }), ) .await?; @@ -621,6 +623,7 @@ async fn burn_via_ata_private_owner() -> Result<()> { to_identifier: Some(0), amount: fund_amount, from_key_path: None, + to_key_path: None, }), ) .await?; diff --git a/integration_tests/tests/token.rs b/integration_tests/tests/token.rs index 0dc3382a..8c3023f5 100644 --- a/integration_tests/tests/token.rs +++ b/integration_tests/tests/token.rs @@ -137,6 +137,7 @@ async fn create_and_transfer_public_token() -> Result<()> { to_identifier: Some(0), amount: transfer_amount, from_key_path: None, + to_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -379,6 +380,7 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> { to_identifier: Some(0), amount: transfer_amount, from_key_path: None, + to_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -768,6 +770,7 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> { to_identifier: Some(0), amount: transfer_amount, from_key_path: None, + to_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -901,6 +904,7 @@ async fn shielded_token_transfer() -> Result<()> { to_identifier: Some(0), amount: transfer_amount, from_key_path: None, + to_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -1029,6 +1033,7 @@ async fn deshielded_token_transfer() -> Result<()> { to_identifier: Some(0), amount: transfer_amount, from_key_path: None, + to_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; @@ -1369,6 +1374,7 @@ async fn transfer_token_using_from_label() -> Result<()> { to_identifier: Some(0), amount: transfer_amount, from_key_path: None, + to_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; diff --git a/keycard_tests.sh b/keycard_tests.sh index 73a5e213..09fb4268 100644 --- a/keycard_tests.sh +++ b/keycard_tests.sh @@ -1,39 +1,40 @@ # Run wallet_with_keycard.sh first source venv/bin/activate # Load the appropriate virtual environment +export KEYCARD_PIN=111111 # Tests wallet keycard available # - Checks whether smart reader and keycard are both available. echo "Test: wallet keycard available" wallet keycard available -echo 'Test: wallet keycard load --pin 111111 --mnemonic "final empty hair duty next drastic normal miss wreck wreck strategy omit"' +echo 'Test: wallet keycard load --mnemonic "final empty hair duty next drastic normal miss wreck wreck strategy omit"' # Install a new mnemonic phrase to keycard -wallet keycard load --pin 111111 --mnemonic "fashion degree mountain wool question damp current pond grow dolphin chronic then" +wallet keycard load --mnemonic "fashion degree mountain wool question damp current pond grow dolphin chronic then" # Commented out to avoid resetting card constantly -echo "Test: wallet auth-transfer --pin 111111 --key-path \"m/44'/60/0\'/0/0\"" -wallet auth-transfer init --pin 111111 --key-path "m/44'/60'/0'/0/0" +echo "Test: wallet auth-transfer --key-path \"m/44'/60/0\'/0/0\"" +wallet auth-transfer init --key-path "m/44'/60'/0'/0/0" -echo "Test: wallet account get --pin 111111 --key-path \"m/44'/60'/0'/0/0\"" -wallet account get --pin 111111 --key-path "m/44'/60'/0'/0/0" +echo "Test: wallet account get --key-path \"m/44'/60'/0'/0/0\"" +wallet account get --key-path "m/44'/60'/0'/0/0" -echo "Test: wallet pinata claim --pin 111111 --key-path \"m/44'/60'/0'/0/0\"" -wallet pinata claim --pin 111111 --key-path "m/44'/60'/0'/0/0" +echo "Test: wallet pinata claim --key-path \"m/44'/60'/0'/0/0\"" +wallet pinata claim --key-path "m/44'/60'/0'/0/0" -echo "Test: wallet account get --pin 111111 --key-path \"m/44'/60'/0'/0/0\"" -wallet account get --pin 111111 --key-path "m/44'/60'/0'/0/0" +echo "Test: wallet account get --key-path \"m/44'/60'/0'/0/0\"" +wallet account get --key-path "m/44'/60'/0'/0/0" echo "Initialize new account (auth-transfer init) and send" -wallet auth-transfer init --pin 111111 --key-path "m/44'/60'/0'/0/1" -wallet auth-transfer send --amount 40 --pin 111111 --from-key-path "m/44'/60'/0'/0/0" --to-key-path "m/44'/60'/0'/0/1" +wallet auth-transfer init --key-path "m/44'/60'/0'/0/1" +wallet auth-transfer send --amount 40 --from-key-path "m/44'/60'/0'/0/0" --to-key-path "m/44'/60'/0'/0/1" -echo "Test: wallet account get --pin 111111 --key-path \"m/44'/60'/0'/0/0\"" -wallet account get --pin 111111 --key-path "m/44'/60'/0'/0/0" +echo "Test: wallet account get --key-path \"m/44'/60'/0'/0/0\"" +wallet account get --key-path "m/44'/60'/0'/0/0" -echo "Test: wallet account get --pin 111111 --key-path \"m/44'/60'/0'/0/1\"" -wallet account get --pin 111111 --key-path "m/44'/60'/0'/0/1" +echo "Test: wallet account get --key-path \"m/44'/60'/0'/0/1\"" +wallet account get --key-path "m/44'/60'/0'/0/1" @@ -58,5 +59,5 @@ echo "Token B" # This fails due to lack of initialization for Token Account #echo "Transfer 1" -wallet auth-transfer send --amount 40 --pin 111111 --from-key-path "m/44'/60'/0'/0/0" --to-npk "55204e2934045b044f06d8222b454d46b54788f33c7dec4f6733d441703bb0e6" --to-vpk "02a8626b0c0ad9383c5678dad48c3969b4174fb377cdb03a6259648032c774cec8" +wallet auth-transfer send --amount 40 --from-key-path "m/44'/60'/0'/0/0" --to-npk "55204e2934045b044f06d8222b454d46b54788f33c7dec4f6733d441703bb0e6" --to-vpk "02a8626b0c0ad9383c5678dad48c3969b4174fb377cdb03a6259648032c774cec8" echo "Transfer 2" diff --git a/keycard_wallet/Cargo.toml b/keycard_wallet/Cargo.toml index d8299d44..b773ad95 100644 --- a/keycard_wallet/Cargo.toml +++ b/keycard_wallet/Cargo.toml @@ -9,5 +9,6 @@ workspace = true [dependencies] nssa.workspace = true +nssa_core.workspace = true pyo3.workspace = true log.workspace = true \ No newline at end of file diff --git a/keycard_wallet/src/lib.rs b/keycard_wallet/src/lib.rs index 37b010fd..c27ecd1c 100644 --- a/keycard_wallet/src/lib.rs +++ b/keycard_wallet/src/lib.rs @@ -1,4 +1,5 @@ use nssa::{AccountId, PublicKey, Signature}; +use nssa_core::NullifierPublicKey; use pyo3::{prelude::*, types::PyAny}; pub mod python_path; @@ -55,7 +56,7 @@ impl KeycardWallet { PublicKey::try_new(public_key) .map_err(|e| PyErr::new::(e.to_string())) - } + } pub fn get_public_key_for_path_with_connect(pin: &str, path: &str) -> PyResult { Python::with_gil(|py| { @@ -141,9 +142,68 @@ impl KeycardWallet { Ok(()) } - pub fn get_account_id_for_path_with_connect(pin: &str, key_path: &str) -> PyResult { + pub fn get_public_account_id_for_path_with_connect(pin: &str, key_path: &str) -> PyResult { let public_key = Self::get_public_key_for_path_with_connect(pin, key_path)?; Ok(format!("Public/{}", AccountId::from(&public_key))) } + + pub fn get_private_keys_for_path( + &self, + py: Python, + path: &str, + ) -> PyResult<([u8; 32], [u8; 32])> { + let (raw_nsk, raw_vsk): (Vec, Vec) = self + .instance + .bind(py) + .call_method1("get_private_keys_for_path", (path,))? + .extract()?; + + let nsk: [u8; 32] = raw_nsk.try_into().map_err(|vec: Vec| { + PyErr::new::(format!( + "expected 32-byte NSK from keycard, got {} bytes", + vec.len() + )) + })?; + + let vsk: [u8; 32] = raw_vsk.try_into().map_err(|vec: Vec| { + PyErr::new::(format!( + "expected 32-byte VSK from keycard, got {} bytes", + vec.len() + )) + })?; + + Ok((nsk, vsk)) + } + + pub fn get_private_keys_for_path_with_connect( + pin: &str, + path: &str, + ) -> PyResult<([u8; 32], [u8; 32])> { + Python::with_gil(|py| { + python_path::add_python_path(py)?; + + let wallet = Self::new(py)?; + + let is_connected = wallet.setup_communication(py, pin)?; + + if is_connected { + log::info!("\u{2705} Keycard is now connected to wallet."); + } else { + log::info!("\u{274c} Keycard is not connected to wallet."); + } + + let result = wallet.get_private_keys_for_path(py, path); + + drop(wallet.disconnect(py)); + result + }) + } + + pub fn get_private_account_id_for_path_with_connect(pin: &str, key_path: &str) -> PyResult { + let (nsk, _vsk) = Self::get_private_keys_for_path_with_connect(pin, key_path)?; + let npk = NullifierPublicKey::from(&nsk); + + Ok(format!("Private/{}", AccountId::from((&npk, 0_u128)))) + } } diff --git a/nssa/src/privacy_preserving_transaction/message.rs b/nssa/src/privacy_preserving_transaction/message.rs index 01e6e04f..86519860 100644 --- a/nssa/src/privacy_preserving_transaction/message.rs +++ b/nssa/src/privacy_preserving_transaction/message.rs @@ -122,7 +122,7 @@ impl Message { } #[must_use] - pub fn hash_message(&self) -> [u8; 32] { + pub fn hash(&self) -> [u8; 32] { let mut bytes = Vec::with_capacity( PREFIX .len() @@ -226,7 +226,7 @@ pub mod tests { assert_eq!( borsh::to_vec(&msg).unwrap(), expected_borsh, - "`privacy_preserving_transaction::hash_message()`: expected borsh order has changed" + "`privacy_preserving_transaction::hash()`: expected borsh order has changed" ); let mut preimage = Vec::with_capacity(PREFIX.len() + expected_borsh.len()); @@ -235,9 +235,9 @@ pub mod tests { let expected_hash: [u8; 32] = Sha256::digest(&preimage).into(); assert_eq!( - msg.hash_message(), + msg.hash(), expected_hash, - "`privacy_preserving_transaction::hash_message()`: serialization has changed" + "`privacy_preserving_transaction::hash()`: serialization has changed" ); } diff --git a/nssa/src/privacy_preserving_transaction/witness_set.rs b/nssa/src/privacy_preserving_transaction/witness_set.rs index 43a36671..20f4c7ca 100644 --- a/nssa/src/privacy_preserving_transaction/witness_set.rs +++ b/nssa/src/privacy_preserving_transaction/witness_set.rs @@ -15,7 +15,7 @@ impl WitnessSet { #[must_use] // TODO: swap for Keycard signing path. pub fn for_message(message: &Message, proof: Proof, private_keys: &[&PrivateKey]) -> Self { - let message_hash = message.hash_message(); + let message_hash = message.hash(); let signatures_and_public_keys = private_keys .iter() .map(|&key| { @@ -49,7 +49,7 @@ impl WitnessSet { #[must_use] pub fn signatures_are_valid_for(&self, message: &Message) -> bool { - let message_hash = message.hash_message(); + let message_hash = message.hash(); for (signature, public_key) in self.signatures_and_public_keys() { if !signature.is_valid_for(&message_hash, public_key) { return false; diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index f71fb372..792f348f 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -68,7 +68,7 @@ impl Message { } #[must_use] - pub fn hash_message(&self) -> [u8; 32] { + pub fn hash(&self) -> [u8; 32] { let mut bytes = Vec::with_capacity( PREFIX .len() @@ -122,7 +122,7 @@ mod tests { assert_eq!( borsh::to_vec(&msg).unwrap(), expected_borsh, - "`public_transaction::hash_message()`: expected borsh order has changed" + "`public_transaction::hash()`: expected borsh order has changed" ); let mut preimage = Vec::with_capacity(PREFIX.len() + expected_borsh.len()); @@ -131,9 +131,9 @@ mod tests { let expected_hash: [u8; 32] = Sha256::digest(&preimage).into(); assert_eq!( - msg.hash_message(), + msg.hash(), expected_hash, - "`public_transaction::hash_message()`: serialization has changed" + "`public_transaction::hash()`: serialization has changed" ); } } diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 7a32c0ea..4cdf8f9a 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -19,7 +19,7 @@ impl WitnessSet { )); } - let message_hash = message.hash_message(); + let message_hash = message.hash(); let signatures_and_public_keys = signatures .iter() .zip(pub_keys.iter()) @@ -41,7 +41,7 @@ impl WitnessSet { #[must_use] pub fn for_message(message: &Message, private_keys: &[&PrivateKey]) -> Self { - let message_hash = message.hash_message(); + let message_hash = message.hash(); let signatures_and_public_keys = private_keys .iter() .map(|&key| { @@ -58,7 +58,7 @@ impl WitnessSet { #[must_use] pub fn is_valid_for(&self, message: &Message) -> bool { - let message_hash = message.hash_message(); + let message_hash = message.hash(); for (signature, public_key) in self.signatures_and_public_keys() { if !signature.is_valid_for(&message_hash, public_key) { return false; @@ -173,7 +173,7 @@ mod tests { assert_eq!(witness_set.signatures_and_public_keys.len(), 2); - let message_bytes = message.hash_message(); + let message_bytes = message.hash(); for ((signature, public_key), expected_public_key) in witness_set .signatures_and_public_keys .into_iter() diff --git a/python/keycard_wallet.py b/python/keycard_wallet.py index 7d7e5bd3..33875ca7 100644 --- a/python/keycard_wallet.py +++ b/python/keycard_wallet.py @@ -3,7 +3,7 @@ from keycard.exceptions import APDUError, TransportError from ecdsa import VerifyingKey, SECP256k1 from keycard.keycard import KeyCard - +from keycard.commands.export_lee_key import export_lee_key from mnemonic import Mnemonic from keycard import constants @@ -122,4 +122,25 @@ class KeycardWallet: except Exception as e: print(f"Error signing message: {e}") + return None + + def get_private_keys_for_path(self, 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 + + private_keys = export_lee_key( + self.card, + constants.DerivationOption.DERIVE, + path + ) + + nsk = private_keys.lee_nsk + vsk = private_keys.lee_vsk + + return (nsk, vsk) + + except Exception as e: + import traceback + traceback.print_exc() return None \ No newline at end of file diff --git a/wallet-ffi/src/transfer.rs b/wallet-ffi/src/transfer.rs index 88b8c963..713310dd 100644 --- a/wallet-ffi/src/transfer.rs +++ b/wallet-ffi/src/transfer.rs @@ -139,7 +139,6 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded( to_keys: *const FfiPrivateAccountKeys, to_identifier: *const FfiU128, amount: *const [u8; 16], - pin: *const c_char, key_path: *const c_char, out_result: *mut FfiTransferResult, ) -> WalletFfiError { @@ -177,16 +176,18 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded( }; let to_identifier = u128::from_le_bytes(unsafe { (*to_identifier).data }); let amount = u128::from_le_bytes(unsafe { *amount }); - let pin = optional_c_str(pin); let key_path = optional_c_str(key_path); let transfer = NativeTokenTransfer(&wallet); - match block_on( - transfer.send_shielded_transfer_to_outer_account( - from_id, to_npk, to_vpk, amount, &pin, &key_path, - ), - ) { + match block_on(transfer.send_shielded_transfer_to_outer_account( + from_id, + to_npk, + to_vpk, + to_identifier, + amount, + &key_path, + )) { Ok((tx_hash, _shared_key)) => { let tx_hash = CString::new(tx_hash.to_string()) .map_or(ptr::null_mut(), std::ffi::CString::into_raw); @@ -240,7 +241,6 @@ pub unsafe extern "C" fn wallet_ffi_transfer_deshielded( from: *const FfiBytes32, to: *const FfiBytes32, amount: *const [u8; 16], - pin: *const c_char, key_path: *const c_char, out_result: *mut FfiTransferResult, ) -> WalletFfiError { @@ -265,12 +265,11 @@ pub unsafe extern "C" fn wallet_ffi_transfer_deshielded( let from_id = AccountId::new(unsafe { (*from).data }); let to_id = AccountId::new(unsafe { (*to).data }); let amount = u128::from_le_bytes(unsafe { *amount }); - let pin = optional_c_str(pin); let key_path = optional_c_str(key_path); let transfer = NativeTokenTransfer(&wallet); - match block_on(transfer.send_deshielded_transfer(from_id, to_id, amount, &pin, &key_path)) { + match block_on(transfer.send_deshielded_transfer(from_id, to_id, amount, &key_path)) { Ok((tx_hash, _shared_key)) => { let tx_hash = CString::new(tx_hash.to_string()) .map_or(ptr::null_mut(), std::ffi::CString::into_raw); @@ -326,7 +325,6 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private( to_keys: *const FfiPrivateAccountKeys, to_identifier: *const FfiU128, amount: *const [u8; 16], - pin: *const c_char, key_path: *const c_char, out_result: *mut FfiTransferResult, ) -> WalletFfiError { @@ -364,16 +362,18 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private( }; let to_identifier = u128::from_le_bytes(unsafe { (*to_identifier).data }); let amount = u128::from_le_bytes(unsafe { *amount }); - let pin = optional_c_str(pin); let key_path = optional_c_str(key_path); let transfer = NativeTokenTransfer(&wallet); - match block_on( - transfer.send_private_transfer_to_outer_account( - from_id, to_npk, to_vpk, amount, &pin, &key_path, - ), - ) { + match block_on(transfer.send_private_transfer_to_outer_account( + from_id, + to_npk, + to_vpk, + to_identifier, + amount, + &key_path, + )) { Ok((tx_hash, _shared_key)) => { let tx_hash = CString::new(tx_hash.to_string()) .map_or(ptr::null_mut(), std::ffi::CString::into_raw); @@ -430,7 +430,6 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded_owned( from: *const FfiBytes32, to: *const FfiBytes32, amount: *const [u8; 16], - pin: *const c_char, key_path: *const c_char, out_result: *mut FfiTransferResult, ) -> WalletFfiError { @@ -455,12 +454,11 @@ pub unsafe extern "C" fn wallet_ffi_transfer_shielded_owned( let from_id = AccountId::new(unsafe { (*from).data }); let to_id = AccountId::new(unsafe { (*to).data }); let amount = u128::from_le_bytes(unsafe { *amount }); - let pin = optional_c_str(pin); let key_path = optional_c_str(key_path); let transfer = NativeTokenTransfer(&wallet); - match block_on(transfer.send_shielded_transfer(from_id, to_id, amount, &pin, &key_path)) { + match block_on(transfer.send_shielded_transfer(from_id, to_id, amount, &key_path)) { Ok((tx_hash, _shared_key)) => { let tx_hash = CString::new(tx_hash.to_string()) .map_or(ptr::null_mut(), std::ffi::CString::into_raw); @@ -517,7 +515,6 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private_owned( from: *const FfiBytes32, to: *const FfiBytes32, amount: *const [u8; 16], - pin: *const c_char, key_path: *const c_char, out_result: *mut FfiTransferResult, ) -> WalletFfiError { @@ -542,13 +539,12 @@ pub unsafe extern "C" fn wallet_ffi_transfer_private_owned( let from_id = AccountId::new(unsafe { (*from).data }); let to_id = AccountId::new(unsafe { (*to).data }); let amount = u128::from_le_bytes(unsafe { *amount }); - let pin = optional_c_str(pin); let key_path = optional_c_str(key_path); let transfer = NativeTokenTransfer(&wallet); match block_on( - transfer.send_private_transfer_to_owned_account(from_id, to_id, amount, &pin, &key_path), + transfer.send_private_transfer_to_owned_account(from_id, to_id, amount, &key_path), ) { Ok((tx_hash, _shared_keys)) => { let tx_hash = CString::new(tx_hash.to_string()) @@ -667,7 +663,6 @@ pub unsafe extern "C" fn wallet_ffi_register_public_account( pub unsafe extern "C" fn wallet_ffi_register_private_account( handle: *mut WalletHandle, account_id: *const FfiBytes32, - pin: *const c_char, key_path: *const c_char, out_result: *mut FfiTransferResult, ) -> WalletFfiError { @@ -690,12 +685,11 @@ pub unsafe extern "C" fn wallet_ffi_register_private_account( }; let account_id = AccountId::new(unsafe { (*account_id).data }); - let pin = optional_c_str(pin); let key_path = optional_c_str(key_path); let transfer = NativeTokenTransfer(&wallet); - match block_on(transfer.register_account_private(account_id, &pin, &key_path)) { + match block_on(transfer.register_account_private(account_id, &key_path)) { Ok((tx_hash, _secret)) => { let tx_hash = CString::new(tx_hash.to_string()) .map_or(ptr::null_mut(), std::ffi::CString::into_raw); diff --git a/wallet-ffi/wallet_ffi.h b/wallet-ffi/wallet_ffi.h index 1f6075f6..e6d42297 100644 --- a/wallet-ffi/wallet_ffi.h +++ b/wallet-ffi/wallet_ffi.h @@ -743,7 +743,6 @@ enum WalletFfiError wallet_ffi_transfer_shielded(struct WalletHandle *handle, const struct FfiPrivateAccountKeys *to_keys, const struct FfiU128 *to_identifier, const uint8_t (*amount)[16], - const char *pin, const char *key_path, struct FfiTransferResult *out_result); @@ -779,7 +778,6 @@ enum WalletFfiError wallet_ffi_transfer_deshielded(struct WalletHandle *handle, const struct FfiBytes32 *from, const struct FfiBytes32 *to, const uint8_t (*amount)[16], - const char *pin, const char *key_path, struct FfiTransferResult *out_result); @@ -817,7 +815,6 @@ enum WalletFfiError wallet_ffi_transfer_private(struct WalletHandle *handle, const struct FfiPrivateAccountKeys *to_keys, const struct FfiU128 *to_identifier, const uint8_t (*amount)[16], - const char *pin, const char *key_path, struct FfiTransferResult *out_result); @@ -856,7 +853,6 @@ enum WalletFfiError wallet_ffi_transfer_shielded_owned(struct WalletHandle *hand const struct FfiBytes32 *from, const struct FfiBytes32 *to, const uint8_t (*amount)[16], - const char *pin, const char *key_path, struct FfiTransferResult *out_result); @@ -895,7 +891,6 @@ enum WalletFfiError wallet_ffi_transfer_private_owned(struct WalletHandle *handl const struct FfiBytes32 *from, const struct FfiBytes32 *to, const uint8_t (*amount)[16], - const char *pin, const char *key_path, struct FfiTransferResult *out_result); @@ -951,7 +946,6 @@ enum WalletFfiError wallet_ffi_register_public_account(struct WalletHandle *hand */ enum WalletFfiError wallet_ffi_register_private_account(struct WalletHandle *handle, const struct FfiBytes32 *account_id, - const char *pin, const char *key_path, struct FfiTransferResult *out_result); diff --git a/wallet/src/cli/programs/amm.rs b/wallet/src/cli/programs/amm.rs index 1805f444..afb36c30 100644 --- a/wallet/src/cli/programs/amm.rs +++ b/wallet/src/cli/programs/amm.rs @@ -22,22 +22,30 @@ pub enum AmmProgramAgnosticSubcommand { #[arg( long, conflicts_with = "user_holding_a_label", - required_unless_present = "user_holding_a_label" + conflicts_with = "user_holding_a_key_path", + required_unless_present_any = ["user_holding_a_label", "user_holding_a_key_path"] )] user_holding_a: Option, /// User holding A account label (alternative to --user-holding-a). - #[arg(long, conflicts_with = "user_holding_a")] + #[arg(long, conflicts_with = "user_holding_a", conflicts_with = "user_holding_a_key_path")] user_holding_a_label: Option, + /// Key path for user holding A (uses Keycard, alternative to --user-holding-a/label). + #[arg(long, conflicts_with = "user_holding_a", conflicts_with = "user_holding_a_label")] + user_holding_a_key_path: Option, /// `user_holding_b` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_b_label", - required_unless_present = "user_holding_b_label" + conflicts_with = "user_holding_b_key_path", + required_unless_present_any = ["user_holding_b_label", "user_holding_b_key_path"] )] user_holding_b: Option, /// User holding B account label (alternative to --user-holding-b). - #[arg(long, conflicts_with = "user_holding_b")] + #[arg(long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_key_path")] user_holding_b_label: Option, + /// Key path for user holding B (uses Keycard, alternative to --user-holding-b/label). + #[arg(long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_label")] + user_holding_b_key_path: Option, /// `user_holding_lp` - valid 32 byte base58 string with privacy prefix. #[arg( long, @@ -86,6 +94,9 @@ pub enum AmmProgramAgnosticSubcommand { /// `token_definition` - valid 32 byte base58 string WITHOUT privacy prefix. #[arg(long)] token_definition: String, + /// Key path for the input token's holding account (uses Keycard). + #[arg(long)] + key_path: Option, }, /// Swap specifying exact output amount. /// @@ -106,6 +117,9 @@ pub enum AmmProgramAgnosticSubcommand { /// `token_definition` - valid 32 byte base58 string WITHOUT privacy prefix. #[arg(long)] token_definition: String, + /// Key path for the input token's holding account (uses Keycard). + #[arg(long)] + key_path: Option, }, /// Add liquidity. /// @@ -117,22 +131,30 @@ pub enum AmmProgramAgnosticSubcommand { #[arg( long, conflicts_with = "user_holding_a_label", - required_unless_present = "user_holding_a_label" + conflicts_with = "user_holding_a_key_path", + required_unless_present_any = ["user_holding_a_label", "user_holding_a_key_path"] )] user_holding_a: Option, /// User holding A account label (alternative to --user-holding-a). - #[arg(long, conflicts_with = "user_holding_a")] + #[arg(long, conflicts_with = "user_holding_a", conflicts_with = "user_holding_a_key_path")] user_holding_a_label: Option, + /// Key path for user holding A (uses Keycard). + #[arg(long, conflicts_with = "user_holding_a", conflicts_with = "user_holding_a_label")] + user_holding_a_key_path: Option, /// `user_holding_b` - valid 32 byte base58 string with privacy prefix. #[arg( long, conflicts_with = "user_holding_b_label", - required_unless_present = "user_holding_b_label" + conflicts_with = "user_holding_b_key_path", + required_unless_present_any = ["user_holding_b_label", "user_holding_b_key_path"] )] user_holding_b: Option, /// User holding B account label (alternative to --user-holding-b). - #[arg(long, conflicts_with = "user_holding_b")] + #[arg(long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_key_path")] user_holding_b_label: Option, + /// Key path for user holding B (uses Keycard). + #[arg(long, conflicts_with = "user_holding_b", conflicts_with = "user_holding_b_label")] + user_holding_b_key_path: Option, /// `user_holding_lp` - valid 32 byte base58 string with privacy prefix. #[arg( long, @@ -180,12 +202,16 @@ pub enum AmmProgramAgnosticSubcommand { #[arg( long, conflicts_with = "user_holding_lp_label", - required_unless_present = "user_holding_lp_label" + conflicts_with = "user_holding_lp_key_path", + required_unless_present_any = ["user_holding_lp_label", "user_holding_lp_key_path"] )] user_holding_lp: Option, /// User holding LP account label (alternative to --user-holding-lp). - #[arg(long, conflicts_with = "user_holding_lp")] + #[arg(long, conflicts_with = "user_holding_lp", conflicts_with = "user_holding_lp_key_path")] user_holding_lp_label: Option, + /// Key path for user holding LP (uses Keycard). + #[arg(long, conflicts_with = "user_holding_lp", conflicts_with = "user_holding_lp_label")] + user_holding_lp_key_path: Option, #[arg(long)] balance_lp: u128, #[arg(long)] @@ -204,8 +230,10 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { Self::New { user_holding_a, user_holding_a_label, + user_holding_a_key_path, user_holding_b, user_holding_b_label, + user_holding_b_key_path, user_holding_lp, user_holding_lp_label, balance_a, @@ -216,14 +244,14 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + user_holding_a_key_path.as_deref(), )?; 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, + user_holding_b_key_path.as_deref(), )?; let user_holding_lp = resolve_id_or_label( user_holding_lp, @@ -260,6 +288,8 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_lp, balance_a, balance_b, + user_holding_a_key_path.as_deref(), + user_holding_b_key_path.as_deref(), ) .await?; @@ -279,6 +309,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { amount_in, min_amount_out, token_definition, + key_path, } => { let user_holding_a = resolve_id_or_label( user_holding_a, @@ -311,6 +342,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { amount_in, min_amount_out, token_definition.parse()?, + key_path.as_deref(), ) .await?; @@ -328,6 +360,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { exact_amount_out, max_amount_in, token_definition, + key_path, } => { let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; @@ -346,6 +379,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { exact_amount_out, max_amount_in, token_definition.parse()?, + key_path.as_deref(), ) .await?; @@ -360,8 +394,10 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { Self::AddLiquidity { user_holding_a, user_holding_a_label, + user_holding_a_key_path, user_holding_b, user_holding_b_label, + user_holding_b_key_path, user_holding_lp, user_holding_lp_label, min_amount_lp, @@ -373,14 +409,14 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + user_holding_a_key_path.as_deref(), )?; 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, + user_holding_b_key_path.as_deref(), )?; let user_holding_lp = resolve_id_or_label( user_holding_lp, @@ -418,6 +454,8 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { min_amount_lp, max_amount_a, max_amount_b, + user_holding_a_key_path.as_deref(), + user_holding_b_key_path.as_deref(), ) .await?; @@ -436,6 +474,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_b_label, user_holding_lp, user_holding_lp_label, + user_holding_lp_key_path, balance_lp, min_amount_a, min_amount_b, @@ -459,7 +498,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_lp_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + user_holding_lp_key_path.as_deref(), )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; @@ -490,6 +529,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { balance_lp, min_amount_a, min_amount_b, + user_holding_lp_key_path.as_deref(), ) .await?; diff --git a/wallet/src/cli/programs/ata.rs b/wallet/src/cli/programs/ata.rs index 1a63fa67..de46a898 100644 --- a/wallet/src/cli/programs/ata.rs +++ b/wallet/src/cli/programs/ata.rs @@ -8,7 +8,7 @@ use crate::{ AccDecodeData::Decode, WalletCore, cli::{SubcommandReturnValue, WalletSubcommand}, - helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix}, + helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix, resolve_keycard_id}, program_facades::ata::Ata, }; @@ -28,16 +28,19 @@ pub enum AtaSubcommand { Create { /// Owner account - valid 32 byte base58 string with privacy prefix. #[arg(long)] - owner: String, + owner: Option, /// Token definition account - valid 32 byte base58 string WITHOUT privacy prefix. #[arg(long)] token_definition: String, + /// Key path for the owner account (uses Keycard). + #[arg(long)] + key_path: Option, }, /// Send tokens from owner's ATA to a recipient token holding account. Send { /// Sender account - valid 32 byte base58 string with privacy prefix. #[arg(long)] - from: String, + from: Option, /// Token definition account - valid 32 byte base58 string WITHOUT privacy prefix. #[arg(long)] token_definition: String, @@ -46,17 +49,23 @@ pub enum AtaSubcommand { to: String, #[arg(long)] amount: u128, + /// Key path for the sender account (uses Keycard). + #[arg(long)] + from_key_path: Option, }, /// Burn tokens from holder's ATA. Burn { /// Holder account - valid 32 byte base58 string with privacy prefix. #[arg(long)] - holder: String, + holder: Option, /// Token definition account - valid 32 byte base58 string WITHOUT privacy prefix. #[arg(long)] token_definition: String, #[arg(long)] amount: u128, + /// Key path for the holder account (uses Keycard). + #[arg(long)] + key_path: Option, }, /// List all ATAs for a given owner across multiple token definitions. List { @@ -92,15 +101,21 @@ impl WalletSubcommand for AtaSubcommand { Self::Create { owner, token_definition, + key_path, } => { - let (owner_str, owner_privacy) = parse_addr_with_privacy_prefix(&owner)?; + let owner_str = match (owner, key_path.as_deref()) { + (Some(o), _) => o, + (None, Some(kp)) => resolve_keycard_id(kp)?, + (None, None) => anyhow::bail!("Provide --owner or --key-path"), + }; + let (owner_str, owner_privacy) = parse_addr_with_privacy_prefix(&owner_str)?; let owner_id: AccountId = owner_str.parse()?; let definition_id: AccountId = token_definition.parse()?; match owner_privacy { AccountPrivacyKind::Public => { Ata(wallet_core) - .send_create(owner_id, definition_id) + .send_create(owner_id, definition_id, key_path.as_deref()) .await?; Ok(SubcommandReturnValue::Empty) } @@ -129,6 +144,7 @@ impl WalletSubcommand for AtaSubcommand { token_definition, to, amount, + from_key_path, } => { let (from_str, from_privacy) = parse_addr_with_privacy_prefix(&from)?; let from_id: AccountId = from_str.parse()?; @@ -138,7 +154,7 @@ impl WalletSubcommand for AtaSubcommand { match from_privacy { AccountPrivacyKind::Public => { Ata(wallet_core) - .send_transfer(from_id, definition_id, to_id, amount) + .send_transfer(from_id, definition_id, to_id, amount, from_key_path.as_deref()) .await?; Ok(SubcommandReturnValue::Empty) } @@ -166,15 +182,16 @@ impl WalletSubcommand for AtaSubcommand { holder, token_definition, amount, + key_path } => { - let (holder_str, holder_privacy) = parse_addr_with_privacy_prefix(&holder)?; + let (holder_str, holder_privacy) = parse_addr_with_privacy_prefix(&holder); let holder_id: AccountId = holder_str.parse()?; let definition_id: AccountId = token_definition.parse()?; match holder_privacy { AccountPrivacyKind::Public => { Ata(wallet_core) - .send_burn(holder_id, definition_id, amount) + .send_burn(holder_id, definition_id, amount, key_path.as_deref()) .await?; Ok(SubcommandReturnValue::Empty) } diff --git a/wallet/src/cli/programs/native_token_transfer.rs b/wallet/src/cli/programs/native_token_transfer.rs index 5dcf684c..ef5eb731 100644 --- a/wallet/src/cli/programs/native_token_transfer.rs +++ b/wallet/src/cli/programs/native_token_transfer.rs @@ -115,7 +115,7 @@ impl WalletSubcommand for AuthTransferSubcommand { let account_id = account_id.parse()?; let (tx_hash, secret) = NativeTokenTransfer(wallet_core) - .register_account_private(account_id, &pin, &key_path) + .register_account_private(account_id, &key_path) .await?; println!("Transaction hash is {tx_hash}"); @@ -211,7 +211,6 @@ impl WalletSubcommand for AuthTransferSubcommand { from, to, amount, - pin: pin.clone(), key_path: from_key_path.clone(), }, ) @@ -221,7 +220,6 @@ impl WalletSubcommand for AuthTransferSubcommand { from, to, amount, - pin: pin.clone(), key_path: from_key_path.clone(), } } @@ -231,7 +229,6 @@ impl WalletSubcommand for AuthTransferSubcommand { from, to, amount, - pin, key_path: from_key_path, }, ) @@ -250,7 +247,6 @@ impl WalletSubcommand for AuthTransferSubcommand { to_vpk, to_identifier, amount, - pin: pin.clone(), key_path: from_key_path.clone(), }, ) @@ -263,7 +259,6 @@ impl WalletSubcommand for AuthTransferSubcommand { to_vpk, to_identifier, amount, - pin, key_path: from_key_path, }, ) @@ -316,8 +311,6 @@ pub enum NativeTokenTransferProgramSubcommand { #[arg(long)] amount: u128, #[arg(long)] - pin: Option, - #[arg(long)] key_path: Option, }, /// Shielded execution. @@ -343,8 +336,6 @@ pub enum NativeTokenTransferProgramSubcommandShielded { #[arg(long)] amount: u128, #[arg(long)] - pin: Option, - #[arg(long)] key_path: Option, }, /// Send native token transfer from `from` to `to` for `amount`. @@ -367,8 +358,6 @@ pub enum NativeTokenTransferProgramSubcommandShielded { #[arg(long)] amount: u128, #[arg(long)] - pin: Option, - #[arg(long)] key_path: Option, }, } @@ -391,8 +380,6 @@ pub enum NativeTokenTransferProgramSubcommandPrivate { #[arg(long)] amount: u128, #[arg(long)] - pin: Option, - #[arg(long)] key_path: Option, }, /// Send native token transfer from `from` to `to` for `amount`. @@ -415,8 +402,6 @@ pub enum NativeTokenTransferProgramSubcommandPrivate { #[arg(long)] amount: u128, #[arg(long)] - pin: Option, - #[arg(long)] key_path: Option, }, } @@ -431,14 +416,13 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { from, to, amount, - pin, key_path, } => { let from: AccountId = from.parse().unwrap(); let to: AccountId = to.parse().unwrap(); let (tx_hash, [secret_from, secret_to]) = NativeTokenTransfer(wallet_core) - .send_private_transfer_to_owned_account(from, to, amount, &pin, &key_path) + .send_private_transfer_to_owned_account(from, to, amount, &key_path) .await?; println!("Transaction hash is {tx_hash}"); @@ -464,7 +448,6 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { to_vpk, to_identifier, amount, - pin, key_path, } => { let from: AccountId = from.parse().unwrap(); @@ -481,7 +464,12 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { let (tx_hash, [secret_from, _]) = NativeTokenTransfer(wallet_core) .send_private_transfer_to_outer_account( - from, to_npk, to_vpk, amount, &pin, &key_path, + from, + to_npk, + to_vpk, + to_identifier.unwrap_or_else(rand::random), + amount, + &key_path, ) .await?; @@ -516,14 +504,13 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { from, to, amount, - pin, key_path, } => { let from: AccountId = from.parse().unwrap(); let to: AccountId = to.parse().unwrap(); let (tx_hash, secret) = NativeTokenTransfer(wallet_core) - .send_shielded_transfer(from, to, amount, &pin, &key_path) + .send_shielded_transfer(from, to, amount, &key_path) .await?; println!("Transaction hash is {tx_hash}"); @@ -549,7 +536,6 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { to_vpk, to_identifier, amount, - pin, key_path, } => { let from: AccountId = from.parse().unwrap(); @@ -567,7 +553,12 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { let (tx_hash, _) = NativeTokenTransfer(wallet_core) .send_shielded_transfer_to_outer_account( - from, to_npk, to_vpk, amount, &pin, &key_path, + from, + to_npk, + to_vpk, + to_identifier.unwrap_or_else(rand::random), + amount, + &key_path, ) .await?; @@ -597,14 +588,13 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { from, to, amount, - pin, key_path, } => { let from: AccountId = from.parse().unwrap(); let to: AccountId = to.parse().unwrap(); let (tx_hash, secret) = NativeTokenTransfer(wallet_core) - .send_deshielded_transfer(from, to, amount, &pin, &key_path) + .send_deshielded_transfer(from, to, amount, &key_path) .await?; println!("Transaction hash is {tx_hash}"); diff --git a/wallet/src/cli/programs/token.rs b/wallet/src/cli/programs/token.rs index 66fb7bf7..f491b765 100644 --- a/wallet/src/cli/programs/token.rs +++ b/wallet/src/cli/programs/token.rs @@ -5,11 +5,12 @@ use nssa::AccountId; use crate::{ AccDecodeData::Decode, + PrivacyPreservingAccount, WalletCore, cli::{SubcommandReturnValue, WalletSubcommand}, helperfunctions::{ AccountPrivacyKind, parse_addr_with_privacy_prefix, resolve_account_label, - resolve_id_or_label, + resolve_id_or_label, resolve_keycard_id, }, program_facades::token::Token, }; @@ -19,6 +20,41 @@ use crate::{ pub enum TokenProgramAgnosticSubcommand { /// Produce a new token. New { + /// `definition_account_id` - valid 32 byte base58 string with privacy prefix. + #[arg( + long, + conflicts_with = "definition_account_label", + conflicts_with = "definition_key_path", + required_unless_present_any = ["definition_account_label", "definition_key_path"] + )] + definition_account_id: Option, + /// Definition account label (alternative to --definition-account-id). + #[arg(long, conflicts_with = "definition_account_id", conflicts_with = "definition_key_path")] + definition_account_label: Option, + /// Key path for the definition account (uses Keycard). + #[arg(long, conflicts_with = "definition_account_id", conflicts_with = "definition_account_label")] + definition_key_path: Option, + /// `supply_account_id` - valid 32 byte base58 string with privacy prefix. + #[arg( + long, + conflicts_with = "supply_account_label", + conflicts_with = "supply_key_path", + required_unless_present_any = ["supply_account_label", "supply_key_path"] + )] + supply_account_id: Option, + /// Supply account label (alternative to --supply-account-id). + #[arg(long, conflicts_with = "supply_account_id", conflicts_with = "supply_key_path")] + supply_account_label: Option, + /// Key path for the supply account (uses Keycard). + #[arg(long, conflicts_with = "supply_account_id", conflicts_with = "supply_account_label")] + supply_key_path: Option, + #[arg(short, long)] + name: String, + #[arg(short, long)] + total_supply: u128, + }, + /// Initialize a token holding account for a given token definition. + Init { /// `definition_account_id` - valid 32 byte base58 string with privacy prefix. #[arg( long, @@ -29,20 +65,23 @@ pub enum TokenProgramAgnosticSubcommand { /// Definition account label (alternative to --definition-account-id). #[arg(long, conflicts_with = "definition_account_id")] definition_account_label: Option, - /// `supply_account_id` - valid 32 byte base58 string with privacy prefix. + /// `holder_account_id` - valid 32 byte base58 string with privacy prefix. #[arg( long, - conflicts_with = "supply_account_label", - required_unless_present = "supply_account_label" + conflicts_with = "holder_account_label", + required_unless_present_any = ["holder_account_label", "holder_key_path"] )] - supply_account_id: Option, - /// Supply account label (alternative to --supply-account-id). - #[arg(long, conflicts_with = "supply_account_id")] - supply_account_label: Option, - #[arg(short, long)] - name: String, - #[arg(short, long)] - total_supply: u128, + holder_account_id: Option, + /// Holder account label (alternative to --holder-account-id). + #[arg(long, conflicts_with = "holder_account_id")] + holder_account_label: Option, + /// `holder_key_path` (alternative to --holder-account-id) uses Keycard. + #[arg( + long, + conflicts_with = "holder_account_id", + conflicts_with = "holder_account_label" + )] + holder_key_path: Option, }, /// Send tokens from one account to another with variable privacy. /// @@ -80,8 +119,12 @@ pub enum TokenProgramAgnosticSubcommand { /// amount - amount of balance to move. #[arg(long)] amount: u128, - #[arg(long, conflicts_with = "from", conflicts_with = "from_label")] + /// `from_key_path` (alternative to --from) uses Keycard. + #[arg(long, conflicts_with = "from", conflicts_with = "from", conflicts_with = "from_label")] from_key_path: Option, + /// `to_key_path` (alternative to --to) uses Keycard. + #[arg(long, conflicts_with = "to", conflicts_with = "to", conflicts_with = "to_label")] + to_key_path: Option, }, /// Burn tokens on `holder`, modify `definition`. /// @@ -125,12 +168,16 @@ pub enum TokenProgramAgnosticSubcommand { #[arg( long, conflicts_with = "definition_label", - required_unless_present = "definition_label" + conflicts_with = "definition_key_path", + required_unless_present_any = ["definition_label", "definition_key_path"] )] definition: Option, /// Definition account label (alternative to --definition). - #[arg(long, conflicts_with = "definition")] + #[arg(long, conflicts_with = "definition", conflicts_with = "definition_key_path")] definition_label: Option, + /// Key path for the definition account (uses Keycard). + #[arg(long, conflicts_with = "definition", conflicts_with = "definition_label")] + definition_key_path: Option, /// holder - valid 32 byte base58 string with privacy prefix. #[arg(long, conflicts_with = "holder_label", required_unless_present_any = ["holder_label", "holder_key_path"])] holder: Option, @@ -159,11 +206,98 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { wallet_core: &mut WalletCore, ) -> Result { match self { + Self::Init { + definition_account_id, + definition_account_label, + holder_account_id, + holder_account_label, + holder_key_path, + } => { + let definition_str = resolve_id_or_label( + definition_account_id, + definition_account_label, + &wallet_core.storage.labels, + &wallet_core.storage.user_data, + None, + )?; + let holder_str = resolve_id_or_label( + holder_account_id, + holder_account_label, + &wallet_core.storage.labels, + &wallet_core.storage.user_data, + holder_key_path.as_deref(), + )?; + + let (definition_id, definition_privacy) = + parse_addr_with_privacy_prefix(&definition_str)?; + let (holder_id, holder_privacy) = parse_addr_with_privacy_prefix(&holder_str)?; + + let definition_account_id: AccountId = definition_id.parse()?; + let holder_account_id: AccountId = holder_id.parse()?; + + let definition_account = match definition_privacy { + AccountPrivacyKind::Public => { + PrivacyPreservingAccount::Public(definition_account_id) + } + AccountPrivacyKind::Private => { + PrivacyPreservingAccount::PrivateOwned(definition_account_id) + } + }; + let holder_account = match holder_privacy { + AccountPrivacyKind::Public => { + PrivacyPreservingAccount::Public(holder_account_id) + } + AccountPrivacyKind::Private => { + PrivacyPreservingAccount::PrivateOwned(holder_account_id) + } + }; + + let (tx_hash, secrets) = Token(wallet_core) + .send_initialize_account(definition_account, holder_account, &holder_key_path) + .await?; + + println!("Transaction hash is {tx_hash}"); + + if secrets.is_empty() { + return Ok(SubcommandReturnValue::Empty); + } + + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let mut secrets_iter = secrets.into_iter(); + let mut acc_decode_data = Vec::new(); + + if matches!(definition_privacy, AccountPrivacyKind::Private) { + acc_decode_data.push(Decode( + secrets_iter.next().expect("expected definition's secret"), + definition_account_id, + )); + } + if matches!(holder_privacy, AccountPrivacyKind::Private) { + acc_decode_data.push(Decode( + secrets_iter.next().expect("expected holder's secret"), + holder_account_id, + )); + } + + wallet_core.decode_insert_privacy_preserving_transaction_results( + &tx, + &acc_decode_data, + )?; + } + + wallet_core.store_persistent_data().await?; + + Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) + } Self::New { definition_account_id, definition_account_label, + definition_key_path, supply_account_id, supply_account_label, + supply_key_path, name, total_supply, } => { @@ -172,14 +306,14 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { definition_account_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + definition_key_path.as_deref(), )?; let supply_account_id = resolve_id_or_label( supply_account_id, supply_account_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + supply_key_path.as_deref(), )?; let (definition_account_id, definition_addr_privacy) = parse_addr_with_privacy_prefix(&definition_account_id)?; @@ -194,6 +328,8 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { supply_account_id, name, total_supply, + definition_key_path, + supply_key_path, }, ) } @@ -241,6 +377,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { to_identifier, amount, from_key_path, + to_key_path, } => { let from = resolve_id_or_label( from, @@ -249,14 +386,17 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { &wallet_core.storage.user_data, from_key_path.as_deref(), )?; - let to = match (to, to_label) { - (v, None) => v, - (None, Some(label)) => Some(resolve_account_label( + let to = match (to, to_label, to_key_path) { + (v, None, None) => v, + (None, Some(label), None) => Some(resolve_account_label( &label, &wallet_core.storage.labels, &wallet_core.storage.user_data, )?), - (Some(_), Some(_)) => { + (None, None, Some(to_key_path)) => { + Some(resolve_keycard_id(&to_key_path)?) + } + _ => { anyhow::bail!("Provide only one of --to or --to-label") } }; @@ -285,7 +425,6 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { sender_account_id: from, recipient_account_id: to, balance_to_move: amount, - pin, sender_key_path: from_key_path, }, ) @@ -380,6 +519,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { definition_account_id: definition, holder_account_id: holder, amount, + holder_key_path, }, ) } @@ -418,6 +558,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { Self::Mint { definition, definition_label, + definition_key_path, holder, holder_label, holder_npk, @@ -430,7 +571,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { definition_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + definition_key_path.as_deref(), )?; let holder = match (holder, holder_label) { (v, None) => v, @@ -469,6 +610,7 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand { definition_account_id: definition, holder_account_id: holder, amount, + definition_key_path: definition_key_path.clone(), }, ) } @@ -566,8 +708,6 @@ pub enum TokenProgramSubcommandPublic { #[arg(short, long)] balance_to_move: u128, #[arg(long)] - pin: Option, - #[arg(long)] sender_key_path: Option, }, // Burn tokens using the token program @@ -578,6 +718,8 @@ pub enum TokenProgramSubcommandPublic { holder_account_id: String, #[arg(short, long)] amount: u128, + #[arg(skip)] + holder_key_path: Option, }, // Transfer tokens using the token program MintToken { @@ -587,6 +729,8 @@ pub enum TokenProgramSubcommandPublic { holder_account_id: String, #[arg(short, long)] amount: u128, + #[arg(skip)] + definition_key_path: Option, }, } @@ -761,6 +905,10 @@ pub enum CreateNewTokenProgramSubcommand { name: String, #[arg(short, long)] total_supply: u128, + #[arg(skip)] + definition_key_path: Option, + #[arg(skip)] + supply_key_path: Option, }, /// Create a new token using the token program. /// @@ -813,7 +961,6 @@ impl WalletSubcommand for TokenProgramSubcommandPublic { sender_account_id, recipient_account_id, balance_to_move, - pin, sender_key_path, } => { Token(wallet_core) @@ -821,7 +968,6 @@ impl WalletSubcommand for TokenProgramSubcommandPublic { sender_account_id.parse().unwrap(), recipient_account_id.parse().unwrap(), balance_to_move, - pin, sender_key_path, ) .await?; @@ -831,12 +977,14 @@ impl WalletSubcommand for TokenProgramSubcommandPublic { definition_account_id, holder_account_id, amount, + holder_key_path, } => { Token(wallet_core) .send_burn_transaction( definition_account_id.parse().unwrap(), holder_account_id.parse().unwrap(), amount, + holder_key_path.as_deref(), ) .await?; Ok(SubcommandReturnValue::Empty) @@ -845,12 +993,14 @@ impl WalletSubcommand for TokenProgramSubcommandPublic { definition_account_id, holder_account_id, amount, + definition_key_path, } => { Token(wallet_core) .send_mint_transaction( definition_account_id.parse().unwrap(), holder_account_id.parse().unwrap(), amount, + definition_key_path.as_deref(), ) .await?; Ok(SubcommandReturnValue::Empty) @@ -1494,6 +1644,8 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand { supply_account_id, name, total_supply, + definition_key_path, + supply_key_path, } => { Token(wallet_core) .send_new_definition( @@ -1501,6 +1653,8 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand { supply_account_id.parse().unwrap(), name, total_supply, + definition_key_path.as_deref(), + supply_key_path.as_deref(), ) .await?; Ok(SubcommandReturnValue::Empty) diff --git a/wallet/src/helperfunctions.rs b/wallet/src/helperfunctions.rs index 54883fdb..cfe1846b 100644 --- a/wallet/src/helperfunctions.rs +++ b/wallet/src/helperfunctions.rs @@ -81,14 +81,18 @@ pub fn resolve_id_or_label( (Some(id), None, None) => Ok(id), (None, Some(label), None) => resolve_account_label(&label, labels, user_data), (None, None, Some(key_path)) => { - let pin = read_pin()?; - KeycardWallet::get_account_id_for_path_with_connect(&pin, key_path) - .map_err(anyhow::Error::from) + resolve_keycard_id(key_path) } _ => anyhow::bail!("provide exactly one of account id, account label or keycard path"), } } +pub fn resolve_keycard_id(key_path: &str) -> Result { + let pin = read_pin()?; + KeycardWallet::get_public_account_id_for_path_with_connect(&pin, key_path) + .map_err(anyhow::Error::from) +} + /// Resolve an account label to its full `Privacy/id` string representation. /// /// Looks up the label in the labels map and determines whether the account is diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index d655a4ca..d480ae3a 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -15,7 +15,7 @@ use bip39::Mnemonic; 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 key_protocol::key_management::key_tree::chain_index::ChainIndex; use keycard_wallet::KeycardWallet; use log::info; use nssa::{ @@ -31,6 +31,7 @@ use nssa_core::{ program::InstructionData, }; pub use privacy_preserving_tx::PrivacyPreservingAccount; +use pyo3::exceptions::PyRuntimeError; use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder}; use tokio::io::AsyncWriteExt as _; @@ -391,7 +392,6 @@ impl WalletCore { accounts: Vec, instruction_data: InstructionData, program: &ProgramWithDependencies, - pin: &Option, key_path: &Option, ) -> Result<(HashType, Vec), ExecutionFailureKind> { self.send_privacy_preserving_tx_with_pre_check( @@ -399,7 +399,6 @@ impl WalletCore { instruction_data, program, |_| Ok(()), - pin, key_path, ) .await @@ -411,28 +410,29 @@ impl WalletCore { instruction_data: InstructionData, program: &ProgramWithDependencies, tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, - pin: &Option, key_path: &Option, ) -> Result<(HashType, Vec), ExecutionFailureKind> { let acc_manager = privacy_preserving_tx::AccountManager::new(self, accounts).await?; let mut pre_states = acc_manager.pre_states(); - let keycard_account = if let Some(pin) = pin.as_ref() { - let account_id = KeycardWallet::get_account_id_for_path_with_connect( - pin, - key_path.as_ref().expect("Expect a key path String."), - ); - + let keycard_account = if let Some(key_path_str) = key_path.as_deref() { + let account_id = { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::< + pyo3::exceptions::PyRuntimeError, + _, + >(e.to_string())) + })?; + KeycardWallet::get_public_account_id_for_path_with_connect(&pin, key_path_str)? + }; let (acc_id, _) = parse_addr_with_privacy_prefix(&account_id).expect("Valid parsing of account id"); - - let account_id = acc_id.parse().expect("Expect a valid Account Id"); + let account_id: AccountId = acc_id.parse().expect("Expect a valid Account Id"); let account = self .get_account_public(account_id) .await .expect("Expect valid account"); - Some(AccountWithMetadata { account, is_authorized: true, @@ -498,7 +498,8 @@ impl WalletCore { ) .unwrap(); - let witness_set = Self::sign_privacy_message(&message, proof.clone(), &acc_manager); + let witness_set = + Self::sign_privacy_message(&message, proof.clone(), &acc_manager, key_path)?; let tx = PrivacyPreservingTransaction::new(message, witness_set); let shared_secrets: Vec<_> = private_account_keys @@ -663,66 +664,65 @@ impl WalletCore { message: &nssa::privacy_preserving_transaction::Message, proof: Proof, acc_manager: &privacy_preserving_tx::AccountManager, - pin: &Option, key_path: &Option, ) -> Result { - pin.as_ref().map_or_else( - || { - Ok( - nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( - message, - proof.clone(), - &acc_manager.public_account_auth(), - ), - ) - }, - |pin| { - let public_key = KeycardWallet::get_public_key_for_path_with_connect( - pin, - key_path.as_ref().expect("Expect a key path String."), - ); - let signature = KeycardWallet::sign_message_for_path_with_connect( - pin, - key_path.as_ref().expect("Expect a key path String."), - &message.hash_message(), - ) - .expect("Expect a valid signature"); - Ok( - nssa::privacy_preserving_transaction::witness_set::WitnessSet::from_list( - proof.clone(), - &[signature], - &[public_key], - ), - ) - }, - ) + if let Some(key_path_str) = key_path.as_deref() { + let (signature, public_key) = { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::< + pyo3::exceptions::PyRuntimeError, + _, + >(e.to_string())) + })?; + KeycardWallet::sign_message_for_path_with_connect( + &pin, + key_path_str, + &message.hash(), + )? + }; + Ok( + nssa::privacy_preserving_transaction::witness_set::WitnessSet::from_list( + proof, + &[signature], + &[public_key], + ), + ) + } else { + Ok( + nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( + message, + proof, + &acc_manager.public_account_auth(), + ), + ) + } } pub fn sign_privacy_message_with_keycard( message: &nssa::privacy_preserving_transaction::Message, proof: Proof, - pin: &str, key_paths: &[String], ) -> Result { - let mut signatures = Vec::::new(); - let mut public_keys = Vec::::new(); + let mut signatures_and_public_keys = Vec::<(Signature, PublicKey)>::new(); for path in key_paths { - 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.hash_message(), - ) - .expect("Expect a valid signature"), + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?; + + signatures_and_public_keys.push( + KeycardWallet::sign_message_for_path_with_connect(&pin, path, &message.hash()) + .expect("Expect a valid signature"), // TODO: Marvin tautology ); } + let (signatures, public_keys): (Vec, Vec) = + signatures_and_public_keys.into_iter().unzip(); + Ok( nssa::privacy_preserving_transaction::witness_set::WitnessSet::from_list( proof, diff --git a/wallet/src/program_facades/amm.rs b/wallet/src/program_facades/amm.rs index b31d0658..f563ba46 100644 --- a/wallet/src/program_facades/amm.rs +++ b/wallet/src/program_facades/amm.rs @@ -15,6 +15,8 @@ impl Amm<'_> { user_holding_lp: AccountId, balance_a: u128, balance_b: u128, + key_path_a: Option<&str>, + key_path_b: Option<&str>, ) -> Result { let program = Program::amm(); let amm_program_id = Program::amm().id(); @@ -58,37 +60,20 @@ impl Amm<'_> { user_holding_lp, ]; + // Check if LP has a stored key to determine if LP nonce is needed — before message creation + let lp_sk = self + .0 + .storage + .user_data + .get_pub_account_signing_key(user_holding_lp); + let mut nonces = self .0 .get_accounts_nonces(vec![user_holding_a, user_holding_b]) .await .map_err(ExecutionFailureKind::SequencerError)?; - let mut private_keys = Vec::new(); - - let signing_key_a = self - .0 - .storage - .user_data - .get_pub_account_signing_key(user_holding_a) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(signing_key_a); - - let signing_key_b = self - .0 - .storage - .user_data - .get_pub_account_signing_key(user_holding_b) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(signing_key_b); - - if let Some(signing_key_lp) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(user_holding_lp) - { - private_keys.push(signing_key_lp); + if lp_sk.is_some() { let lp_nonces = self .0 .get_accounts_nonces(vec![user_holding_lp]) @@ -109,8 +94,59 @@ impl Amm<'_> { ) .unwrap(); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &private_keys); + let msg_hash = message.hash(); + let pin = if key_path_a.is_some() || key_path_b.is_some() { + Some(crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?) + } else { + None + }; + + let (sig_a, pk_a) = if let Some(kp) = key_path_a { + keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( + pin.as_ref().unwrap(), + kp, + &msg_hash, + )? + } else { + let sk = self + .0 + .storage + .user_data + .get_pub_account_signing_key(user_holding_a) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + (nssa::Signature::new(&sk, &msg_hash), nssa::PublicKey::new_from_private_key(&sk)) + }; + + let (sig_b, pk_b) = if let Some(kp) = key_path_b { + keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( + pin.as_ref().unwrap(), + kp, + &msg_hash, + )? + } else { + let sk = self + .0 + .storage + .user_data + .get_pub_account_signing_key(user_holding_b) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + (nssa::Signature::new(&sk, &msg_hash), nssa::PublicKey::new_from_private_key(&sk)) + }; + + let mut sigs = vec![sig_a, sig_b]; + let mut pks = vec![pk_a, pk_b]; + + if let Some(sk_lp) = lp_sk { + sigs.push(nssa::Signature::new(&sk_lp, &msg_hash)); + pks.push(nssa::PublicKey::new_from_private_key(&sk_lp)); + } + + let witness_set = nssa::public_transaction::WitnessSet::from_list(&message, &sigs, &pks) + .map_err(ExecutionFailureKind::TransactionBuildError)?; let tx = nssa::PublicTransaction::new(message, witness_set); @@ -128,6 +164,7 @@ impl Amm<'_> { swap_amount_in: u128, min_amount_out: u128, token_definition_id_in: AccountId, + key_path: Option<&str>, ) -> Result { let instruction = amm_core::Instruction::SwapExactInput { swap_amount_in, @@ -184,13 +221,6 @@ impl Amm<'_> { .await .map_err(ExecutionFailureKind::SequencerError)?; - let signing_key = self - .0 - .storage - .user_data - .get_pub_account_signing_key(account_id_auth) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - let message = nssa::public_transaction::Message::try_new( program.id(), account_ids, @@ -199,8 +229,26 @@ impl Amm<'_> { ) .unwrap(); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + let msg_hash = message.hash(); + let witness_set = if let Some(kp) = key_path { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?; + let (sig, pk) = + keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(&pin, kp, &msg_hash)?; + nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk]) + .map_err(ExecutionFailureKind::TransactionBuildError)? + } else { + let signing_key = self + .0 + .storage + .user_data + .get_pub_account_signing_key(account_id_auth) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]) + }; let tx = nssa::PublicTransaction::new(message, witness_set); @@ -218,6 +266,7 @@ impl Amm<'_> { exact_amount_out: u128, max_amount_in: u128, token_definition_id_in: AccountId, + key_path: Option<&str>, ) -> Result { let instruction = amm_core::Instruction::SwapExactOutput { exact_amount_out, @@ -274,13 +323,6 @@ impl Amm<'_> { .await .map_err(ExecutionFailureKind::SequencerError)?; - let signing_key = self - .0 - .storage - .user_data - .get_pub_account_signing_key(account_id_auth) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - let message = nssa::public_transaction::Message::try_new( program.id(), account_ids, @@ -289,8 +331,26 @@ impl Amm<'_> { ) .unwrap(); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + let msg_hash = message.hash(); + let witness_set = if let Some(kp) = key_path { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?; + let (sig, pk) = + keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(&pin, kp, &msg_hash)?; + nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk]) + .map_err(ExecutionFailureKind::TransactionBuildError)? + } else { + let signing_key = self + .0 + .storage + .user_data + .get_pub_account_signing_key(account_id_auth) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]) + }; let tx = nssa::PublicTransaction::new(message, witness_set); @@ -309,6 +369,8 @@ impl Amm<'_> { min_amount_liquidity: u128, max_amount_to_add_token_a: u128, max_amount_to_add_token_b: u128, + key_path_a: Option<&str>, + key_path_b: Option<&str>, ) -> Result { let instruction = amm_core::Instruction::AddLiquidity { min_amount_liquidity, @@ -358,20 +420,6 @@ impl Amm<'_> { .await .map_err(ExecutionFailureKind::SequencerError)?; - let signing_key_a = self - .0 - .storage - .user_data - .get_pub_account_signing_key(user_holding_a) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - - let signing_key_b = self - .0 - .storage - .user_data - .get_pub_account_signing_key(user_holding_b) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - let message = nssa::public_transaction::Message::try_new( program.id(), account_ids, @@ -380,10 +428,52 @@ impl Amm<'_> { ) .unwrap(); - let witness_set = nssa::public_transaction::WitnessSet::for_message( - &message, - &[signing_key_a, signing_key_b], - ); + let msg_hash = message.hash(); + let pin = if key_path_a.is_some() || key_path_b.is_some() { + Some(crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?) + } else { + None + }; + + let (sig_a, pk_a) = if let Some(kp) = key_path_a { + keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( + pin.as_ref().unwrap(), + kp, + &msg_hash, + )? + } else { + let sk = self + .0 + .storage + .user_data + .get_pub_account_signing_key(user_holding_a) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + (nssa::Signature::new(&sk, &msg_hash), nssa::PublicKey::new_from_private_key(&sk)) + }; + + let (sig_b, pk_b) = if let Some(kp) = key_path_b { + keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( + pin.as_ref().unwrap(), + kp, + &msg_hash, + )? + } else { + let sk = self + .0 + .storage + .user_data + .get_pub_account_signing_key(user_holding_b) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + (nssa::Signature::new(&sk, &msg_hash), nssa::PublicKey::new_from_private_key(&sk)) + }; + + let witness_set = + nssa::public_transaction::WitnessSet::from_list(&message, &[sig_a, sig_b], &[pk_a, pk_b]) + .map_err(ExecutionFailureKind::TransactionBuildError)?; let tx = nssa::PublicTransaction::new(message, witness_set); @@ -402,6 +492,7 @@ impl Amm<'_> { remove_liquidity_amount: u128, min_amount_to_remove_token_a: u128, min_amount_to_remove_token_b: u128, + key_path_lp: Option<&str>, ) -> Result { let instruction = amm_core::Instruction::RemoveLiquidity { remove_liquidity_amount, @@ -451,13 +542,6 @@ impl Amm<'_> { .await .map_err(ExecutionFailureKind::SequencerError)?; - let signing_key_lp = self - .0 - .storage - .user_data - .get_pub_account_signing_key(user_holding_lp) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - let message = nssa::public_transaction::Message::try_new( program.id(), account_ids, @@ -466,8 +550,26 @@ impl Amm<'_> { ) .unwrap(); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key_lp]); + let msg_hash = message.hash(); + let witness_set = if let Some(kp) = key_path_lp { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?; + let (sig, pk) = + keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(&pin, kp, &msg_hash)?; + nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk]) + .map_err(ExecutionFailureKind::TransactionBuildError)? + } else { + let signing_key_lp = self + .0 + .storage + .user_data + .get_pub_account_signing_key(user_holding_lp) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key_lp]) + }; let tx = nssa::PublicTransaction::new(message, witness_set); diff --git a/wallet/src/program_facades/ata.rs b/wallet/src/program_facades/ata.rs index a72d50a9..6285bc7b 100644 --- a/wallet/src/program_facades/ata.rs +++ b/wallet/src/program_facades/ata.rs @@ -17,6 +17,7 @@ impl Ata<'_> { &self, owner_id: AccountId, definition_id: AccountId, + key_path: Option<&str>, ) -> Result { let program = Program::ata(); let ata_program_id = program.id(); @@ -33,15 +34,6 @@ impl Ata<'_> { .await .map_err(ExecutionFailureKind::SequencerError)?; - let Some(signing_key) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(owner_id) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - let instruction = ata_core::Instruction::Create { ata_program_id }; let message = nssa::public_transaction::Message::try_new( @@ -51,8 +43,28 @@ impl Ata<'_> { instruction, )?; - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + let msg_hash = message.hash(); + let witness_set = if let Some(kp) = key_path { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?; + let (sig, pk) = + keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(&pin, kp, &msg_hash)?; + nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk]) + .map_err(ExecutionFailureKind::TransactionBuildError)? + } else { + let Some(signing_key) = self + .0 + .storage + .user_data + .get_pub_account_signing_key(owner_id) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]) + }; let tx = nssa::PublicTransaction::new(message, witness_set); @@ -69,6 +81,7 @@ impl Ata<'_> { definition_id: AccountId, recipient_id: AccountId, amount: u128, + key_path: Option<&str>, ) -> Result { let program = Program::ata(); let ata_program_id = program.id(); @@ -85,15 +98,6 @@ impl Ata<'_> { .await .map_err(ExecutionFailureKind::SequencerError)?; - let Some(signing_key) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(owner_id) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - let instruction = ata_core::Instruction::Transfer { ata_program_id, amount, @@ -106,8 +110,28 @@ impl Ata<'_> { instruction, )?; - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + let msg_hash = message.hash(); + let witness_set = if let Some(kp) = key_path { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?; + let (sig, pk) = + keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(&pin, kp, &msg_hash)?; + nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk]) + .map_err(ExecutionFailureKind::TransactionBuildError)? + } else { + let Some(signing_key) = self + .0 + .storage + .user_data + .get_pub_account_signing_key(owner_id) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]) + }; let tx = nssa::PublicTransaction::new(message, witness_set); @@ -123,6 +147,7 @@ impl Ata<'_> { owner_id: AccountId, definition_id: AccountId, amount: u128, + key_path: Option<&str>, ) -> Result { let program = Program::ata(); let ata_program_id = program.id(); @@ -139,15 +164,6 @@ impl Ata<'_> { .await .map_err(ExecutionFailureKind::SequencerError)?; - let Some(signing_key) = self - .0 - .storage - .user_data - .get_pub_account_signing_key(owner_id) - else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - let instruction = ata_core::Instruction::Burn { ata_program_id, amount, @@ -160,8 +176,28 @@ impl Ata<'_> { instruction, )?; - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + let msg_hash = message.hash(); + let witness_set = if let Some(kp) = key_path { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?; + let (sig, pk) = + keycard_wallet::KeycardWallet::sign_message_for_path_with_connect(&pin, kp, &msg_hash)?; + nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk]) + .map_err(ExecutionFailureKind::TransactionBuildError)? + } else { + let Some(signing_key) = self + .0 + .storage + .user_data + .get_pub_account_signing_key(owner_id) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]) + }; let tx = nssa::PublicTransaction::new(message, witness_set); @@ -199,7 +235,6 @@ impl Ata<'_> { instruction_data, &ata_with_token_dependency(), &None, - &None, ) .await .map(|(hash, mut secrets)| { @@ -240,7 +275,6 @@ impl Ata<'_> { instruction_data, &ata_with_token_dependency(), &None, - &None, ) .await .map(|(hash, mut secrets)| { @@ -280,7 +314,6 @@ impl Ata<'_> { instruction_data, &ata_with_token_dependency(), &None, - &None, ) .await .map(|(hash, mut secrets)| { diff --git a/wallet/src/program_facades/native_token_transfer/deshielded.rs b/wallet/src/program_facades/native_token_transfer/deshielded.rs index 9cd87ea9..5992491e 100644 --- a/wallet/src/program_facades/native_token_transfer/deshielded.rs +++ b/wallet/src/program_facades/native_token_transfer/deshielded.rs @@ -10,7 +10,6 @@ impl NativeTokenTransfer<'_> { from: AccountId, to: AccountId, balance_to_move: u128, - pin: &Option, key_path: &Option, ) -> Result<(HashType, nssa_core::SharedSecretKey), ExecutionFailureKind> { let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); @@ -24,7 +23,6 @@ impl NativeTokenTransfer<'_> { instruction_data, &program.into(), tx_pre_check, - pin, key_path, ) .await diff --git a/wallet/src/program_facades/native_token_transfer/private.rs b/wallet/src/program_facades/native_token_transfer/private.rs index 546d290c..65ccfd12 100644 --- a/wallet/src/program_facades/native_token_transfer/private.rs +++ b/wallet/src/program_facades/native_token_transfer/private.rs @@ -11,7 +11,6 @@ impl NativeTokenTransfer<'_> { pub async fn register_account_private( &self, from: AccountId, - pin: &Option, key_path: &Option, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction: u128 = 0; @@ -21,7 +20,6 @@ impl NativeTokenTransfer<'_> { vec![PrivacyPreservingAccount::PrivateOwned(from)], Program::serialize_instruction(instruction).unwrap(), &Program::authenticated_transfer_program().into(), - pin, key_path, ) .await @@ -39,7 +37,6 @@ impl NativeTokenTransfer<'_> { to_vpk: ViewingPublicKey, to_identifier: Identifier, balance_to_move: u128, - pin: &Option, key_path: &Option, ) -> Result<(HashType, [SharedSecretKey; 2]), ExecutionFailureKind> { let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); @@ -57,7 +54,6 @@ impl NativeTokenTransfer<'_> { instruction_data, &program.into(), tx_pre_check, - pin, key_path, ) .await @@ -74,7 +70,6 @@ impl NativeTokenTransfer<'_> { from: AccountId, to: AccountId, balance_to_move: u128, - pin: &Option, key_path: &Option, ) -> Result<(HashType, [SharedSecretKey; 2]), ExecutionFailureKind> { let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); @@ -88,7 +83,6 @@ impl NativeTokenTransfer<'_> { instruction_data, &program.into(), tx_pre_check, - pin, key_path, ) .await diff --git a/wallet/src/program_facades/native_token_transfer/public.rs b/wallet/src/program_facades/native_token_transfer/public.rs index 69185147..e63356e3 100644 --- a/wallet/src/program_facades/native_token_transfer/public.rs +++ b/wallet/src/program_facades/native_token_transfer/public.rs @@ -48,7 +48,7 @@ impl NativeTokenTransfer<'_> { e.to_string(), )) })?; - let msg_hash = message.hash_message(); + let msg_hash = message.hash(); let (from_sig, from_pk) = KeycardWallet::sign_message_for_path_with_connect(&pin, from_key_path, &msg_hash)?; if let Some(to_key_path) = to_key_path { @@ -99,11 +99,8 @@ impl NativeTokenTransfer<'_> { e.to_string(), )) })?; - let (signature, pub_key) = KeycardWallet::sign_message_for_path_with_connect( - &pin, - key_path, - &message.hash_message(), - )?; + let (signature, pub_key) = + KeycardWallet::sign_message_for_path_with_connect(&pin, key_path, &message.hash())?; WitnessSet::from_list(&message, &[signature], &[pub_key]) .map_err(ExecutionFailureKind::TransactionBuildError)? } else { diff --git a/wallet/src/program_facades/native_token_transfer/shielded.rs b/wallet/src/program_facades/native_token_transfer/shielded.rs index 59b6a306..b9c70aa5 100644 --- a/wallet/src/program_facades/native_token_transfer/shielded.rs +++ b/wallet/src/program_facades/native_token_transfer/shielded.rs @@ -11,7 +11,6 @@ impl NativeTokenTransfer<'_> { from: AccountId, to: AccountId, balance_to_move: u128, - pin: &Option, from_key_path: &Option, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); @@ -25,7 +24,6 @@ impl NativeTokenTransfer<'_> { instruction_data, &program.into(), tx_pre_check, - pin, from_key_path, ) .await @@ -45,7 +43,6 @@ impl NativeTokenTransfer<'_> { to_vpk: ViewingPublicKey, to_identifier: Identifier, balance_to_move: u128, - pin: &Option, from_key_path: &Option, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let (instruction_data, program, tx_pre_check) = auth_transfer_preparation(balance_to_move); @@ -63,7 +60,6 @@ impl NativeTokenTransfer<'_> { instruction_data, &program.into(), tx_pre_check, - pin, from_key_path, ) .await diff --git a/wallet/src/program_facades/pinata.rs b/wallet/src/program_facades/pinata.rs index f30fcd5c..0d633dcc 100644 --- a/wallet/src/program_facades/pinata.rs +++ b/wallet/src/program_facades/pinata.rs @@ -61,7 +61,6 @@ impl Pinata<'_> { nssa::program::Program::serialize_instruction(solution).unwrap(), &nssa::program::Program::pinata().into(), &None, - &None, ) .await .map(|(resp, secrets)| { diff --git a/wallet/src/program_facades/token.rs b/wallet/src/program_facades/token.rs index c2aa5145..7269cc16 100644 --- a/wallet/src/program_facades/token.rs +++ b/wallet/src/program_facades/token.rs @@ -1,7 +1,8 @@ use common::{HashType, transaction::NSSATransaction}; use keycard_wallet::KeycardWallet; -use nssa::{AccountId, program::Program, public_transaction::WitnessSet}; -use nssa_core::{NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; +use nssa::{AccountId, Signature, PublicKey, program::Program, public_transaction::WitnessSet}; +use nssa_core::{Identifier, NullifierPublicKey, SharedSecretKey, encryption::ViewingPublicKey}; +use pyo3::exceptions::PyRuntimeError; use sequencer_service_rpc::RpcClient as _; use token_core::Instruction; @@ -16,6 +17,8 @@ impl Token<'_> { supply_account_id: AccountId, name: String, total_supply: u128, + definition_key_path: Option<&str>, + supply_key_path: Option<&str>, ) -> Result { let account_ids = vec![definition_account_id, supply_account_id]; let program_id = nssa::program::Program::token().id(); @@ -33,23 +36,47 @@ impl Token<'_> { ) .unwrap(); - let def_private_key = self - .0 - .storage - .user_data - .get_pub_account_signing_key(definition_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - let supply_private_key = self - .0 - .storage - .user_data - .get_pub_account_signing_key(supply_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + let msg_hash = message.hash(); + let pin = if definition_key_path.is_some() || supply_key_path.is_some() { + Some(crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?) + } else { + None + }; - let witness_set = nssa::public_transaction::WitnessSet::for_message( + let (sig_def, pk_def) = if let Some(kp) = definition_key_path { + KeycardWallet::sign_message_for_path_with_connect(pin.as_ref().unwrap(), kp, &msg_hash)? + } else { + let sk = self + .0 + .storage + .user_data + .get_pub_account_signing_key(definition_account_id) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + (Signature::new(&sk, &msg_hash), PublicKey::new_from_private_key(&sk)) + }; + + let (sig_sup, pk_sup) = if let Some(kp) = supply_key_path { + KeycardWallet::sign_message_for_path_with_connect(pin.as_ref().unwrap(), kp, &msg_hash)? + } else { + let sk = self + .0 + .storage + .user_data + .get_pub_account_signing_key(supply_account_id) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + (Signature::new(&sk, &msg_hash), PublicKey::new_from_private_key(&sk)) + }; + + let witness_set = nssa::public_transaction::WitnessSet::from_list( &message, - &[def_private_key, supply_private_key], - ); + &[sig_def, sig_sup], + &[pk_def, pk_sup], + ) + .map_err(ExecutionFailureKind::TransactionBuildError)?; let tx = nssa::PublicTransaction::new(message, witness_set); @@ -80,7 +107,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -112,7 +138,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -144,7 +169,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -160,7 +184,6 @@ impl Token<'_> { sender_account_id: AccountId, recipient_account_id: AccountId, amount: u128, - pin: Option, sender_key_path: Option, ) -> Result { let account_ids = vec![sender_account_id, recipient_account_id]; @@ -187,19 +210,19 @@ impl Token<'_> { instruction, ) .unwrap(); - - let witness_set = if let Some(pin) = pin { - let sender_public_key = KeycardWallet::get_public_key_for_path_with_connect( + let witness_set = if let Some(sender_key_path) = sender_key_path { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?; + let (signature, public_key) = KeycardWallet::sign_message_for_path_with_connect( &pin, - sender_key_path.as_ref().expect("Expect a key path String."), - ); - let signature = KeycardWallet::sign_message_for_path_with_connect( - &pin, - sender_key_path.as_ref().expect("Expect a key path String."), - &message.hash_message(), + &sender_key_path, + &message.hash(), ) .expect("Expect a valid signature"); - WitnessSet::from_list(&[signature], &[sender_public_key]) + WitnessSet::from_list(&message, &[signature], &[public_key]) } else { let mut private_keys = Vec::new(); let sender_sk = self @@ -223,10 +246,13 @@ impl Token<'_> { ); } - nssa::public_transaction::WitnessSet::for_message(&message, &private_keys) + Ok(nssa::public_transaction::WitnessSet::for_message( + &message, + &private_keys, + )) }; - let tx = nssa::PublicTransaction::new(message, witness_set); + let tx = nssa::PublicTransaction::new(message, witness_set.unwrap()); //TODO: Marvin Ok(self .0 @@ -256,7 +282,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -294,7 +319,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -326,7 +350,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -359,7 +382,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -398,7 +420,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -410,11 +431,88 @@ impl Token<'_> { }) } + pub async fn send_initialize_account( + &self, + definition_account: PrivacyPreservingAccount, + holder_account: PrivacyPreservingAccount, + key_path: &Option, + ) -> Result<(HashType, Vec), ExecutionFailureKind> { + let instruction = Instruction::InitializeAccount; + + if definition_account.is_public() && holder_account.is_public() { + let PrivacyPreservingAccount::Public(definition_account_id) = definition_account else { + unreachable!() + }; + let PrivacyPreservingAccount::Public(holder_account_id) = holder_account else { + unreachable!() + }; + + let nonces = self + .0 + .get_accounts_nonces(vec![holder_account_id]) + .await + .map_err(ExecutionFailureKind::SequencerError)?; + let message = nssa::public_transaction::Message::try_new( + Program::token().id(), + vec![definition_account_id, holder_account_id], + nonces, + instruction, + ) + .expect("Instruction should serialize"); + + let witness_set = if let Some(key_path) = key_path { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError( + pyo3::PyErr::new::(e.to_string()), + ) + })?; + let (signature, pub_key) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( + &pin, + key_path, + &message.hash(), + )?; + nssa::public_transaction::WitnessSet::from_list(&message, &[signature], &[pub_key]) + .map_err(ExecutionFailureKind::TransactionBuildError)? + } else { + let signing_key = self + .0 + .storage + .user_data + .get_pub_account_signing_key(holder_account_id) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]) + }; + + let tx = nssa::PublicTransaction::new(message, witness_set); + + let hash = self + .0 + .sequencer_client + .send_transaction(NSSATransaction::Public(tx)) + .await?; + Ok((hash, vec![])) + } else { + let instruction_data = + Program::serialize_instruction(instruction).expect("Instruction should serialize"); + + self.0 + .send_privacy_preserving_tx( + vec![definition_account, holder_account], + instruction_data, + &Program::token().into(), + key_path, + ) + .await + .map(|(resp, secrets)| (resp, secrets)) + } + } + pub async fn send_burn_transaction( &self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, + holder_key_path: Option<&str>, ) -> Result { let account_ids = vec![definition_account_id, holder_account_id]; let instruction = Instruction::Burn { @@ -434,14 +532,26 @@ impl Token<'_> { ) .expect("Instruction should serialize"); - let signing_key = self - .0 - .storage - .user_data - .get_pub_account_signing_key(holder_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + let msg_hash = message.hash(); + let witness_set = if let Some(kp) = holder_key_path { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?; + let (sig, pk) = + KeycardWallet::sign_message_for_path_with_connect(&pin, kp, &msg_hash)?; + nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk]) + .map_err(ExecutionFailureKind::TransactionBuildError)? + } else { + let signing_key = self + .0 + .storage + .user_data + .get_pub_account_signing_key(holder_account_id) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]) + }; let tx = nssa::PublicTransaction::new(message, witness_set); @@ -473,7 +583,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -505,7 +614,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -538,7 +646,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -555,6 +662,7 @@ impl Token<'_> { definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, + definition_key_path: Option<&str>, ) -> Result { let account_ids = vec![definition_account_id, holder_account_id]; let instruction = Instruction::Mint { @@ -567,22 +675,13 @@ impl Token<'_> { .await .map_err(ExecutionFailureKind::SequencerError)?; - let mut private_keys = Vec::new(); - let definition_sk = self - .0 - .storage - .user_data - .get_pub_account_signing_key(definition_account_id) - .ok_or(ExecutionFailureKind::KeyNotFoundError)?; - private_keys.push(definition_sk); - - if let Some(holder_sk) = self + if self .0 .storage .user_data .get_pub_account_signing_key(holder_account_id) + .is_some() { - private_keys.push(holder_sk); let recipient_nonces = self .0 .get_accounts_nonces(vec![holder_account_id]) @@ -602,8 +701,27 @@ impl Token<'_> { instruction, ) .unwrap(); - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &private_keys); + + let msg_hash = message.hash(); + let witness_set = if let Some(kp) = definition_key_path { + let pin = crate::helperfunctions::read_pin().map_err(|e| { + ExecutionFailureKind::KeycardError(pyo3::PyErr::new::( + e.to_string(), + )) + })?; + let (sig, pk) = + KeycardWallet::sign_message_for_path_with_connect(&pin, kp, &msg_hash)?; + nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk]) + .map_err(ExecutionFailureKind::TransactionBuildError)? + } else { + let signing_key = self + .0 + .storage + .user_data + .get_pub_account_signing_key(definition_account_id) + .ok_or(ExecutionFailureKind::KeyNotFoundError)?; + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]) + }; let tx = nssa::PublicTransaction::new(message, witness_set); @@ -635,7 +753,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -673,7 +790,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -705,7 +821,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -738,7 +853,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| { @@ -777,7 +891,6 @@ impl Token<'_> { instruction_data, &Program::token().into(), &None, - &None, ) .await .map(|(resp, secrets)| {