From ffa6ceb7c0c2968dc2af755645782724fbc37c9d Mon Sep 17 00:00:00 2001 From: Hansie Odendaal Date: Wed, 31 Jul 2024 14:48:01 +0200 Subject: [PATCH] testing spend --- .../src/automation/commands.rs | 53 +++- .../src/automation/mod.rs | 1 - .../comms/src/accessor_methods.rs | 5 +- .../wallet_output_builder.rs | 10 +- .../wallet/src/transaction_service/handle.rs | 49 ---- .../wallet/src/transaction_service/service.rs | 259 +----------------- 6 files changed, 65 insertions(+), 312 deletions(-) diff --git a/applications/minotari_console_wallet/src/automation/commands.rs b/applications/minotari_console_wallet/src/automation/commands.rs index 00c3d2eeca1..4667c3637a4 100644 --- a/applications/minotari_console_wallet/src/automation/commands.rs +++ b/applications/minotari_console_wallet/src/automation/commands.rs @@ -221,6 +221,8 @@ async fn finalise_aggregate_utxo( script_signatures: Vec, wallet_script_secret_key: PrivateKey, ) -> Result { + trace!(target: LOG_TARGET, "finalise_aggregate_utxo: start"); + let mut meta_sig = Signature::default(); for sig in &meta_signatures { meta_sig = &meta_sig + sig; @@ -229,6 +231,7 @@ async fn finalise_aggregate_utxo( for sig in &script_signatures { script_sig = &script_sig + sig; } + trace!(target: LOG_TARGET, "finalise_aggregate_utxo: aggregated signatures"); wallet_transaction_service .finalize_aggregate_utxo(tx_id, meta_sig, script_sig, wallet_script_secret_key) @@ -807,7 +810,7 @@ pub async fn command_runner( break; }, }; - let signature = match key_manager_service + let verification_signature = match key_manager_service .sign_script_message(&key_id, PrivateKey::from(index).as_bytes()) .await { @@ -821,7 +824,7 @@ pub async fn command_runner( outputs_for_leader.push(PreMineCreateStep1ForLeader { index, script_public_key, - verification_signature: signature, + verification_signature, }); } if error { @@ -1186,6 +1189,14 @@ pub async fn command_runner( println!(); }, PreMineSpendSessionInfo(args) => { + match key_manager_service.get_wallet_type().await { + WalletType::Ledger(_) => {}, + _ => { + eprintln!("\nError: Wallet type must be 'Ledger' to spend pre-mine outputs!\n"); + break; + }, + } + let embedded_output = match get_embedded_pre_mine_outputs(vec![args.output_index]) { Ok(outputs) => outputs[0].clone(), Err(e) => { @@ -1240,6 +1251,14 @@ pub async fn command_runner( println!(); }, PreMineSpendPartyDetails(args) => { + match key_manager_service.get_wallet_type().await { + WalletType::Ledger(_) => {}, + _ => { + eprintln!("\nError: Wallet type must be 'Ledger' to spend pre-mine outputs!\n"); + break; + }, + } + if args.alias.is_empty() || args.alias.contains(" ") { eprintln!("\nError: Alias cannot contain spaces!\n"); break; @@ -1313,15 +1332,13 @@ pub async fn command_runner( break; }, }; - let script_input_signature = key_manager_service - .sign_script_message(&wallet_spend_key.key_id, commitment.as_bytes()) + .sign_script_message(&pre_mine_script_key_id, commitment.as_bytes()) .await?; let out_dir = out_dir(&session_info.session_id, Context::Spend)?; let step_2_outputs_for_leader = PreMineSpendStep2OutputsForLeader { script_input_signature, - wallet_public_spend_key: wallet_spend_key.pub_key, public_script_nonce_key: script_nonce_key.pub_key, public_sender_offset_key: sender_offset_key.pub_key, public_sender_offset_nonce_key: sender_offset_nonce.pub_key, @@ -1355,6 +1372,14 @@ pub async fn command_runner( println!(); }, PreMineSpendEncumberAggregateUtxo(args) => { + match key_manager_service.get_wallet_type().await { + WalletType::Ledger(_) => {}, + _ => { + eprintln!("\nError: Wallet type must be 'Ledger' to spend pre-mine outputs!\n"); + break; + }, + } + // Read session info let session_info = read_verify_session_info::(&args.session_id)?; @@ -1441,6 +1466,14 @@ pub async fn command_runner( } }, PreMineSpendInputOutputSigs(args) => { + match key_manager_service.get_wallet_type().await { + WalletType::Ledger(_) => {}, + _ => { + eprintln!("\nError: Wallet type must be 'Ledger' to spend pre-mine outputs!\n"); + break; + }, + } + // Read session info let session_info = read_verify_session_info::(&args.session_id)?; // Read leader input @@ -1484,7 +1517,7 @@ pub async fn command_runner( // Metadata signature let script_offset = key_manager_service - .get_script_offset(&vec![party_info.wallet_spend_key_id], &vec![party_info + .get_script_offset(&vec![party_info.pre_mine_script_key_id], &vec![party_info .sender_offset_key_id .clone()]) .await?; @@ -1545,6 +1578,14 @@ pub async fn command_runner( } }, PreMineSpendAggregateTransaction(args) => { + match key_manager_service.get_wallet_type().await { + WalletType::Ledger(_) => {}, + _ => { + eprintln!("\nError: Wallet type must be 'Ledger' to spend pre-mine outputs!\n"); + break; + }, + } + // Read session info let session_info = read_verify_session_info::(&args.session_id)?; diff --git a/applications/minotari_console_wallet/src/automation/mod.rs b/applications/minotari_console_wallet/src/automation/mod.rs index 27ab569548c..59caf406198 100644 --- a/applications/minotari_console_wallet/src/automation/mod.rs +++ b/applications/minotari_console_wallet/src/automation/mod.rs @@ -79,7 +79,6 @@ struct PreMineSpendStep2OutputsForSelf { #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)] struct PreMineSpendStep2OutputsForLeader { script_input_signature: CheckSigSchnorrSignature, - wallet_public_spend_key: PublicKey, public_script_nonce_key: PublicKey, public_sender_offset_key: PublicKey, public_sender_offset_nonce_key: PublicKey, diff --git a/applications/minotari_ledger_wallet/comms/src/accessor_methods.rs b/applications/minotari_ledger_wallet/comms/src/accessor_methods.rs index abf94f2d33e..1774436eda1 100644 --- a/applications/minotari_ledger_wallet/comms/src/accessor_methods.rs +++ b/applications/minotari_ledger_wallet/comms/src/accessor_methods.rs @@ -48,7 +48,10 @@ pub fn verify_ledger_application() -> Result<(), LedgerDeviceError> { if let Ok(mut verified) = VERIFIED.try_lock() { if verified.is_none() { match verify() { - Ok(_) => *verified = Some(Ok(())), + Ok(_) => { + debug!(target: LOG_TARGET, "Ledger application 'Minotari Wallet' running and verified"); + *verified = Some(Ok(())) + }, Err(e) => return Err(e), } } diff --git a/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs b/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs index 0bae2d5c6a6..ba011105540 100644 --- a/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs +++ b/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs @@ -21,10 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use derivative::Derivative; -use tari_common_types::{ - key_branches::TransactionKeyManagerBranch, - types::{ComAndPubSignature, PublicKey}, -}; +use tari_common_types::types::{ComAndPubSignature, PublicKey}; use tari_script::{ExecutionStack, TariScript}; use crate::{ @@ -229,9 +226,7 @@ impl WalletOutputBuilder { let aggregate_sender_offset_public_key = aggregated_sender_offset_public_key_shares + &sender_offset_public_key_self; - let ephemeral_pubkey_self = key_manager - .get_next_key(TransactionKeyManagerBranch::MetadataEphemeralNonce.get_branch_key()) - .await?; + let ephemeral_pubkey_self = key_manager.get_random_key().await?; let aggregate_ephemeral_pubkey = aggregated_ephemeral_public_key_shares + &ephemeral_pubkey_self.pub_key; let receiver_partial_metadata_signature = key_manager @@ -314,6 +309,7 @@ impl WalletOutputBuilder { #[cfg(test)] mod test { + use tari_common_types::key_branches::TransactionKeyManagerBranch; use tari_key_manager::key_manager_service::KeyManagerInterface; use super::*; diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index 751b670e26d..931751e9c51 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -102,15 +102,6 @@ pub enum TransactionServiceRequest { message: String, claim_public_key: Option, }, - CreateNMUtxo { - amount: MicroMinotari, - fee_per_gram: MicroMinotari, - n: u8, - m: u8, - public_keys: Vec, - message: [u8; 32], - maturity: u64, - }, EncumberAggregateUtxo { fee_per_gram: MicroMinotari, output_hash: HashOutput, @@ -219,18 +210,6 @@ impl fmt::Display for TransactionServiceRequest { amount, destination, message ), Self::BurnTari { amount, message, .. } => write!(f, "Burning Tari ({}, {})", amount, message), - Self::CreateNMUtxo { - amount, - fee_per_gram: _, - n, - m, - public_keys: _, - message: _, - maturity: _, - } => f.write_str(&format!( - "Creating a new n-of-m aggregate uxto with: amount = {}, n = {}, m = {}", - amount, n, m - )), Self::EncumberAggregateUtxo { fee_per_gram, output_hash, @@ -709,34 +688,6 @@ impl TransactionServiceHandle { } } - pub async fn create_aggregate_signature_utxo( - &mut self, - amount: MicroMinotari, - fee_per_gram: MicroMinotari, - n: u8, - m: u8, - public_keys: Vec, - message: [u8; 32], - maturity: u64, - ) -> Result<(TxId, FixedHash), TransactionServiceError> { - match self - .handle - .call(TransactionServiceRequest::CreateNMUtxo { - amount, - fee_per_gram, - n, - m, - public_keys, - message, - maturity, - }) - .await?? - { - TransactionServiceResponse::TransactionSentWithOutputHash(tx_id, output_hash) => Ok((tx_id, output_hash)), - _ => Err(TransactionServiceError::UnexpectedApiResponse), - } - } - #[allow(clippy::mutable_key_type)] pub async fn encumber_aggregate_utxo( &mut self, diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 8455e9723f5..dda738baffb 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -39,7 +39,7 @@ use tari_common_types::{ key_branches::TransactionKeyManagerBranch, tari_address::{TariAddress, TariAddressFeatures}, transaction::{ImportStatus, TransactionDirection, TransactionStatus, TxId}, - types::{CommitmentFactory, FixedHash, HashOutput, PrivateKey, PublicKey, Signature}, + types::{CommitmentFactory, HashOutput, PrivateKey, PublicKey, Signature}, }; use tari_comms::{types::CommsPublicKey, NodeIdentity}; use tari_comms_dht::outbound::OutboundMessageRequester; @@ -48,21 +48,19 @@ use tari_core::{ covenants::Covenant, mempool::FeePerGramStat, one_sided::{ - public_key_to_output_encryption_key, shared_secret_to_output_encryption_key, shared_secret_to_output_spending_key, stealth_address_script_spending_key, }, proto::{base_node as base_node_proto, base_node::FetchMatchingUtxos}, transactions::{ - key_manager::{TariKeyId, TransactionKeyManagerInterface}, + key_manager::TransactionKeyManagerInterface, tari_amount::MicroMinotari, transaction_components::{ encrypted_data::PaymentId, CodeTemplateRegistration, KernelFeatures, OutputFeatures, - RangeProofType, Transaction, TransactionOutput, WalletOutputBuilder, @@ -84,15 +82,7 @@ use tari_crypto::{ }; use tari_key_manager::key_manager_service::KeyId; use tari_p2p::domain_message::DomainMessage; -use tari_script::{ - push_pubkey_script, - script, - slice_to_boxed_message, - CheckSigSchnorrSignature, - ExecutionStack, - ScriptContext, - TariScript, -}; +use tari_script::{push_pubkey_script, script, CheckSigSchnorrSignature, ExecutionStack, ScriptContext, TariScript}; use tari_service_framework::{reply_channel, reply_channel::Receiver}; use tari_shutdown::ShutdownSignal; use tokio::{ @@ -702,29 +692,6 @@ where tx_id, proof: Box::new(proof), }), - TransactionServiceRequest::CreateNMUtxo { - amount, - fee_per_gram, - n, - m, - public_keys, - message, - maturity, - } => self - .create_aggregate_signature_utxo( - amount, - fee_per_gram, - n, - m, - public_keys, - message, - maturity, - transaction_broadcast_join_handles, - ) - .await - .map(|(tx_id, output_hash)| { - TransactionServiceResponse::TransactionSentWithOutputHash(tx_id, output_hash) - }), TransactionServiceRequest::EncumberAggregateUtxo { fee_per_gram, output_hash, @@ -1180,218 +1147,6 @@ where Ok(()) } - /// Creates a utxo with aggregate public key out of m-of-n public keys - #[allow(clippy::too_many_lines)] - pub async fn create_aggregate_signature_utxo( - &mut self, - amount: MicroMinotari, - fee_per_gram: MicroMinotari, - n: u8, - m: u8, - public_keys: Vec, - message: [u8; 32], - maturity: u64, - transaction_broadcast_join_handles: &mut FuturesUnordered< - JoinHandle>>, - >, - ) -> Result<(TxId, FixedHash), TransactionServiceError> { - let tx_id = TxId::new_random(); - - let msg = slice_to_boxed_message(message.as_bytes()); - let script = script!(CheckMultiSigVerifyAggregatePubKey(n, m, public_keys.clone(), msg)); - - // Empty covenant - let covenant = Covenant::default(); - - // Default range proof - let minimum_value_promise = amount; - - // Prepare sender part of transaction - let mut stp = self - .resources - .output_manager_service - .prepare_transaction_to_send( - tx_id, - amount, - UtxoSelectionCriteria::default(), - OutputFeatures { - range_proof_type: RangeProofType::RevealedValue, - maturity, - ..Default::default() - }, - fee_per_gram, - TransactionMetadata::default(), - "".to_string(), - script.clone(), - covenant.clone(), - minimum_value_promise, - ) - .await?; - let sender_message = TransactionSenderMessage::new_single_round_message( - stp.get_single_round_message(&self.resources.transaction_key_manager_service) - .await?, - ); - - // This call is needed to advance the state from `SingleRoundMessageReady` to `CollectingSingleSignature`, - // but the returned value is not used - let _single_round_sender_data = stp - .build_single_round_message(&self.resources.transaction_key_manager_service) - .await - .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; - - self.resources - .output_manager_service - .confirm_pending_transaction(tx_id) - .await - .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; - - // Prepare receiver part of the transaction - - // In generating an aggregate public key utxo, we can use a randomly generated spend key - let spending_key = PrivateKey::random(&mut OsRng); - let sum_keys = public_keys.iter().fold(PublicKey::default(), |acc, x| acc + x); - let encryption_private_key = public_key_to_output_encryption_key(&sum_keys)?; - - let sender_offset_private_key = stp - .get_recipient_sender_offset_private_key() - .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))? - .ok_or(TransactionServiceProtocolError::new( - tx_id, - TransactionServiceError::InvalidKeyId("Missing sender offset keyid".to_string()), - ))?; - - let encryption_key_id = self - .resources - .transaction_key_manager_service - .import_key(encryption_private_key) - .await?; - - let sender_offset_public_key = self - .resources - .transaction_key_manager_service - .get_public_key_at_key_id(&sender_offset_private_key) - .await?; - - let spending_key_id = self - .resources - .transaction_key_manager_service - .import_key(spending_key.clone()) - .await?; - - let wallet_output = WalletOutputBuilder::new(amount, spending_key_id) - .with_features( - sender_message - .single() - .ok_or(TransactionServiceProtocolError::new( - tx_id, - TransactionServiceError::InvalidMessageError("Sent invalid message type".to_string()), - ))? - .features - .clone(), - ) - .with_script(script) - // We don't want the given utxo to be spendable as an input to a later transaction, so we set - // spendable height of the current utxo to be u64::MAx - .with_script_lock_height(u64::MAX) - .encrypt_data_for_recovery( - &self.resources.transaction_key_manager_service, - Some(&encryption_key_id), - PaymentId::Empty, - ) - .await? - .with_input_data( - ExecutionStack::default(), - ) - .with_covenant(covenant) - .with_sender_offset_public_key(sender_offset_public_key) - .with_script_key(TariKeyId::default()) - .with_minimum_value_promise(minimum_value_promise) - .sign_as_sender_and_receiver( - &self.resources.transaction_key_manager_service, - &sender_offset_private_key, - ) - .await - .unwrap() - .try_build(&self.resources.transaction_key_manager_service) - .await - .unwrap(); - - let tip_height = self.last_seen_tip_height.unwrap_or(0); - let consensus_constants = self.consensus_manager.consensus_constants(tip_height); - let rtp = ReceiverTransactionProtocol::new( - sender_message, - wallet_output.clone(), - &self.resources.transaction_key_manager_service, - consensus_constants, - ) - .await; - let recipient_reply = rtp.get_signed_data()?.clone(); - - // Start finalize - stp.add_single_recipient_info(recipient_reply, &self.resources.transaction_key_manager_service) - .await - .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; - - // Finalize: - stp.finalize(&self.resources.transaction_key_manager_service) - .await - .map_err(|e| { - error!( - target: LOG_TARGET, - "Transaction (TxId: {}) could not be finalized. Failure error: {:?}", tx_id, e, - ); - TransactionServiceProtocolError::new(tx_id, e.into()) - })?; - info!( - target: LOG_TARGET, - "Finalized create n of m transaction TxId: {}", tx_id - ); - - // This event being sent is important, but not critical to the protocol being successful. Send only fails if - // there are no subscribers. - let _size = self - .event_publisher - .send(Arc::new(TransactionEvent::TransactionCompletedImmediately(tx_id))); - - // Broadcast create n of m aggregate public key transaction - let tx = stp - .get_transaction() - .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; - let fee = stp - .get_fee_amount() - .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?; - self.resources - .output_manager_service - .add_output_with_tx_id(tx_id, wallet_output.clone(), Some(SpendingPriority::Normal)) - .await?; - self.submit_transaction( - transaction_broadcast_join_handles, - CompletedTransaction::new( - tx_id, - self.resources.interactive_tari_address.clone(), - self.resources.interactive_tari_address.clone(), - amount, - fee, - tx.clone(), - TransactionStatus::Completed, - "".to_string(), - Utc::now().naive_utc(), - TransactionDirection::Outbound, - None, - None, - None, - ) - .map_err(|e| TransactionServiceProtocolError::new(tx_id, e.into()))?, - ) - .await?; - - // we want to print out the hash of the utxo - let output_hash = wallet_output - .hash(&self.resources.transaction_key_manager_service) - .await?; - Ok((tx_id, output_hash)) - } - async fn fetch_unspent_outputs_from_node( &mut self, hashes: Vec, @@ -1501,7 +1256,9 @@ where JoinHandle>>, >, ) -> Result { + trace!(target: LOG_TARGET, "finalized_aggregate_encumbed_tx: start"); let mut transaction = self.db.get_completed_transaction(tx_id)?; + trace!(target: LOG_TARGET, "finalized_aggregate_encumbed_tx: completed_transaction"); // Add the aggregate signature components transaction.transaction.script_offset = &transaction.transaction.script_offset + &script_offset; @@ -1510,11 +1267,13 @@ where &(transaction.transaction.body.outputs()[0].commitment.clone()), &transaction.transaction.body.outputs()[0].metadata_signature + &total_meta_data_signature, )?; + trace!(target: LOG_TARGET, "finalized_aggregate_encumbed_tx: updated metadata_signature"); transaction.transaction.body.update_script_signature( &(transaction.transaction.body.inputs()[0].commitment()?.clone()), &transaction.transaction.body.inputs()[0].script_signature + &total_script_data_signature, )?; + trace!(target: LOG_TARGET, "finalized_aggregate_encumbed_tx: updated script_signature"); // Validate the aggregate signatures and script offset let factory = CommitmentFactory::default(); @@ -1527,11 +1286,13 @@ where .commitment() .map_err(|e| TransactionServiceError::ServiceError(format!("TxId: {}, {}", tx_id, e)))?, ); + trace!(target: LOG_TARGET, "finalized_aggregate_encumbed_tx: input_data {:?}", input.input_data); input_keys = input_keys + input .run_and_verify_script(&factory, Some(context)) .map_err(|e| TransactionServiceError::ServiceError(format!("TxId: {}, {}", tx_id, e)))?; } + trace!(target: LOG_TARGET, "finalized_aggregate_encumbed_tx: validated inputs"); let mut output_keys = PublicKey::default(); for output in transaction.transaction.body.outputs() { output @@ -1539,6 +1300,7 @@ where .map_err(|e| TransactionServiceError::ServiceError(format!("TxId: {}, {}", tx_id, e)))?; output_keys = output_keys + output.sender_offset_public_key.clone(); } + trace!(target: LOG_TARGET, "finalized_aggregate_encumbed_tx: validated outputs"); let lhs = input_keys - output_keys; if lhs != PublicKey::from_secret_key(&transaction.transaction.script_offset) { return Err(TransactionServiceError::ServiceError(format!( @@ -1546,6 +1308,7 @@ where tx_id ))); } + trace!(target: LOG_TARGET, "finalized_aggregate_encumbed_tx: validated script offstet"); // Update the wallet database let _res = self