use common::{HashType, transaction::NSSATransaction}; use keycard_wallet::KeycardWallet; use nssa::{AccountId, PublicKey, Signature, 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; use crate::{ExecutionFailureKind, PrivacyPreservingAccount, WalletCore}; pub struct Token<'wallet>(pub &'wallet WalletCore); impl Token<'_> { pub async fn send_new_definition( &self, definition_account_id: AccountId, 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(); let instruction = Instruction::NewFungibleDefinition { name, total_supply }; let nonces = self .0 .get_accounts_nonces(account_ids.clone()) .await .map_err(ExecutionFailureKind::SequencerError)?; let message = nssa::public_transaction::Message::try_new( program_id, account_ids, nonces, instruction, ) .unwrap(); 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 (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, &[sig_def, sig_sup], &[pk_def, pk_sup], ) .map_err(ExecutionFailureKind::TransactionBuildError)?; let tx = nssa::PublicTransaction::new(message, witness_set); Ok(self .0 .sequencer_client .send_transaction(NSSATransaction::Public(tx)) .await?) } pub async fn send_new_definition_private_owned_supply( &self, definition_account_id: AccountId, supply_account_id: AccountId, name: String, total_supply: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::NewFungibleDefinition { name, total_supply }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::Public(definition_account_id), PrivacyPreservingAccount::PrivateOwned(supply_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let first = secrets .into_iter() .next() .expect("expected supply's secret"); (resp, first) }) } pub async fn send_new_definition_private_owned_definiton( &self, definition_account_id: AccountId, supply_account_id: AccountId, name: String, total_supply: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::NewFungibleDefinition { name, total_supply }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::PrivateOwned(definition_account_id), PrivacyPreservingAccount::Public(supply_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let first = secrets .into_iter() .next() .expect("expected definition's secret"); (resp, first) }) } pub async fn send_new_definition_private_owned_definiton_and_supply( &self, definition_account_id: AccountId, supply_account_id: AccountId, name: String, total_supply: u128, ) -> Result<(HashType, [SharedSecretKey; 2]), ExecutionFailureKind> { let instruction = Instruction::NewFungibleDefinition { name, total_supply }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::PrivateOwned(definition_account_id), PrivacyPreservingAccount::PrivateOwned(supply_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let mut iter = secrets.into_iter(); let first = iter.next().expect("expected definition's secret"); let second = iter.next().expect("expected supply's secret"); (resp, [first, second]) }) } pub async fn send_transfer_transaction( &self, sender_account_id: AccountId, recipient_account_id: AccountId, amount: u128, sender_key_path: Option, ) -> Result { let account_ids = vec![sender_account_id, recipient_account_id]; let program_id = nssa::program::Program::token().id(); let instruction = Instruction::Transfer { amount_to_transfer: amount, }; // Only the sender authorises a token Transfer — the recipient holding must already be // initialised (no recipient signature required, matching the burn pattern). let nonces = self .0 .get_accounts_nonces(vec![sender_account_id]) .await .map_err(ExecutionFailureKind::SequencerError)?; let message = nssa::public_transaction::Message::try_new( program_id, account_ids, nonces, instruction, ) .unwrap(); 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, &message.hash(), )?; WitnessSet::from_list(&message, &[signature], &[public_key]) .map_err(ExecutionFailureKind::TransactionBuildError)? } else { let sender_sk = self .0 .storage .user_data .get_pub_account_signing_key(sender_account_id) .ok_or(ExecutionFailureKind::KeyNotFoundError)?; nssa::public_transaction::WitnessSet::for_message(&message, &[sender_sk]) }; let tx = nssa::PublicTransaction::new(message, witness_set); Ok(self .0 .sequencer_client .send_transaction(NSSATransaction::Public(tx)) .await?) } pub async fn send_transfer_transaction_private_owned_account( &self, sender_account_id: AccountId, recipient_account_id: AccountId, amount: u128, ) -> Result<(HashType, [SharedSecretKey; 2]), ExecutionFailureKind> { let instruction = Instruction::Transfer { amount_to_transfer: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::PrivateOwned(sender_account_id), PrivacyPreservingAccount::PrivateOwned(recipient_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let mut iter = secrets.into_iter(); let first = iter.next().expect("expected sender's secret"); let second = iter.next().expect("expected recipient's secret"); (resp, [first, second]) }) } pub async fn send_transfer_transaction_private_foreign_account( &self, sender_account_id: AccountId, recipient_npk: NullifierPublicKey, recipient_vpk: ViewingPublicKey, recipient_identifier: Identifier, amount: u128, ) -> Result<(HashType, [SharedSecretKey; 2]), ExecutionFailureKind> { let instruction = Instruction::Transfer { amount_to_transfer: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::PrivateOwned(sender_account_id), PrivacyPreservingAccount::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: recipient_identifier, }, ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let mut iter = secrets.into_iter(); let first = iter.next().expect("expected sender's secret"); let second = iter.next().expect("expected recipient's secret"); (resp, [first, second]) }) } pub async fn send_transfer_transaction_deshielded( &self, sender_account_id: AccountId, recipient_account_id: AccountId, amount: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::Transfer { amount_to_transfer: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::PrivateOwned(sender_account_id), PrivacyPreservingAccount::Public(recipient_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let first = secrets .into_iter() .next() .expect("expected sender's secret"); (resp, first) }) } pub async fn send_transfer_transaction_shielded_owned_account( &self, sender_account_id: AccountId, recipient_account_id: AccountId, amount: u128, sender_key_path: Option, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::Transfer { amount_to_transfer: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::Public(sender_account_id), PrivacyPreservingAccount::PrivateOwned(recipient_account_id), ], instruction_data, &Program::token().into(), &sender_key_path, ) .await .map(|(resp, secrets)| { let first = secrets .into_iter() .next() .expect("expected recipient's secret"); (resp, first) }) } pub async fn send_transfer_transaction_shielded_foreign_account( &self, sender_account_id: AccountId, recipient_npk: NullifierPublicKey, recipient_vpk: ViewingPublicKey, recipient_identifier: Identifier, amount: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::Transfer { amount_to_transfer: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::Public(sender_account_id), PrivacyPreservingAccount::PrivateForeign { npk: recipient_npk, vpk: recipient_vpk, identifier: recipient_identifier, }, ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let first = secrets .into_iter() .next() .expect("expected recipient's secret"); (resp, first) }) } 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::< pyo3::exceptions::PyRuntimeError, _, >(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 } } 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 { amount_to_burn: amount, }; 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(), account_ids, nonces, instruction, ) .expect("Instruction should serialize"); 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); Ok(self .0 .sequencer_client .send_transaction(NSSATransaction::Public(tx)) .await?) } pub async fn send_burn_transaction_private_owned_account( &self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, ) -> Result<(HashType, [SharedSecretKey; 2]), ExecutionFailureKind> { let instruction = Instruction::Burn { amount_to_burn: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::PrivateOwned(definition_account_id), PrivacyPreservingAccount::PrivateOwned(holder_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let mut iter = secrets.into_iter(); let first = iter.next().expect("expected definition's secret"); let second = iter.next().expect("expected holder's secret"); (resp, [first, second]) }) } pub async fn send_burn_transaction_deshielded_owned_account( &self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::Burn { amount_to_burn: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::PrivateOwned(definition_account_id), PrivacyPreservingAccount::Public(holder_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let first = secrets .into_iter() .next() .expect("expected definition's secret"); (resp, first) }) } pub async fn send_burn_transaction_shielded( &self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::Burn { amount_to_burn: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::Public(definition_account_id), PrivacyPreservingAccount::PrivateOwned(holder_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let first = secrets .into_iter() .next() .expect("expected holder's secret"); (resp, first) }) } pub async fn send_mint_transaction( &self, 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 { amount_to_mint: amount, }; let mut nonces = self .0 .get_accounts_nonces(vec![definition_account_id]) .await .map_err(ExecutionFailureKind::SequencerError)?; if self .0 .storage .user_data .get_pub_account_signing_key(holder_account_id) .is_some() { let recipient_nonces = self .0 .get_accounts_nonces(vec![holder_account_id]) .await .map_err(ExecutionFailureKind::SequencerError)?; nonces.extend(recipient_nonces); } else { println!( "Holder's account ({holder_account_id}) private key not found in wallet. Proceeding with only definition's key." ); } let message = nssa::public_transaction::Message::try_new( Program::token().id(), account_ids, nonces, instruction, ) .unwrap(); 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); Ok(self .0 .sequencer_client .send_transaction(NSSATransaction::Public(tx)) .await?) } pub async fn send_mint_transaction_private_owned_account( &self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, ) -> Result<(HashType, [SharedSecretKey; 2]), ExecutionFailureKind> { let instruction = Instruction::Mint { amount_to_mint: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::PrivateOwned(definition_account_id), PrivacyPreservingAccount::PrivateOwned(holder_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let mut iter = secrets.into_iter(); let first = iter.next().expect("expected definition's secret"); let second = iter.next().expect("expected holder's secret"); (resp, [first, second]) }) } pub async fn send_mint_transaction_private_foreign_account( &self, definition_account_id: AccountId, holder_npk: NullifierPublicKey, holder_vpk: ViewingPublicKey, holder_identifier: Identifier, amount: u128, ) -> Result<(HashType, [SharedSecretKey; 2]), ExecutionFailureKind> { let instruction = Instruction::Mint { amount_to_mint: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::PrivateOwned(definition_account_id), PrivacyPreservingAccount::PrivateForeign { npk: holder_npk, vpk: holder_vpk, identifier: holder_identifier, }, ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let mut iter = secrets.into_iter(); let first = iter.next().expect("expected definition's secret"); let second = iter.next().expect("expected holder's secret"); (resp, [first, second]) }) } pub async fn send_mint_transaction_deshielded( &self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::Mint { amount_to_mint: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::PrivateOwned(definition_account_id), PrivacyPreservingAccount::Public(holder_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let first = secrets .into_iter() .next() .expect("expected definition's secret"); (resp, first) }) } pub async fn send_mint_transaction_shielded_owned_account( &self, definition_account_id: AccountId, holder_account_id: AccountId, amount: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::Mint { amount_to_mint: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::Public(definition_account_id), PrivacyPreservingAccount::PrivateOwned(holder_account_id), ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let first = secrets .into_iter() .next() .expect("expected holder's secret"); (resp, first) }) } pub async fn send_mint_transaction_shielded_foreign_account( &self, definition_account_id: AccountId, holder_npk: NullifierPublicKey, holder_vpk: ViewingPublicKey, holder_identifier: Identifier, amount: u128, ) -> Result<(HashType, SharedSecretKey), ExecutionFailureKind> { let instruction = Instruction::Mint { amount_to_mint: amount, }; let instruction_data = Program::serialize_instruction(instruction).expect("Instruction should serialize"); self.0 .send_privacy_preserving_tx( vec![ PrivacyPreservingAccount::Public(definition_account_id), PrivacyPreservingAccount::PrivateForeign { npk: holder_npk, vpk: holder_vpk, identifier: holder_identifier, }, ], instruction_data, &Program::token().into(), &None, ) .await .map(|(resp, secrets)| { let first = secrets .into_iter() .next() .expect("expected holder's secret"); (resp, first) }) } }