From 475ed509d16f84c82aa59b1d5325c41fb245f9f7 Mon Sep 17 00:00:00 2001 From: Hansie Odendaal Date: Mon, 13 Jan 2025 14:51:37 +0200 Subject: [PATCH 1/4] Update payment id - Updated `PaymentId::Open, AddressAndData, TransactionInfo` to reflect the transaction type for all defined types in `pub enum TxType`. - Fixed transaction identification for all defined types in `pub enum TxType`. - Added method `transaction_type_from_encrypted_data` to the wallet FFI and a unit test to test all cases. - Added wallet FFI test cases to ensure coin-join and coin-split transactions are correctly flagged in the payment ID. --- .../src/automation/commands.rs | 58 +- .../src/grpc/wallet_grpc_server.rs | 16 +- .../src/ui/components/burn_tab.rs | 11 +- .../src/ui/components/send_tab.rs | 17 +- .../src/ui/components/transactions_tab.rs | 57 +- .../src/ui/state/app_state.rs | 18 +- .../src/ui/state/tasks.rs | 6 +- .../commands/command/get_chain_metadata.rs | 2 +- applications/minotari_node/src/config.rs | 2 +- .../contacts/src/chat_client/src/config.rs | 2 +- .../src/transactions/key_manager/inner.rs | 15 + .../src/transactions/key_manager/interface.rs | 7 + .../src/transactions/key_manager/wrapper.rs | 13 + .../core/src/transactions/test_helpers.rs | 6 +- .../transaction_components/encrypted_data.rs | 528 ++++++++++-------- .../wallet_output_builder.rs | 1 + .../transaction_protocol/sender.rs | 19 +- .../transaction_initializer.rs | 52 +- .../src/output_manager_service/handle.rs | 7 + .../src/output_manager_service/service.rs | 110 ++-- .../storage/sqlite_db/new_output_sql.rs | 2 + .../wallet/src/transaction_service/service.rs | 135 ++++- .../transaction_service/storage/sqlite_db.rs | 34 +- .../utxo_scanner_service/utxo_scanner_task.rs | 3 +- base_layer/wallet/src/wallet.rs | 15 +- .../transaction_service_tests/service.rs | 88 +-- .../transaction_service_tests/storage.rs | 20 +- .../transaction_protocols.rs | 7 +- .../wallet_ffi/src/callback_handler_tests.rs | 15 +- base_layer/wallet_ffi/src/lib.rs | 384 ++++++++++++- base_layer/wallet_ffi/wallet.h | 31 +- common/config/presets/c_base_node_c.toml | 2 +- integration_tests/src/ffi/ffi_import.rs | 6 + .../tests/steps/wallet_ffi_steps.rs | 12 +- integration_tests/tests/steps/wallet_steps.rs | 192 ++++--- 35 files changed, 1311 insertions(+), 582 deletions(-) diff --git a/applications/minotari_console_wallet/src/automation/commands.rs b/applications/minotari_console_wallet/src/automation/commands.rs index 4b08b1fbbb..4cd51ed5f8 100644 --- a/applications/minotari_console_wallet/src/automation/commands.rs +++ b/applications/minotari_console_wallet/src/automation/commands.rs @@ -81,7 +81,7 @@ use tari_core::{ key_manager::TransactionKeyManagerInterface, tari_amount::{uT, MicroMinotari, Minotari}, transaction_components::{ - encrypted_data::PaymentId, + encrypted_data::{PaymentId, TxType}, EncryptedData, OutputFeatures, Transaction, @@ -793,7 +793,7 @@ pub async fn command_runner( transaction_service.clone(), config.fee_per_gram, args.amount, - PaymentId::open_from_str(&args.payment_id), + PaymentId::open(&args.payment_id, TxType::Burn), ) .await { @@ -978,8 +978,11 @@ pub async fn command_runner( args.fee_per_gram, output_hash, commitment.clone(), - args.recipient_address, - PaymentId::open_from_str(&args.payment_id), + args.recipient_address.clone(), + PaymentId::open( + &args.payment_id, + detect_tx_metadata(&wallet, args.recipient_address).await, + ), ) .await { @@ -1338,14 +1341,17 @@ pub async fn command_runner( sender_offset_public_key_shares, metadata_ephemeral_public_key_shares, dh_shared_secret_shares, - current_recipient_address, + current_recipient_address.clone(), original_maturity, if pre_mine_from_file.is_some() { UseOutput::AsProvided(embedded_output) } else { UseOutput::FromBlockchain(embedded_output.hash()) }, - PaymentId::open_from_str(&args.payment_id), + PaymentId::open( + &args.payment_id, + detect_tx_metadata(&wallet, current_recipient_address).await, + ), ) .await { @@ -1987,8 +1993,8 @@ pub async fn command_runner( transaction_service.clone(), config.fee_per_gram, args.amount, - args.destination, - PaymentId::open_from_str(&args.payment_id), + args.destination.clone(), + PaymentId::open(&args.payment_id, detect_tx_metadata(&wallet, args.destination).await), ) .await { @@ -2005,8 +2011,8 @@ pub async fn command_runner( config.fee_per_gram, args.amount, UtxoSelectionCriteria::default(), - args.destination, - PaymentId::open_from_str(&args.payment_id), + args.destination.clone(), + PaymentId::open(&args.payment_id, detect_tx_metadata(&wallet, args.destination).await), ) .await { @@ -2030,9 +2036,9 @@ pub async fn command_runner( args.start_amount, args.increase_amount, args.start_time.unwrap_or_else(Utc::now), - args.destination, + args.destination.clone(), transaction_type, - PaymentId::open_from_str(&args.payment_id), + PaymentId::open(&args.payment_id, detect_tx_metadata(&wallet, args.destination).await), ) .await { @@ -2044,7 +2050,7 @@ pub async fn command_runner( args.amount_per_split, args.num_splits, args.fee_per_gram, - PaymentId::open_from_str(&args.payment_id), + PaymentId::open(&args.payment_id, TxType::CoinSplit), &mut output_service, &mut transaction_service.clone(), ) @@ -2244,7 +2250,7 @@ pub async fn command_runner( args.amount, UtxoSelectionCriteria::default(), args.destination, - PaymentId::open_from_str(&args.payment_id), + PaymentId::open(&args.payment_id, TxType::ClaimAtomicSwap), ) .await { @@ -2267,7 +2273,7 @@ pub async fn command_runner( hash, args.pre_image.into(), config.fee_per_gram.into(), - PaymentId::open_from_str(&args.payment_id), + PaymentId::open(&args.payment_id, TxType::ClaimAtomicSwap), ) .await { @@ -2287,7 +2293,7 @@ pub async fn command_runner( transaction_service.clone(), hash, config.fee_per_gram.into(), - PaymentId::open_from_str(&args.payment_id), + PaymentId::open(&args.payment_id, TxType::HtlcAtomicSwapRefund), ) .await { @@ -2328,7 +2334,7 @@ pub async fn command_runner( ), UtxoSelectionCriteria::default(), config.fee_per_gram * uT, - PaymentId::open_from_str(&args.payment_id), + PaymentId::open(&args.payment_id, TxType::ValidatorNodeRegistration), ) .await?; debug!(target: LOG_TARGET, "Registering VN tx_id {}", tx_id); @@ -2703,6 +2709,24 @@ pub async fn command_runner( Ok(unban_peer_manager_peers) } +async fn detect_tx_metadata(wallet: &WalletSqlite, destination: TariAddress) -> TxType { + if let Ok(interactive_address) = wallet.get_wallet_interactive_address().await { + if let Ok(one_sided_address) = wallet.get_wallet_one_sided_address().await { + if destination == interactive_address || destination == one_sided_address { + TxType::PaymentToSelf + } else { + TxType::PaymentToOther + } + } else if destination == interactive_address { + TxType::PaymentToSelf + } else { + TxType::PaymentToOther + } + } else { + TxType::PaymentToOther + } +} + async fn temp_ban_peers(wallet: &WalletSqlite, peer_list: &mut Vec) { for peer in peer_list { let _unused = wallet diff --git a/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs index 5e1490d508..44163be640 100644 --- a/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -103,7 +103,7 @@ use tari_core::{ transactions::{ tari_amount::{MicroMinotari, T}, transaction_components::{ - encrypted_data::PaymentId, + encrypted_data::{PaymentId, TxType}, CodeTemplateRegistration, OutputFeatures, OutputType, @@ -414,7 +414,7 @@ impl wallet_server::Wallet for WalletGrpcServer { tx_id, tx, amount, - PaymentId::open_from_str("Claiming HTLC transaction with pre-image"), + PaymentId::open("Claiming HTLC transaction with pre-image", TxType::ClaimAtomicSwap), ) .await { @@ -469,7 +469,7 @@ impl wallet_server::Wallet for WalletGrpcServer { tx_id, tx, amount, - PaymentId::open_from_str("Creating HTLC refund transaction"), + PaymentId::open("Creating HTLC refund transaction", TxType::HtlcAtomicSwapRefund), ) .await { @@ -832,7 +832,7 @@ impl wallet_server::Wallet for WalletGrpcServer { usize::try_from(message.split_count) .map_err(|_| Status::internal("Count not convert u64 to usize".to_string()))?, MicroMinotari::from(message.fee_per_gram), - PaymentId::Empty, + PaymentId::open("Creating coin-split transaction", TxType::CoinSplit), ) .await .map_err(|e| Status::internal(format!("{:?}", e)))?; @@ -983,13 +983,17 @@ impl wallet_server::Wallet for WalletGrpcServer { .map_err(|e| Status::internal(e.to_string()))?; output = output.with_script(script![Nop].map_err(|e| Status::invalid_argument(e.to_string()))?); + let payment_id = PaymentId::open( + &format!("Template registration '{}'", template_name), + TxType::CodeTemplateRegistration, + ); let (tx_id, transaction) = output_manager .create_send_to_self_with_output( vec![output], fee_per_gram.into(), UtxoSelectionCriteria::default(), - PaymentId::open_from_str(&format!("Template registration {}", template_name)), + payment_id.clone(), ) .await .map_err(|e| Status::internal(e.to_string()))?; @@ -1008,7 +1012,7 @@ impl wallet_server::Wallet for WalletGrpcServer { let template_address = reg_output.hash(); transaction_service - .submit_transaction(tx_id, transaction, 0.into(), PaymentId::Empty) + .submit_transaction(tx_id, transaction, 0.into(), payment_id) .await .map_err(|e| Status::internal(e.to_string()))?; diff --git a/applications/minotari_console_wallet/src/ui/components/burn_tab.rs b/applications/minotari_console_wallet/src/ui/components/burn_tab.rs index a8f0dfb291..385e0c02ff 100644 --- a/applications/minotari_console_wallet/src/ui/components/burn_tab.rs +++ b/applications/minotari_console_wallet/src/ui/components/burn_tab.rs @@ -5,7 +5,10 @@ use std::fs; use log::*; use minotari_wallet::output_manager_service::UtxoSelectionCriteria; -use tari_core::transactions::{tari_amount::MicroMinotari, transaction_components::encrypted_data::PaymentId}; +use tari_core::transactions::{ + tari_amount::MicroMinotari, + transaction_components::encrypted_data::{PaymentId, TxType}, +}; use tokio::{runtime::Handle, sync::watch}; use tui::{ backend::Backend, @@ -311,11 +314,7 @@ impl BurnTab { amount.into(), UtxoSelectionCriteria::default(), fee_per_gram, - if self.payment_id_field.is_empty() { - PaymentId::Empty - } else { - PaymentId::open_from_str(&self.payment_id_field) - }, + PaymentId::open(&self.payment_id_field, TxType::Burn), tx, )) { Err(e) => { diff --git a/applications/minotari_console_wallet/src/ui/components/send_tab.rs b/applications/minotari_console_wallet/src/ui/components/send_tab.rs index 108e28ee47..bfbedf07c1 100644 --- a/applications/minotari_console_wallet/src/ui/components/send_tab.rs +++ b/applications/minotari_console_wallet/src/ui/components/send_tab.rs @@ -4,7 +4,10 @@ use log::*; use minotari_wallet::output_manager_service::UtxoSelectionCriteria; use tari_common_types::wallet_types::WalletType; -use tari_core::transactions::{tari_amount::MicroMinotari, transaction_components::encrypted_data::PaymentId}; +use tari_core::transactions::{ + tari_amount::MicroMinotari, + transaction_components::encrypted_data::{PaymentId, TxType}, +}; use tari_utilities::hex::Hex; use tokio::{runtime::Handle, sync::watch}; use tui::{ @@ -284,11 +287,7 @@ impl SendTab { amount.into(), UtxoSelectionCriteria::default(), fee_per_gram, - if self.payment_id_field.is_empty() { - PaymentId::Empty - } else { - PaymentId::open_from_str(&self.payment_id_field) - }, + PaymentId::open(&self.payment_id_field, TxType::PaymentToOther), tx, ), ) { @@ -308,11 +307,7 @@ impl SendTab { amount.into(), UtxoSelectionCriteria::default(), fee_per_gram, - if self.payment_id_field.is_empty() { - PaymentId::Empty - } else { - PaymentId::open_from_str(&self.payment_id_field) - }, + PaymentId::open(&self.payment_id_field, TxType::PaymentToOther), tx, )) { Err(e) => { diff --git a/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs b/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs index 1014cea4bc..ae2ef57c33 100644 --- a/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs +++ b/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs @@ -6,8 +6,11 @@ use std::collections::HashMap; use chrono::{DateTime, Local}; use log::*; use minotari_wallet::transaction_service::storage::models::TxCancellationReason; -use tari_common_types::transaction::{TransactionDirection, TransactionStatus}; -use tari_core::transactions::transaction_components::encrypted_data::PaymentId; +use tari_common_types::{ + tari_address::TariAddress, + transaction::{TransactionDirection, TransactionStatus}, +}; +use tari_core::transactions::transaction_components::encrypted_data::{PaymentId, TxType}; use tokio::runtime::Handle; use tui::{ backend::Backend, @@ -211,22 +214,44 @@ impl TransactionsTab { let cancelled = tx.cancelled.is_some(); let text_color = text_colors.get(&cancelled).unwrap_or(&Color::Reset).to_owned(); - let (status, burn) = if let Some(PaymentId::TransactionInfo { burn, .. }) = tx.payment_id.clone() { - ( - match tx.status { - TransactionStatus::OneSidedUnconfirmed => TransactionStatus::MinedUnconfirmed, - TransactionStatus::OneSidedConfirmed => TransactionStatus::MinedConfirmed, - _ => tx.status.clone(), - }, - burn, - ) - } else { - (tx.status.clone(), tx.burn) + let mut transaction_status = tx.status.clone(); + let mut transaction_type = if tx.burn { TxType::Burn } else { TxType::PaymentToOther }; + if let Some( + PaymentId::Open { tx_type, .. } | + PaymentId::AddressAndData { tx_type, .. } | + PaymentId::TransactionInfo { tx_type, .. }, + ) = tx.payment_id.clone() + { + match tx.status { + TransactionStatus::OneSidedUnconfirmed => transaction_status = TransactionStatus::MinedUnconfirmed, + TransactionStatus::OneSidedConfirmed => transaction_status = TransactionStatus::MinedConfirmed, + _ => {}, + } + transaction_type = tx_type.clone(); }; - let address_text = match (&tx.direction, &tx.coinbase, burn) { + // TODO: Remove when backwards compatibility for `PaymentId::Open` and `PaymentId::AddressAndData` is no + // TODO: longer required. + if let Some(PaymentId::Open { .. } | PaymentId::AddressAndData { .. }) = tx.payment_id.clone() { + if transaction_type == TxType::PaymentToSelf && tx.source_address != tx.destination_address { + transaction_type = TxType::PaymentToOther; + } + if transaction_type == TxType::Burn && tx.destination_address != TariAddress::default() { + transaction_type = TxType::PaymentToOther; + } + } + + let address_text = match (&tx.direction, &tx.coinbase, transaction_type) { (_, true, _) => "Mining reward", - (_, _, true) => "Burned output", + (_, _, TxType::Burn) => "Burned output", + (_, _, TxType::CoinJoin) => "Coin join", + (_, _, TxType::CoinSplit) => "Coin split", + (_, _, TxType::ImportedUtxoNoneRewindable) => "Imported output", + (_, _, TxType::PaymentToSelf) => "Payment to self", + (_, _, TxType::ValidatorNodeRegistration) => "Validator node registration", + (_, _, TxType::CodeTemplateRegistration) => "Code template registration", + (_, _, TxType::HtlcAtomicSwapRefund) => "HTLC atomic swap refund", + (_, _, TxType::ClaimAtomicSwap) => "Claim atomic swap", (TransactionDirection::Outbound, _, _) => &app_state.get_alias(tx.destination_address.to_base58()), _ => &app_state.get_alias(tx.source_address.to_base58()), }; @@ -273,7 +298,7 @@ impl TransactionsTab { } else if tx.cancelled.is_some() { "Rejected".to_string() } else { - status.to_string() + transaction_status.to_string() }; column3_items.push(ListItem::new(Span::styled( status_display, diff --git a/applications/minotari_console_wallet/src/ui/state/app_state.rs b/applications/minotari_console_wallet/src/ui/state/app_state.rs index 973c2a0a80..0877d0a06e 100644 --- a/applications/minotari_console_wallet/src/ui/state/app_state.rs +++ b/applications/minotari_console_wallet/src/ui/state/app_state.rs @@ -60,7 +60,12 @@ use tari_comms::{ use tari_contacts::contacts_service::{handle::ContactsLivenessEvent, types::Contact}; use tari_core::transactions::{ tari_amount::{uT, MicroMinotari}, - transaction_components::{encrypted_data::PaymentId, OutputFeatures, TemplateType, TransactionError}, + transaction_components::{ + encrypted_data::{PaymentId, TxType}, + OutputFeatures, + TemplateType, + TransactionError, + }, weight::TransactionWeight, }; use tari_shutdown::ShutdownSignal; @@ -1208,10 +1213,15 @@ impl CompletedTransactionInfo { let outputs_count = tx.transaction.body.outputs().len(); let coinbase = tx.transaction.body.contains_coinbase(); // Faux transactions for scanned change outputs must correspond to the original transaction - let burn = if let PaymentId::TransactionInfo { burn, .. } = tx.payment_id { - burn + let burn = if tx.transaction.body.contains_burn() { + true + } else if let PaymentId::Open { tx_type, .. } | + PaymentId::AddressAndData { tx_type, .. } | + PaymentId::TransactionInfo { tx_type, .. } = tx.payment_id.clone() + { + tx_type == TxType::Burn } else { - tx.transaction.body.contains_burn() + false }; Ok(Self { diff --git a/applications/minotari_console_wallet/src/ui/state/tasks.rs b/applications/minotari_console_wallet/src/ui/state/tasks.rs index 2c88d104bc..168620f9c2 100644 --- a/applications/minotari_console_wallet/src/ui/state/tasks.rs +++ b/applications/minotari_console_wallet/src/ui/state/tasks.rs @@ -24,7 +24,7 @@ use std::{convert::TryFrom, path::PathBuf}; use blake2::Blake2b; use digest::consts::U64; -use log::{error, warn}; +use log::{debug, error, warn}; use minotari_wallet::{ output_manager_service::UtxoSelectionCriteria, storage::{database::WalletDatabase, sqlite_db::wallet::WalletSqliteDatabase}, @@ -204,6 +204,10 @@ pub async fn send_burn_transaction_task( // burning minotari // ---------------------------------------------------------------------------- + debug!( + target: LOG_TARGET, "Burn tari - amount: {}, fee per gram: {}, payment id: {}, claim pk: {}, selection: {}", + amount, fee_per_gram, payment_id, claim_public_key.clone().unwrap_or_default(), selection_criteria + ); let (burn_tx_id, original_proof) = match transaction_service_handle .burn_tari(amount, selection_criteria, fee_per_gram, payment_id, claim_public_key) .await diff --git a/applications/minotari_node/src/commands/command/get_chain_metadata.rs b/applications/minotari_node/src/commands/command/get_chain_metadata.rs index 2a7827a037..ab0f14fbcc 100644 --- a/applications/minotari_node/src/commands/command/get_chain_metadata.rs +++ b/applications/minotari_node/src/commands/command/get_chain_metadata.rs @@ -26,7 +26,7 @@ use clap::Parser; use super::{CommandContext, HandleCommand}; -/// Gets your base node chain meta data +/// Gets your base node chain metadata #[derive(Debug, Parser)] pub struct Args {} diff --git a/applications/minotari_node/src/config.rs b/applications/minotari_node/src/config.rs index 044caee2cd..c21245f7b1 100644 --- a/applications/minotari_node/src/config.rs +++ b/applications/minotari_node/src/config.rs @@ -135,7 +135,7 @@ pub struct BaseNodeConfig { pub status_line_interval: Duration, /// The buffer size for the publish/subscribe connector channel, connecting comms messages to the domain layer pub buffer_size: usize, - /// Liveness meta data auto ping interval between peers + /// Liveness metadata auto ping interval between peers #[serde(with = "serializers::seconds")] pub metadata_auto_ping_interval: Duration, /// The state_machine config settings diff --git a/base_layer/contacts/src/chat_client/src/config.rs b/base_layer/contacts/src/chat_client/src/config.rs index c8f0aee7bc..0781e99769 100644 --- a/base_layer/contacts/src/chat_client/src/config.rs +++ b/base_layer/contacts/src/chat_client/src/config.rs @@ -86,7 +86,7 @@ pub struct ChatClientConfig { pub p2p: P2pConfig, /// If set this node will only sync to the nodes in this set pub force_sync_peers: StringList, - /// Liveness meta data auto ping interval between peers + /// Liveness metadata auto ping interval between peers #[serde(with = "serializers::seconds")] pub metadata_auto_ping_interval: Duration, /// The location of the log path diff --git a/base_layer/core/src/transactions/key_manager/inner.rs b/base_layer/core/src/transactions/key_manager/inner.rs index 5daaf6e3e6..15d31ec3b8 100644 --- a/base_layer/core/src/transactions/key_manager/inner.rs +++ b/base_layer/core/src/transactions/key_manager/inner.rs @@ -1607,6 +1607,21 @@ where TBackend: KeyManagerBackend + 'static Ok(data) } + pub async fn extract_payment_id_from_encrypted_data( + &self, + encrypted_data: &EncryptedData, + commitment: &Commitment, + custom_recovery_key_id: Option<&TariKeyId>, + ) -> Result { + let recovery_key = if let Some(key_id) = custom_recovery_key_id { + self.get_private_key(key_id).await? + } else { + self.get_private_view_key().await? + }; + let (_, _, payment_id) = EncryptedData::decrypt_data(&recovery_key, commitment, encrypted_data)?; + Ok(payment_id) + } + pub async fn try_output_key_recovery( &self, output: &TransactionOutput, diff --git a/base_layer/core/src/transactions/key_manager/interface.rs b/base_layer/core/src/transactions/key_manager/interface.rs index 44a19cc555..ae81758f3a 100644 --- a/base_layer/core/src/transactions/key_manager/interface.rs +++ b/base_layer/core/src/transactions/key_manager/interface.rs @@ -189,6 +189,13 @@ pub trait TransactionKeyManagerInterface: KeyManagerInterface { payment_id: PaymentId, ) -> Result; + async fn extract_payment_id_from_encrypted_data( + &self, + encrypted_data: &EncryptedData, + commitment: &Commitment, + custom_recovery_key_id: Option<&TariKeyId>, + ) -> Result; + async fn try_output_key_recovery( &self, output: &TransactionOutput, diff --git a/base_layer/core/src/transactions/key_manager/wrapper.rs b/base_layer/core/src/transactions/key_manager/wrapper.rs index 77385031e5..58e29ff01f 100644 --- a/base_layer/core/src/transactions/key_manager/wrapper.rs +++ b/base_layer/core/src/transactions/key_manager/wrapper.rs @@ -404,6 +404,19 @@ where TBackend: KeyManagerBackend + 'static .await } + async fn extract_payment_id_from_encrypted_data( + &self, + encrypted_data: &EncryptedData, + commitment: &Commitment, + custom_recovery_key_id: Option<&TariKeyId>, + ) -> Result { + self.transaction_key_manager_inner + .read() + .await + .extract_payment_id_from_encrypted_data(encrypted_data, commitment, custom_recovery_key_id) + .await + } + async fn try_output_key_recovery( &self, output: &TransactionOutput, diff --git a/base_layer/core/src/transactions/test_helpers.rs b/base_layer/core/src/transactions/test_helpers.rs index e2ded3c714..0ff7da3e54 100644 --- a/base_layer/core/src/transactions/test_helpers.rs +++ b/base_layer/core/src/transactions/test_helpers.rs @@ -77,6 +77,7 @@ pub async fn create_test_input< maturity: u64, key_manager: &TransactionKeyManagerWrapper>, coinbase_extra: Vec, + payment_id: Option, ) -> WalletOutput { let params = TestParams::new(key_manager).await; params @@ -88,6 +89,7 @@ pub async fn create_test_input< coinbase_extra: CoinBaseExtra::try_from(coinbase_extra).unwrap(), ..Default::default() }, + payment_id: payment_id.unwrap_or_default(), ..Default::default() }, key_manager, @@ -169,7 +171,7 @@ impl TestParams { let output = WalletOutputBuilder::new(params.value, self.commitment_mask_key_id.clone()) .with_features(params.features) .with_script(params.script.clone()) - .encrypt_data_for_recovery(key_manager, None, PaymentId::Empty) + .encrypt_data_for_recovery(key_manager, None, params.payment_id) .await .unwrap() .with_input_data(input_data) @@ -218,6 +220,7 @@ pub struct UtxoTestParams { pub covenant: Covenant, pub output_version: Option, pub minimum_value_promise: MicroMinotari, + pub payment_id: PaymentId, } impl UtxoTestParams { @@ -239,6 +242,7 @@ impl Default for UtxoTestParams { covenant: Covenant::default(), output_version: None, minimum_value_promise: MicroMinotari::zero(), + payment_id: Default::default(), } } } diff --git a/base_layer/core/src/transactions/transaction_components/encrypted_data.rs b/base_layer/core/src/transactions/transaction_components/encrypted_data.rs index 0f84c3524b..e18405d796 100644 --- a/base_layer/core/src/transactions/transaction_components/encrypted_data.rs +++ b/base_layer/core/src/transactions/transaction_components/encrypted_data.rs @@ -81,6 +81,79 @@ pub struct EncryptedData { data: MaxSizeBytes, } +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Default)] +pub enum TxType { + #[default] + PaymentToOther = 0b0000, + PaymentToSelf = 0b0001, + Burn = 0b0010, + CoinSplit = 0b0011, + CoinJoin = 0b0100, + ValidatorNodeRegistration = 0b0101, + ClaimAtomicSwap = 0b0110, + HtlcAtomicSwapRefund = 0b0111, + CodeTemplateRegistration = 0b1000, + ImportedUtxoNoneRewindable = 0b1001, +} + +impl TxType { + fn from_u8(value: u8) -> Self { + TxType::from_u16(u16::from(value)) + } + + fn from_u16(value: u16) -> Self { + match value & 0b1111 { + 0b0000 => TxType::PaymentToOther, + 0b0001 => TxType::PaymentToSelf, + 0b0010 => TxType::Burn, + 0b0011 => TxType::CoinSplit, + 0b0100 => TxType::CoinJoin, + 0b0101 => TxType::ValidatorNodeRegistration, + 0b0110 => TxType::ClaimAtomicSwap, + 0b0111 => TxType::HtlcAtomicSwapRefund, + 0b1000 => TxType::CodeTemplateRegistration, + 0b1001 => TxType::ImportedUtxoNoneRewindable, + _ => TxType::default(), + } + } + + pub fn as_u8(&self) -> u8 { + match self { + TxType::PaymentToOther => 0b0000, + TxType::PaymentToSelf => 0b0001, + TxType::Burn => 0b0010, + TxType::CoinSplit => 0b0011, + TxType::CoinJoin => 0b0100, + TxType::ValidatorNodeRegistration => 0b0101, + TxType::ClaimAtomicSwap => 0b0110, + TxType::HtlcAtomicSwapRefund => 0b0111, + TxType::CodeTemplateRegistration => 0b1000, + TxType::ImportedUtxoNoneRewindable => 0b1001, + } + } + + fn as_bytes(&self) -> Vec { + vec![self.as_u8()] + } +} + +impl Display for TxType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + TxType::PaymentToOther => write!(f, "PaymentToOther"), + TxType::PaymentToSelf => write!(f, "PaymentToSelf"), + TxType::Burn => write!(f, "Burn"), + TxType::CoinSplit => write!(f, "CoinSplit"), + TxType::CoinJoin => write!(f, "CoinJoin"), + TxType::ValidatorNodeRegistration => write!(f, "ValidatorNodeRegistration"), + TxType::ClaimAtomicSwap => write!(f, "ClaimAtomicSwap"), + TxType::HtlcAtomicSwapRefund => write!(f, "HtlcAtomicSwapRefund"), + TxType::CodeTemplateRegistration => write!(f, "CodeTemplateRegistration"), + TxType::ImportedUtxoNoneRewindable => write!(f, "ImportedUtxoNoneRewindable"), + } + } +} + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Default)] pub enum PaymentId { /// No payment ID. @@ -90,18 +163,18 @@ pub enum PaymentId { U64(u64), /// A u256 number. U256(U256), - /// A Tari address, usually the sender address. - Address(TariAddress), - /// Open - the user optionally specifies a human-readable string. - Open(Vec), + /// Open - the user optionally specifies a human-readable string 'user_data' ('tx_type' is added by the system). + Open { user_data: Vec, tx_type: TxType }, /// This payment ID is automatically generated by the system for output UTXOs. The optional user specified - /// `Open(Vec)` payment ID will be assigned to `user_data`; the system adds in the sender address. + /// `PaymentId::Open` payment ID will be assigned to `tx_type` and `user_data`; the system adds in the sender + /// address. AddressAndData { sender_address: TariAddress, + tx_type: TxType, user_data: Vec, }, /// This payment ID is automatically generated by the system for change outputs. The optional user specified - /// `Open(Vec)` payment ID will be assigned to `user_data`; the system adds in the other data + /// `PaymentId::Open` payment ID will be assigned to `tx_type` and `user_data`; the system adds in the other data /// address. TransactionInfo { recipient_address: TariAddress, @@ -111,7 +184,7 @@ pub enum PaymentId { weight: u64, inputs_count: usize, outputs_count: usize, - burn: bool, + tx_type: TxType, user_data: Vec, }, } @@ -125,12 +198,12 @@ impl PaymentId { PaymentId::Empty => 0, PaymentId::U64(_) => SIZE_VALUE, PaymentId::U256(_) => SIZE_U256, - PaymentId::Address(a) => a.get_size(), - PaymentId::Open(v) => v.len(), + PaymentId::Open { user_data, .. } => user_data.len() + 1, PaymentId::AddressAndData { sender_address, user_data, - } => sender_address.get_size() + user_data.len(), + .. + } => sender_address.get_size() + user_data.len() + 1, PaymentId::TransactionInfo { recipient_address, user_data, @@ -139,20 +212,51 @@ impl PaymentId { } } - // Helper function to set the 'amount' of a 'PaymentId::TransactionInfo' + /// Helper function to set the 'amount' of a 'PaymentId::TransactionInfo' pub fn transaction_info_set_amount(&mut self, amount: MicroMinotari) { if let PaymentId::TransactionInfo { amount: a, .. } = self { *a = amount; } } - // Helper function to set the 'recipient_address' of a 'PaymentId::TransactionInfo' + pub fn get_type(&self) -> TxType { + match self { + PaymentId::Open { tx_type, .. } | + PaymentId::AddressAndData { tx_type, .. } | + PaymentId::TransactionInfo { tx_type, .. } => tx_type.clone(), + _ => TxType::default(), + } + } + + /// Helper function to set the 'recipient_address' of a 'PaymentId::TransactionInfo' pub fn transaction_info_set_address(&mut self, address: TariAddress) { if let PaymentId::TransactionInfo { recipient_address, .. } = self { *recipient_address = address } } + /// Helper function to convert a 'PaymentId::Open' or 'PaymentId::Empty' to a 'PaymentId::AddressAndData', with the + /// optional 'tx_type' only applicable to 'PaymentId::Open', otherwise 'payment_id' is kept as is. + pub fn add_sender_address( + payment_id: PaymentId, + sender_address: TariAddress, + tx_type: Option, + ) -> PaymentId { + match payment_id { + PaymentId::Open { user_data, tx_type } => PaymentId::AddressAndData { + sender_address, + tx_type, + user_data, + }, + PaymentId::Empty => PaymentId::AddressAndData { + sender_address, + tx_type: tx_type.unwrap_or_default(), + user_data: vec![], + }, + _ => payment_id, + } + } + // This method is infallible; any out-of-bound values will be zeroed. fn pack_meta_data(&self) -> Vec { if let PaymentId::TransactionInfo { @@ -161,7 +265,7 @@ impl PaymentId { inputs_count, outputs_count, sender_one_sided, - burn, + tx_type, .. } = self { @@ -181,8 +285,8 @@ impl PaymentId { } else { *inputs_count }; - // - Use 2 bytes less 1 bit for 'outputs_count', max value: 32,767, and 1 bit for 'burn' - let outputs_count = if *outputs_count > 2usize.pow(15) - 1 { + // - Use 2 bytes less 4 bits for 'outputs_count', max value: 4,095, and 3 bits for 'tx_meta_data' + let outputs_count = if *outputs_count > 2usize.pow(12) - 1 { 0 } else { *outputs_count @@ -190,11 +294,11 @@ impl PaymentId { // Pack bytes.extend_from_slice(&fee.to_be_bytes()[4..]); bytes.extend_from_slice(&weight.to_be_bytes()[6..]); - let inputs_count_packed = - (u16::from_usize(inputs_count).unwrap_or_default() & 0x7FFF) | (u16::from(*sender_one_sided) << 15); + let inputs_count_packed = (u16::from_usize(inputs_count).unwrap_or_default() & 0b0111111111111111) | + (u16::from(*sender_one_sided) << 15); bytes.extend_from_slice(&inputs_count_packed.to_be_bytes()); - let outputs_count_packed = - (u16::from_usize(outputs_count).unwrap_or_default() & 0x7FFF) | (u16::from(*burn) << 15); + let outputs_count_packed = (u16::from_usize(outputs_count).unwrap_or_default() & 0b0000111111111111) | + (u16::from(tx_type.as_u8()) << 12); bytes.extend_from_slice(&outputs_count_packed.to_be_bytes()); bytes @@ -203,19 +307,19 @@ impl PaymentId { } } - fn unpack_meta_data(bytes: &[u8; 10]) -> (MicroMinotari, u64, usize, usize, bool, bool) { + fn unpack_meta_data(bytes: &[u8; 10]) -> (MicroMinotari, u64, usize, usize, bool, TxType) { // Extract fee from the first 4 bytes let fee = u64::from(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])); // Extract weight from the next 2 bytes let weight = u64::from(u16::from_be_bytes([bytes[4], bytes[5]])); // Extract inputs_count and sender_one_sided from the next 2 bytes let inputs_count_packed = u16::from_be_bytes([bytes[6], bytes[7]]); - let inputs_count = (inputs_count_packed & 0x7FFF) as usize; - let sender_one_sided = (inputs_count_packed & 0x8000) != 0; - // Extract outputs_count and burn from the next 2 bytes + let inputs_count = (inputs_count_packed & 0b0111111111111111) as usize; + let sender_one_sided = (inputs_count_packed & 0b1000000000000000) != 0; + // Extract outputs_count and tx_type from the next 2 bytes let outputs_count_packed = u16::from_be_bytes([bytes[8], bytes[9]]); - let outputs_count = (outputs_count_packed & 0x7FFF) as usize; - let burn = (outputs_count_packed & 0x8000) != 0; + let outputs_count = (outputs_count_packed & 0b0000111111111111) as usize; + let tx_type = TxType::from_u16((outputs_count_packed & 0b1111000000000000) >> 12); ( MicroMinotari::from(fee), @@ -223,7 +327,7 @@ impl PaymentId { inputs_count, outputs_count, sender_one_sided, - burn, + tx_type, ) } @@ -236,8 +340,7 @@ impl PaymentId { v.to_little_endian(bytes); bytes.to_vec() }, - PaymentId::Address(v) => v.to_vec(), - PaymentId::Open(v) => v.clone(), + PaymentId::Open { user_data, .. } => user_data.clone(), PaymentId::AddressAndData { user_data, .. } => user_data.clone(), PaymentId::TransactionInfo { user_data, .. } => user_data.clone(), } @@ -252,14 +355,19 @@ impl PaymentId { v.to_little_endian(&mut bytes); bytes }, - PaymentId::Address(v) => v.to_vec(), - PaymentId::Open(v) => v.clone(), + PaymentId::Open { user_data, tx_type } => { + let mut bytes = tx_type.as_bytes(); + bytes.extend_from_slice(user_data); + bytes + }, PaymentId::AddressAndData { sender_address, - user_data: id, + user_data, + tx_type, } => { let mut bytes = sender_address.to_vec(); - bytes.extend_from_slice(id); + bytes.extend_from_slice(&tx_type.as_bytes()); + bytes.extend_from_slice(user_data); bytes }, PaymentId::TransactionInfo { @@ -277,6 +385,7 @@ impl PaymentId { } } + #[allow(clippy::too_many_lines)] pub fn from_bytes(bytes: &[u8]) -> Self { match bytes.len() { 0 => PaymentId::Empty, @@ -290,27 +399,25 @@ impl PaymentId { PaymentId::U256(v) }, len if len <= TARI_ADDRESS_INTERNAL_SINGLE_SIZE => { - if let Ok(v) = TariAddress::from_bytes(bytes) { - // Single - PaymentId::Address(v) - } else { - // data - PaymentId::Open(bytes.to_vec()) + // data + PaymentId::Open { + user_data: if bytes.len() > 1 { + bytes[1..].to_vec() + } else { + Vec::new() + }, + tx_type: TxType::from_u8(bytes[0]), } }, _ => { - // PaymentId::Address - if let Ok(v) = TariAddress::from_bytes(bytes) { - // Dual - return PaymentId::Address(v); - } // PaymentId::AddressAndData if bytes.len() > TARI_ADDRESS_INTERNAL_DUAL_SIZE { // Dual + data if let Ok(sender_address) = TariAddress::from_bytes(&bytes[0..TARI_ADDRESS_INTERNAL_DUAL_SIZE]) { return PaymentId::AddressAndData { sender_address, - user_data: bytes[TARI_ADDRESS_INTERNAL_DUAL_SIZE..].to_vec(), + tx_type: TxType::from_u8(bytes[TARI_ADDRESS_INTERNAL_DUAL_SIZE]), + user_data: bytes[TARI_ADDRESS_INTERNAL_DUAL_SIZE + 1..].to_vec(), }; } } @@ -319,17 +426,18 @@ impl PaymentId { if let Ok(sender_address) = TariAddress::from_bytes(&bytes[0..TARI_ADDRESS_INTERNAL_SINGLE_SIZE]) { return PaymentId::AddressAndData { sender_address, - user_data: bytes[TARI_ADDRESS_INTERNAL_SINGLE_SIZE..].to_vec(), + tx_type: TxType::from_u8(bytes[TARI_ADDRESS_INTERNAL_SINGLE_SIZE]), + user_data: bytes[TARI_ADDRESS_INTERNAL_SINGLE_SIZE + 1..].to_vec(), }; } } - // PaymentId::ChangeData + // PaymentId::TransactionInfo let mut amount_bytes = [0u8; SIZE_VALUE]; amount_bytes.copy_from_slice(&bytes[0..SIZE_VALUE]); let amount = MicroMinotari::from(u64::from_le_bytes(amount_bytes)); let mut meta_data_bytes = [0u8; PaymentId::SIZE_META_DATA]; meta_data_bytes.copy_from_slice(&bytes[SIZE_VALUE..PaymentId::SIZE_VALUE_AND_META_DATA]); - let (fee, weight, inputs_count, outputs_count, sender_one_sided, burn) = + let (fee, weight, inputs_count, outputs_count, sender_one_sided, tx_meta_data) = PaymentId::unpack_meta_data(&meta_data_bytes); // Amount + fee + Single/Dual if let Ok(recipient_address) = TariAddress::from_bytes(&bytes[PaymentId::SIZE_VALUE_AND_META_DATA..]) { @@ -341,7 +449,7 @@ impl PaymentId { weight, inputs_count, outputs_count, - burn, + tx_type: tx_meta_data, user_data: Vec::new(), }; } @@ -359,7 +467,7 @@ impl PaymentId { weight, inputs_count, outputs_count, - burn, + tx_type: tx_meta_data, user_data: bytes[PaymentId::SIZE_VALUE_AND_META_DATA + TARI_ADDRESS_INTERNAL_DUAL_SIZE..] .to_vec(), }; @@ -379,14 +487,21 @@ impl PaymentId { weight, inputs_count, outputs_count, - burn, + tx_type: tx_meta_data, user_data: bytes[PaymentId::SIZE_VALUE_AND_META_DATA + TARI_ADDRESS_INTERNAL_SINGLE_SIZE..] .to_vec(), }; } } // Single - PaymentId::Open(bytes.to_vec()) + PaymentId::Open { + user_data: if bytes.len() > 1 { + bytes[1..].to_vec() + } else { + Vec::new() + }, + tx_type: TxType::from_u8(bytes[0]), + } }, } } @@ -402,16 +517,18 @@ impl PaymentId { PaymentId::Empty => self.to_string(), PaymentId::U64(v) => format!("{}", v), PaymentId::U256(v) => format!("{}", v), - PaymentId::Address(v) => v.to_base58(), - PaymentId::Open(v) => PaymentId::stringify_bytes(v), + PaymentId::Open { user_data, .. } => PaymentId::stringify_bytes(user_data), PaymentId::AddressAndData { user_data, .. } => PaymentId::stringify_bytes(user_data), PaymentId::TransactionInfo { user_data, .. } => PaymentId::stringify_bytes(user_data), } } - /// Helper function to create a `PaymentId::Open` from a string - pub fn open_from_str(s: &str) -> Self { - PaymentId::Open(s.as_bytes().to_vec()) + /// Helper function to create a `PaymentId::Open` from a string and the transaction type + pub fn open(s: &str, tx_type: TxType) -> Self { + PaymentId::Open { + user_data: s.as_bytes().to_vec(), + tx_type, + } } } @@ -421,16 +538,19 @@ impl Display for PaymentId { PaymentId::Empty => write!(f, "None"), PaymentId::U64(v) => write!(f, "u64({v})"), PaymentId::U256(v) => write!(f, "u256({v})"), - PaymentId::Address(v) => write!(f, "address({})", v.to_base58()), - PaymentId::Open(v) => write!(f, "data({})", PaymentId::stringify_bytes(v)), + PaymentId::Open { user_data, tx_type } => { + write!(f, "type({}), data({})", tx_type, PaymentId::stringify_bytes(user_data)) + }, PaymentId::AddressAndData { sender_address, - user_data: id, + tx_type, + user_data, } => write!( f, - "sender_address({}), data({})", + "sender_address({}), type({}), data({})", sender_address.to_base58(), - PaymentId::stringify_bytes(id) + tx_type, + PaymentId::stringify_bytes(user_data) ), PaymentId::TransactionInfo { recipient_address, @@ -441,11 +561,11 @@ impl Display for PaymentId { inputs_count, outputs_count, user_data, - burn, + tx_type: tx_meta_data, } => write!( f, "recipient_address({}), sender_one_sided({}), amount({}), fee({}), weight({}), inputs_count({}), \ - outputs_count({}), burn({}), data({})", + outputs_count({}), type({}), data({})", recipient_address.to_base58(), sender_one_sided, amount, @@ -453,7 +573,7 @@ impl Display for PaymentId { weight, inputs_count, outputs_count, - burn, + tx_meta_data, PaymentId::stringify_bytes(user_data), ), } @@ -668,105 +788,6 @@ mod test { const_assert!(TARI_ADDRESS_INTERNAL_SINGLE_SIZE < TARI_ADDRESS_INTERNAL_DUAL_SIZE); } - fn encrypt_decrypt_payment_id(payment_id: PaymentId) -> PaymentId { - let (value, mask) = (u64::MAX, PrivateKey::random(&mut OsRng)); - let commitment = CommitmentFactory::default().commit(&mask, &PrivateKey::from(value)); - let encryption_key = PrivateKey::random(&mut OsRng); - let amount = MicroMinotari::from(value); - let encrypted_data = - EncryptedData::encrypt_data(&encryption_key, &commitment, amount, &mask, payment_id.clone()).unwrap(); - let (_decrypted_value, _decrypted_mask, decrypted_payment_id) = - EncryptedData::decrypt_data(&encryption_key, &commitment, &encrypted_data).unwrap(); - decrypted_payment_id - } - - #[test] - fn it_encrypts_and_decrypts_equivalent_structures() { - // Address vs. AddressAndData with zero data - - // Single - let payment_id_1 = - PaymentId::Address(TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap()); - let payment_id_2 = PaymentId::AddressAndData { - sender_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), - user_data: vec![], - }; - - let decrypted_payment_id_1 = encrypt_decrypt_payment_id(payment_id_1.clone()); - let decrypted_payment_id_2 = encrypt_decrypt_payment_id(payment_id_2); - // Decrypted bytes are the same - assert_eq!(decrypted_payment_id_1.to_bytes(), decrypted_payment_id_2.to_bytes()); - // But the types are different - assert_eq!(payment_id_1, decrypted_payment_id_1); - assert_eq!(payment_id_1, decrypted_payment_id_2); - - // Dual - let payment_id_1 = PaymentId::Address( - TariAddress::from_base58( - "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", - ) - .unwrap(), - ); - let payment_id_2 = PaymentId::AddressAndData { - sender_address: TariAddress::from_base58( - "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", - ) - .unwrap(), - user_data: vec![], - }; - - let decrypted_payment_id_1 = encrypt_decrypt_payment_id(payment_id_1.clone()); - let decrypted_payment_id_2 = encrypt_decrypt_payment_id(payment_id_2); - // Decrypted bytes are the same - assert_eq!(decrypted_payment_id_1.to_bytes(), decrypted_payment_id_2.to_bytes()); - // But the types are different - assert_eq!(payment_id_1, decrypted_payment_id_1); - assert_eq!(payment_id_1, decrypted_payment_id_2); - } - - fn test_edge_case_address(payment_id: PaymentId) { - if let PaymentId::Address(address) = payment_id.clone() { - let payment_id_bytes = payment_id.to_bytes(); - // Manipulate the last byte to invalidate the address - let mut payment_id_2_bytes = payment_id_bytes.clone(); - let payment_id_2_bytes_len = payment_id_2_bytes.len(); - payment_id_2_bytes[payment_id_2_bytes_len - 1] += 1; - - // The original payment_id deserializes to 'PaymentId::Address' - let payment_id_from_bytes = PaymentId::from_bytes(&payment_id_bytes); - assert_eq!(payment_id_from_bytes, payment_id); - - // The manipulated payment_id deserializes to 'PaymentId::Open' - let payment_id_2_from_bytes = PaymentId::from_bytes(&payment_id_2_bytes); - assert_eq!(payment_id_2_from_bytes, PaymentId::Open(payment_id_2_bytes)); - - // All the bytes except for the last byte of the two payment ids should be the same - let address_vec = address.to_vec(); - let payment_id_2_vec = payment_id_2_from_bytes.user_data_as_bytes(); - assert_eq!( - address_vec[..address_vec.len() - 1], - payment_id_2_vec[..payment_id_2_vec.len() - 1] - ); - } else { - panic!("payment_id_1 should be an address"); - } - } - - #[test] - fn it_encrypts_and_decrypts_edge_cases() { - let payment_id = - PaymentId::Address(TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap()); - test_edge_case_address(payment_id); - - let payment_id = PaymentId::Address( - TariAddress::from_base58( - "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", - ) - .unwrap(), - ); - test_edge_case_address(payment_id); - } - #[test] #[allow(clippy::too_many_lines)] fn it_encrypts_and_decrypts_correctly() { @@ -777,20 +798,20 @@ mod test { PaymentId::U256( U256::from_dec_str("465465489789785458694894263185648978947864164681631").expect("Should not fail"), ), - PaymentId::Address( - TariAddress::from_base58( - "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", - ) - .unwrap(), - ), - PaymentId::Address(TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap()), - PaymentId::Open(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), - PaymentId::Open(vec![1; 256]), + PaymentId::Open { + user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + tx_type: TxType::default(), + }, + PaymentId::Open { + user_data: vec![1; 255], + tx_type: TxType::default(), + }, PaymentId::AddressAndData { sender_address: TariAddress::from_base58( "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", ) .unwrap(), + tx_type: TxType::PaymentToOther, user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }, PaymentId::AddressAndData { @@ -798,15 +819,18 @@ mod test { "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", ) .unwrap(), - user_data: vec![1; 189], + tx_type: TxType::PaymentToSelf, + user_data: vec![1; 188], }, PaymentId::AddressAndData { sender_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), + tx_type: TxType::Burn, user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }, PaymentId::AddressAndData { sender_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), - user_data: vec![1; 189], + tx_type: TxType::CoinSplit, + user_data: vec![1; 188], }, // Single + amount PaymentId::TransactionInfo { @@ -817,7 +841,7 @@ mod test { weight: 19000, inputs_count: 712, outputs_count: 3, - burn: false, + tx_type: TxType::CoinJoin, user_data: vec![], }, // Single + amount + data @@ -829,7 +853,7 @@ mod test { weight: 19000, inputs_count: 712, outputs_count: 3, - burn: false, + tx_type: TxType::ValidatorNodeRegistration, user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }, // Dual + amount @@ -844,7 +868,7 @@ mod test { weight: 19000, inputs_count: 712, outputs_count: 3, - burn: false, + tx_type: TxType::CoinSplit, user_data: vec![], }, // Dual + amount + data @@ -859,7 +883,7 @@ mod test { weight: 19000, inputs_count: 712, outputs_count: 3, - burn: true, + tx_type: TxType::Burn, user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }, ] { @@ -902,20 +926,20 @@ mod test { PaymentId::U256( U256::from_dec_str("465465489789785458694894263185648978947864164681631").expect("Should not fail"), ), - PaymentId::Address( - TariAddress::from_base58( - "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", - ) - .unwrap(), - ), - PaymentId::Address(TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap()), - PaymentId::Open(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), - PaymentId::Open(vec![1; 256]), + PaymentId::Open { + user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + tx_type: TxType::default(), + }, + PaymentId::Open { + user_data: vec![1; 255], + tx_type: TxType::default(), + }, PaymentId::AddressAndData { sender_address: TariAddress::from_base58( "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", ) .unwrap(), + tx_type: TxType::PaymentToOther, user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }, PaymentId::AddressAndData { @@ -923,15 +947,18 @@ mod test { "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", ) .unwrap(), - user_data: vec![1; 189], + tx_type: TxType::PaymentToSelf, + user_data: vec![1; 188], }, PaymentId::AddressAndData { sender_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), + tx_type: TxType::CoinJoin, user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }, PaymentId::AddressAndData { sender_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), - user_data: vec![1; 189], + tx_type: TxType::ValidatorNodeRegistration, + user_data: vec![1; 188], }, // Single + amount PaymentId::TransactionInfo { @@ -942,7 +969,7 @@ mod test { weight: 19000, inputs_count: 712, outputs_count: 3, - burn: true, + tx_type: TxType::ClaimAtomicSwap, user_data: vec![], }, // Single + amount + data @@ -954,7 +981,7 @@ mod test { weight: 19000, inputs_count: 712, outputs_count: 3, - burn: false, + tx_type: TxType::PaymentToOther, user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }, // Dual + amount @@ -969,7 +996,7 @@ mod test { weight: 19000, inputs_count: 712, outputs_count: 3, - burn: true, + tx_type: TxType::PaymentToSelf, user_data: vec![], }, // Dual + amount + data @@ -984,7 +1011,7 @@ mod test { weight: 19000, inputs_count: 712, outputs_count: 3, - burn: true, + tx_type: TxType::CoinSplit, user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], }, ] { @@ -1008,6 +1035,54 @@ mod test { } } + #[test] + fn tx_type_serialize_deserialize_correctly() { + for tx_type in [ + TxType::PaymentToOther, + TxType::PaymentToSelf, + TxType::Burn, + TxType::CoinSplit, + TxType::CoinJoin, + TxType::ValidatorNodeRegistration, + TxType::ClaimAtomicSwap, + TxType::HtlcAtomicSwapRefund, + TxType::CodeTemplateRegistration, + TxType::ImportedUtxoNoneRewindable, + ] { + let payment_id = PaymentId::Open { + tx_type: tx_type.clone(), + user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + }; + let payment_id_bytes = payment_id.to_bytes(); + let payment_id_from_bytes = PaymentId::from_bytes(&payment_id_bytes); + assert_eq!(payment_id, payment_id_from_bytes); + + let payment_id = PaymentId::AddressAndData { + sender_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), + tx_type: tx_type.clone(), + user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + }; + let payment_id_bytes = payment_id.to_bytes(); + let payment_id_from_bytes = PaymentId::from_bytes(&payment_id_bytes); + assert_eq!(payment_id, payment_id_from_bytes); + + let payment_id = PaymentId::TransactionInfo { + recipient_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), + sender_one_sided: false, + amount: MicroMinotari::from(123456), + fee: MicroMinotari::from(123), + weight: 19000, + inputs_count: 712, + outputs_count: 3, + tx_type, + user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + }; + let payment_id_bytes = payment_id.to_bytes(); + let payment_id_from_bytes = PaymentId::from_bytes(&payment_id_bytes); + assert_eq!(payment_id, payment_id_from_bytes); + } + } + #[test] fn payment_id_display() { assert_eq!(PaymentId::Empty.to_string(), "None"); @@ -1020,21 +1095,22 @@ mod test { "u256(465465489789785458694894263185648978947864164681631)" ); assert_eq!( - PaymentId::Address(TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap()) - .to_string(), - "address(f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk)" - ); - assert_eq!( - PaymentId::Open(vec![0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64]).to_string(), - "data(Hello World)" + PaymentId::Open { + user_data: vec![0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64], + tx_type: TxType::CoinSplit + } + .to_string(), + "type(CoinSplit), data(Hello World)" ); assert_eq!( PaymentId::AddressAndData { sender_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), + tx_type: TxType::HtlcAtomicSwapRefund, user_data: vec![0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64] } .to_string(), - "sender_address(f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk), data(Hello World)" + "sender_address(f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk), type(HtlcAtomicSwapRefund), data(Hello \ + World)" ); assert_eq!( PaymentId::TransactionInfo { @@ -1045,12 +1121,12 @@ mod test { weight: 5127, inputs_count: 712, outputs_count: 3, - burn: true, + tx_type: TxType::Burn, user_data: vec![0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64] } .to_string(), "recipient_address(f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk), sender_one_sided(false), \ - amount(123456 µT), fee(123 µT), weight(5127), inputs_count(712), outputs_count(3), burn(true), \ + amount(123456 µT), fee(123 µT), weight(5127), inputs_count(712), outputs_count(3), type(Burn), \ data(Hello World)" ); assert_eq!( @@ -1062,18 +1138,18 @@ mod test { weight: 19227, inputs_count: 3124, outputs_count: 2533, - burn: false, + tx_type: TxType::ValidatorNodeRegistration, user_data: "Hello World!!! 11-22-33".as_bytes().to_vec(), } .to_string(), "recipient_address(f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk), sender_one_sided(true), amount(1234 \ - µT), fee(123 µT), weight(19227), inputs_count(3124), outputs_count(2533), burn(false), data(Hello \ - World!!! 11-22-33)" + µT), fee(123 µT), weight(19227), inputs_count(3124), outputs_count(2533), \ + type(ValidatorNodeRegistration), data(Hello World!!! 11-22-33)" ); } #[test] - fn test_max_meta_data_values() { + fn test_payment_id_max_meta_data_values() { // Maximum values for the metadata fields let payment_id_1 = PaymentId::TransactionInfo { recipient_address: TariAddress::from_base58( @@ -1085,8 +1161,8 @@ mod test { fee: MicroMinotari::from(4_294_967_295), weight: 65_535, inputs_count: 32_767, - outputs_count: 32_767, - burn: false, + outputs_count: 4_095, + tx_type: TxType::PaymentToOther, user_data: "Hello World!!! 11-22-33".as_bytes().to_vec(), }; let payment_id_2 = PaymentId::TransactionInfo { @@ -1096,8 +1172,8 @@ mod test { fee: MicroMinotari::from(4_294_967_295), weight: 65_535, inputs_count: 32_767, - outputs_count: 32_767, - burn: true, + outputs_count: 4_095, + tx_type: TxType::PaymentToSelf, user_data: "Hello World!!! 11-22-33".as_bytes().to_vec(), }; @@ -1105,13 +1181,13 @@ mod test { payment_id_1.to_string(), "recipient_address(f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb), \ sender_one_sided(true), amount(18446744073709.551615 T), fee(4294.967295 T), weight(65535), inputs_count(32767), \ - outputs_count(32767), burn(false), data(Hello World!!! 11-22-33)" + outputs_count(4095), type(PaymentToOther), data(Hello World!!! 11-22-33)" ); assert_eq!( payment_id_2.to_string(), "recipient_address(f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk), sender_one_sided(false), \ amount(18446744073709.551615 T), fee(4294.967295 T), weight(65535), inputs_count(32767), \ - outputs_count(32767), burn(true), data(Hello World!!! 11-22-33)" + outputs_count(4095), type(PaymentToSelf), data(Hello World!!! 11-22-33)" ); let payment_id_1_bytes = payment_id_1.to_bytes(); @@ -1131,30 +1207,30 @@ mod test { fee: MicroMinotari::from(4_294_967_295 + 100), // 4294.967395 T weight: 65_535 + 100, // = 65635 inputs_count: 32_767 + 100, // = 32768 - outputs_count: 32_767 + 100, // = 32768 - burn: true, + outputs_count: 4_095 + 100, // = 4195 + tx_type: TxType::Burn, user_data: "Hello World!!! 11-22-33".as_bytes().to_vec(), }; - // - It can be displayed as is.. + // - It can be displayed as is ... assert_eq!( payment_id_3.to_string(), "recipient_address(f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb), \ sender_one_sided(true), amount(18446744073709.551615 T), fee(4294.967395 T), weight(65635), inputs_count(32867), \ - outputs_count(32867), burn(true), data(Hello World!!! 11-22-33)" + outputs_count(4195), type(Burn), data(Hello World!!! 11-22-33)" ); - // - but it cannot be serialized and deserialized as is - overflowed metadata will be zeroed. + // ... but it cannot be serialized and deserialized as is - overflowed metadata will be zeroed. let payment_id_3_bytes = payment_id_3.to_bytes(); let payment_id_3_from_bytes = PaymentId::from_bytes(&payment_id_3_bytes); assert_eq!( payment_id_3_from_bytes.to_string(), "recipient_address(f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb), \ sender_one_sided(true), amount(18446744073709.551615 T), fee(0 µT), weight(0), inputs_count(0), \ - outputs_count(0), burn(true), data(Hello World!!! 11-22-33)" + outputs_count(0), type(Burn), data(Hello World!!! 11-22-33)" ); } #[test] - fn it_gets_useable_data() { + fn it_gets_useable_payment_id_data() { let payment_id = PaymentId::Empty; assert_eq!("", PaymentId::stringify_bytes(&payment_id.user_data_as_bytes())); @@ -1170,17 +1246,9 @@ mod test { U256::from_little_endian(&payment_id.user_data_as_bytes()).to_string() ); - let payment_id = - PaymentId::Address(TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap()); - assert_eq!( - "f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk", - TariAddress::from_bytes(&payment_id.user_data_as_bytes()) - .unwrap() - .to_base58() - ); - let payment_id = PaymentId::AddressAndData { sender_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), + tx_type: TxType::CoinSplit, user_data: "Hello World!!!".as_bytes().to_vec(), }; assert_eq!( @@ -1196,7 +1264,7 @@ mod test { weight: 19227, inputs_count: 3124, outputs_count: 2533, - burn: false, + tx_type: TxType::PaymentToOther, user_data: "Hello World!!! 11-22-33".as_bytes().to_vec(), }; assert_eq!( 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 fff2dbcf37..748e01851d 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 @@ -126,6 +126,7 @@ impl WalletOutputBuilder { custom_recovery_key_id: Option<&TariKeyId>, payment_id: PaymentId, ) -> Result { + self.payment_id = payment_id.clone(); self.encrypted_data = key_manager .encrypt_data_for_recovery( &self.commitment_mask_key_id, diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index ee26c10d42..7be4b8f6c2 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -98,7 +98,6 @@ pub(super) struct RawTransactionInfo { /// The total public nonce for the transaction signature. This is calculated when sender sends single round message /// to receiver. pub total_sender_nonce: PublicKey, - /// Details used to construct the transaction kernel. pub metadata: TransactionMetadata, /// A user payment ID for the sender/receiver @@ -1048,7 +1047,7 @@ mod test { let key_manager = create_memory_db_key_manager().unwrap(); let p1 = TestParams::new(&key_manager).await; let p2 = TestParams::new(&key_manager).await; - let input = create_test_input(MicroMinotari(1200), 0, &key_manager, vec![]).await; + let input = create_test_input(MicroMinotari(1200), 0, &key_manager, vec![], None).await; let mut builder = SenderTransactionProtocol::builder(create_consensus_constants(0), key_manager.clone()); let script = TariScript::default(); let output_features = OutputFeatures::default(); @@ -1112,7 +1111,7 @@ mod test { let a_change_key = TestParams::new(&key_manager).await; // Bob's parameters let bob_key = TestParams::new(&key_manager).await; - let input = create_test_input(MicroMinotari(1200), 0, &key_manager, vec![]).await; + let input = create_test_input(MicroMinotari(1200), 0, &key_manager, vec![], None).await; let utxo = input.to_transaction_input(&key_manager).await.unwrap(); let script = script!(Nop).unwrap(); let consensus_constants = create_consensus_constants(0); @@ -1223,7 +1222,7 @@ mod test { let alice_key = TestParams::new(&key_manager).await; // Bob's parameters let bob_key = TestParams::new(&key_manager).await; - let input = create_test_input(MicroMinotari(25000), 0, &key_manager, vec![]).await; + let input = create_test_input(MicroMinotari(25000), 0, &key_manager, vec![], None).await; let consensus_constants = create_consensus_constants(0); let mut builder = SenderTransactionProtocol::builder(consensus_constants.clone(), key_manager.clone()); let script = script!(Nop).unwrap(); @@ -1337,9 +1336,9 @@ mod test { let factories = CryptoFactories::default(); // Bob's parameters let bob_key = TestParams::new(&key_manager).await; - let input = create_test_input(MicroMinotari(10000), 0, &key_manager, vec![]).await; - let input2 = create_test_input(MicroMinotari(2000), 0, &key_manager, vec![]).await; - let input3 = create_test_input(MicroMinotari(15000), 0, &key_manager, vec![]).await; + let input = create_test_input(MicroMinotari(10000), 0, &key_manager, vec![], None).await; + let input2 = create_test_input(MicroMinotari(2000), 0, &key_manager, vec![], None).await; + let input3 = create_test_input(MicroMinotari(15000), 0, &key_manager, vec![], None).await; let consensus_constants = create_consensus_constants(0); let mut builder = SenderTransactionProtocol::builder(consensus_constants.clone(), key_manager.clone()); let script = script!(Nop).unwrap(); @@ -1447,7 +1446,7 @@ mod test { // Alice's parameters let key_manager = create_memory_db_key_manager().unwrap(); let (utxo_amount, fee_per_gram, amount) = (MicroMinotari(2500), MicroMinotari(10), MicroMinotari(500)); - let input = create_test_input(utxo_amount, 0, &key_manager, vec![]).await; + let input = create_test_input(utxo_amount, 0, &key_manager, vec![], None).await; let script = script!(Nop).unwrap(); let mut builder = SenderTransactionProtocol::builder(create_consensus_constants(0), key_manager.clone()); let change = TestParams::new(&key_manager).await; @@ -1487,7 +1486,7 @@ mod test { // Alice's parameters let key_manager = create_memory_db_key_manager().unwrap(); let (utxo_amount, fee_per_gram, amount) = (MicroMinotari(2500), MicroMinotari(10), MicroMinotari(500)); - let input = create_test_input(utxo_amount, 0, &key_manager, vec![]).await; + let input = create_test_input(utxo_amount, 0, &key_manager, vec![], None).await; let script = script!(Nop).unwrap(); let mut builder = SenderTransactionProtocol::builder(create_consensus_constants(0), key_manager.clone()); let change = TestParams::new(&key_manager).await; @@ -1531,7 +1530,7 @@ mod test { // Bob's parameters let bob_test_params = TestParams::new(&key_manager_bob).await; let alice_value = MicroMinotari(25000); - let input = create_test_input(alice_value, 0, &key_manager_alice, vec![]).await; + let input = create_test_input(alice_value, 0, &key_manager_alice, vec![], None).await; let script = script!(Nop).unwrap(); let consensus_constants = create_consensus_constants(0); diff --git a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs index 408efd991a..8edf384f03 100644 --- a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs +++ b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs @@ -42,9 +42,8 @@ use crate::{ key_manager::{TariKeyId, TransactionKeyManagerInterface}, tari_amount::*, transaction_components::{ - encrypted_data::PaymentId, + encrypted_data::{PaymentId, TxType}, OutputFeatures, - OutputType, TransactionOutput, TransactionOutputVersion, WalletOutput, @@ -422,16 +421,6 @@ where KM: TransactionKeyManagerInterface .own_address .features() .contains(TariAddressFeatures::INTERACTIVE); - let burn = if let Some(recipient) = self.recipient.clone() { - recipient.recipient_output_features.output_type == OutputType::Burn - } else { - false - }; - let burn = burn || - self.sender_custom_outputs - .iter() - .any(|v| v.output.features.output_type == OutputType::Burn) || - self.burn_commitment.is_some(); let mut payment_id = PaymentId::TransactionInfo { recipient_address: TariAddress::default(), @@ -441,7 +430,16 @@ where KM: TransactionKeyManagerInterface weight: weight_without_change + weight_of_change, inputs_count: num_inputs, outputs_count: num_outputs + 1, - burn, + tx_type: if let Some( + PaymentId::Open { tx_type, .. } | PaymentId::AddressAndData { tx_type, .. }, + ) = self.payment_id.clone() + { + tx_type + } else if self.kernel_features.is_burned() { + TxType::Burn + } else { + TxType::default() + }, user_data: if let Some(data) = self.payment_id.clone() { data.user_data_as_bytes() } else { @@ -450,13 +448,24 @@ where KM: TransactionKeyManagerInterface }; if let Some(recipient) = self.recipient.clone() { payment_id.transaction_info_set_amount(recipient.amount); - if !burn { - payment_id.transaction_info_set_address(recipient.recipient_address); + match payment_id.get_type() { + TxType::PaymentToOther => { + payment_id.transaction_info_set_address(recipient.recipient_address) + }, + TxType::PaymentToSelf | + TxType::CoinSplit | + TxType::CoinJoin | + TxType::ValidatorNodeRegistration | + TxType::CodeTemplateRegistration | + TxType::ClaimAtomicSwap | + TxType::HtlcAtomicSwapRefund => payment_id.transaction_info_set_address(own_address), + _ => {}, } } else { payment_id.transaction_info_set_amount(total_to_self); payment_id.transaction_info_set_address(own_address); } + trace!(target: LOG_TARGET, "Modified change payment id: {}, TxId: {:?}", payment_id, self.tx_id); let encrypted_data = self .key_manager @@ -759,7 +768,7 @@ mod test { // Create some inputs let key_manager = create_memory_db_key_manager().unwrap(); let p = TestParams::new(&key_manager).await; - let input = create_test_input(MicroMinotari(5000), 0, &key_manager, vec![]).await; + let input = create_test_input(MicroMinotari(5000), 0, &key_manager, vec![], None).await; let constants = create_consensus_constants(0); let expected_fee = Fee::from(*constants.transaction_weight_params()).calculate( MicroMinotari(4), @@ -824,6 +833,7 @@ mod test { 0, &key_manager, vec![], + None, ) .await; let output = p @@ -884,7 +894,7 @@ mod test { .await .unwrap() .with_fee_per_gram(MicroMinotari(2)); - let input_base = create_test_input(MicroMinotari(50), 0, &key_manager, vec![]).await; + let input_base = create_test_input(MicroMinotari(50), 0, &key_manager, vec![], None).await; for _ in 0..=MAX_TRANSACTION_INPUTS { builder.with_input(input_base.clone()).await.unwrap(); } @@ -906,7 +916,7 @@ mod test { p.get_size_for_default_features_and_scripts(1) .expect("Failed to borsh serialized size"), ); - let input = create_test_input(500 * uT + tx_fee, 0, &key_manager, vec![]).await; + let input = create_test_input(500 * uT + tx_fee, 0, &key_manager, vec![], None).await; let script = script!(Nop).unwrap(); // Start the builder let constants = create_consensus_constants(0); @@ -944,7 +954,7 @@ mod test { // Create some inputs let key_manager = create_memory_db_key_manager().unwrap(); let p = TestParams::new(&key_manager).await; - let input = create_test_input(MicroMinotari(400), 0, &key_manager, vec![]).await; + let input = create_test_input(MicroMinotari(400), 0, &key_manager, vec![], None).await; let script = script!(Nop).unwrap(); let output = create_wallet_output_with_data( script.clone(), @@ -998,8 +1008,8 @@ mod test { // Create some inputs let key_manager = create_memory_db_key_manager().unwrap(); let p = TestParams::new(&key_manager).await; - let input1 = create_test_input(MicroMinotari(2000), 0, &key_manager, vec![]).await; - let input2 = create_test_input(MicroMinotari(3000), 0, &key_manager, vec![]).await; + let input1 = create_test_input(MicroMinotari(2000), 0, &key_manager, vec![], None).await; + let input2 = create_test_input(MicroMinotari(3000), 0, &key_manager, vec![], None).await; let fee_per_gram = MicroMinotari(6); let script = script!(Nop).unwrap(); diff --git a/base_layer/wallet/src/output_manager_service/handle.rs b/base_layer/wallet/src/output_manager_service/handle.rs index 5bf9d01a29..9f7fe56d1f 100644 --- a/base_layer/wallet/src/output_manager_service/handle.rs +++ b/base_layer/wallet/src/output_manager_service/handle.rs @@ -109,6 +109,7 @@ pub enum OutputManagerRequest { output_features: Box, fee_per_gram: MicroMinotari, lock_height: Option, + payment_id: PaymentId, }, CreatePayToSelfWithOutputs { outputs: Vec, @@ -134,6 +135,7 @@ pub enum OutputManagerRequest { CreateCoinJoin { commitments: Vec, fee_per_gram: MicroMinotari, + payment_id: PaymentId, }, FeeEstimate { amount: MicroMinotari, @@ -246,6 +248,7 @@ impl fmt::Display for OutputManagerRequest { CreateCoinJoin { commitments, fee_per_gram, + .. } => write!( f, "CreateCoinJoin: commitments={:#?}, fee_per_gram={}", @@ -732,12 +735,14 @@ impl OutputManagerHandle { &mut self, commitments: Vec, fee_per_gram: MicroMinotari, + payment_id: PaymentId, ) -> Result<(TxId, Transaction, MicroMinotari), OutputManagerError> { match self .handle .call(OutputManagerRequest::CreateCoinJoin { commitments, fee_per_gram, + payment_id, }) .await?? { @@ -936,6 +941,7 @@ impl OutputManagerHandle { output_features: OutputFeatures, fee_per_gram: MicroMinotari, lock_height: Option, + payment_id: PaymentId, ) -> Result<(MicroMinotari, Transaction), OutputManagerError> { match self .handle @@ -946,6 +952,7 @@ impl OutputManagerHandle { output_features: Box::new(output_features), fee_per_gram, lock_height, + payment_id, }) .await?? { diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index bb0eec3dd9..6363b1433e 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -48,7 +48,7 @@ use tari_core::{ key_manager::{TariKeyId, TransactionKeyManagerInterface}, tari_amount::MicroMinotari, transaction_components::{ - encrypted_data::PaymentId, + encrypted_data::{PaymentId, TxType}, EncryptedData, KernelFeatures, OutputFeatures, @@ -291,7 +291,10 @@ where output_hash, expected_commitment, recipient_address, - PaymentId::Open(output_hash.to_vec()), + PaymentId::Open { + user_data: output_hash.to_vec(), + tx_type: TxType::PaymentToOther, + }, 0, RangeProofType::BulletProofPlus, 0.into(), @@ -352,6 +355,7 @@ where output_features, fee_per_gram, lock_height, + payment_id, } => self .create_pay_to_self_transaction( tx_id, @@ -360,6 +364,7 @@ where *output_features, fee_per_gram, lock_height, + payment_id, ) .await .map(OutputManagerResponse::PayToSelfTransaction), @@ -448,8 +453,9 @@ where OutputManagerRequest::CreateCoinJoin { commitments, fee_per_gram, + payment_id, } => self - .create_coin_join(commitments, fee_per_gram) + .create_coin_join(commitments, fee_per_gram, payment_id) .await .map(OutputManagerResponse::Transaction), @@ -801,7 +807,11 @@ where } else { return Err(OutputManagerError::InvalidScriptHash); }; - let payment_id = PaymentId::Address(single_round_sender_data.sender_address.clone()); + let payment_id = PaymentId::AddressAndData { + sender_address: single_round_sender_data.sender_address.clone(), + tx_type: TxType::PaymentToOther, + user_data: vec![], + }; let encrypted_data = self .resources .key_manager @@ -977,10 +987,8 @@ where ) -> Result { debug!( target: LOG_TARGET, - "Preparing to send transaction. Amount: {}. UTXO Selection: {}. Fee per gram: {}. ", - amount, - selection_criteria, - fee_per_gram, + "Preparing to send transaction - TxId: {}, amount: {}, fee per gram: {}, payment id: {}, selection: {}", + tx_id, amount, fee_per_gram, payment_id, selection_criteria, ); let features_and_scripts_byte_size = self .resources @@ -1836,14 +1844,11 @@ where .stealth_address_script_spending_key(&commitment_mask_key_id, recipient_address.public_spend_key()) .await?; let script = push_pubkey_script(&script_spending_key); - let payment_id = match payment_id { - PaymentId::Open(v) => PaymentId::AddressAndData { - sender_address: self.resources.interactive_tari_address.clone(), - user_data: v, - }, - PaymentId::Empty => PaymentId::Address(self.resources.one_sided_tari_address.clone()), - _ => payment_id, - }; + let payment_id = PaymentId::add_sender_address( + payment_id, + self.resources.one_sided_tari_address.clone(), + Some(TxType::PaymentToOther), + ); let output = WalletOutputBuilder::new(amount, commitment_mask_key_id) .with_features( @@ -1907,6 +1912,7 @@ where output_features: OutputFeatures, fee_per_gram: MicroMinotari, lock_height: Option, + payment_id: PaymentId, ) -> Result<(MicroMinotari, Transaction), OutputManagerError> { let covenant = Covenant::default(); @@ -1952,7 +1958,9 @@ where builder.with_input(kmo.wallet_output.clone()).await?; } - let (output, sender_offset_key_id) = self.output_to_self(output_features, amount, covenant).await?; + let (output, sender_offset_key_id) = self + .output_to_self(output_features, amount, covenant, payment_id) + .await?; builder .with_output(output.wallet_output.clone(), sender_offset_key_id.clone()) @@ -1975,7 +1983,7 @@ where Covenant::default(), self.resources.interactive_tari_address.clone(), ) - .with_payment_id(PaymentId::open_from_str("Pay to self transaction")); + .with_payment_id(PaymentId::open("Pay to self transaction", TxType::PaymentToSelf)); let mut stp = builder .build() @@ -2405,10 +2413,13 @@ where self.resources.key_manager.clone(), ); tx_builder - .with_payment_id(PaymentId::open_from_str(&format!( - "Coin split transaction, {} into {} outputs", - accumulated_amount, number_of_splits - ))) + .with_payment_id(PaymentId::open( + &format!( + "Coin split transaction, {} into {} outputs", + accumulated_amount, number_of_splits + ), + TxType::CoinSplit, + )) .with_lock_height(0) .with_fee_per_gram(fee_per_gram) .with_kernel_features(KernelFeatures::empty()); @@ -2432,7 +2443,12 @@ where }; let (output, sender_offset_key_id) = self - .output_to_self(OutputFeatures::default(), amount_per_split, Covenant::default()) + .output_to_self( + OutputFeatures::default(), + amount_per_split, + Covenant::default(), + PaymentId::open(&format!("{} even coin splits", number_of_splits), TxType::CoinSplit), + ) .await?; tx_builder @@ -2567,11 +2583,12 @@ where self.resources.consensus_constants.clone(), self.resources.key_manager.clone(), ); + let payment_id = PaymentId::open( + &format!("Coin split, {} into {} outputs", accumulated_amount, number_of_splits), + TxType::CoinSplit, + ); tx_builder - .with_payment_id(PaymentId::open_from_str(&format!( - "Coin split transaction, {} into {} outputs", - accumulated_amount, number_of_splits - ))) + .with_payment_id(payment_id.clone()) .with_lock_height(0) .with_fee_per_gram(fee_per_gram) .with_kernel_features(KernelFeatures::empty()); @@ -2591,7 +2608,12 @@ where for _ in 0..number_of_splits { let (output, sender_offset_key_id) = self - .output_to_self(OutputFeatures::default(), amount_per_split, Covenant::default()) + .output_to_self( + OutputFeatures::default(), + amount_per_split, + Covenant::default(), + payment_id.clone(), + ) .await?; tx_builder @@ -2688,6 +2710,7 @@ where output_features: OutputFeatures, amount: MicroMinotari, covenant: Covenant, + payment_id: PaymentId, ) -> Result<(DbWalletOutput, TariKeyId), OutputManagerError> { let (commitment_mask_key, script_key) = self .resources @@ -2695,7 +2718,12 @@ where .get_next_commitment_mask_and_script_key() .await?; let script = script!(PushPubKey(Box::new(script_key.pub_key.clone())))?; - let payment_id = PaymentId::Address(self.resources.interactive_tari_address.clone()); + let payment_id = PaymentId::add_sender_address( + payment_id, + self.resources.interactive_tari_address.clone(), + Some(TxType::PaymentToSelf), + ); + let encrypted_data = self .resources .key_manager @@ -2762,6 +2790,7 @@ where &mut self, commitments: Vec, fee_per_gram: MicroMinotari, + payment_id: PaymentId, ) -> Result<(TxId, Transaction, MicroMinotari), OutputManagerError> { let default_features_and_scripts_size = self .default_features_and_scripts_size() @@ -2804,11 +2833,7 @@ where self.resources.key_manager.clone(), ); tx_builder - .with_payment_id(PaymentId::open_from_str(&format!( - "Coin join transaction, {} outputs{} into", - src_outputs.len(), - accumulated_amount - ))) + .with_payment_id(payment_id.clone()) .with_lock_height(0) .with_fee_per_gram(fee_per_gram) .with_kernel_features(KernelFeatures::empty()); @@ -2824,7 +2849,12 @@ where } let (output, sender_offset_key_id) = self - .output_to_self(OutputFeatures::default(), accumulated_amount, Covenant::default()) + .output_to_self( + OutputFeatures::default(), + accumulated_amount, + Covenant::default(), + payment_id.clone(), + ) .await?; tx_builder @@ -2904,7 +2934,7 @@ where ) .await? .with_sender_address(self.resources.interactive_tari_address.clone()) - .with_payment_id(PaymentId::Empty) + .with_payment_id(PaymentId::open("scraping wallet", TxType::PaymentToOther)) .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_lock_height(tx_meta.lock_height) .with_kernel_features(tx_meta.kernel_features) @@ -2998,8 +3028,8 @@ where self.resources.key_manager.get_spend_key().await?.key_id, output.sender_offset_public_key, output.metadata_signature, - // Although the technically the script does have a script lock higher than 0, this does not apply - // to to us as we are claiming the Hashed part which has a 0 time lock + // Although technically the script does have a script lock higher than 0, this does not apply + // to us as we are claiming the Hashed part which has a 0 time lock 0, output.covenant, output.encrypted_data, @@ -3016,7 +3046,7 @@ where builder .with_lock_height(0) .with_fee_per_gram(fee_per_gram) - .with_payment_id(PaymentId::open_from_str("SHA-XTR atomic swap")) + .with_payment_id(PaymentId::open("SHA-XTR atomic swap", TxType::ClaimAtomicSwap)) .with_kernel_features(KernelFeatures::empty()) .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_input(rewound_output) @@ -3099,7 +3129,7 @@ where builder .with_lock_height(0) .with_fee_per_gram(fee_per_gram) - .with_payment_id(PaymentId::open_from_str("SHA-XTR atomic refund")) + .with_payment_id(PaymentId::open("SHA-XTR atomic refund", TxType::HtlcAtomicSwapRefund)) .with_kernel_features(KernelFeatures::empty()) .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_input(output) diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs index 35f7d359e3..c24a6bf2bf 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs @@ -65,6 +65,7 @@ pub struct NewOutputSql { pub minimum_value_promise: i64, pub source: i32, pub spending_priority: i32, + pub payment_id: Option>, } impl NewOutputSql { @@ -111,6 +112,7 @@ impl NewOutputSql { minimum_value_promise: output.wallet_output.minimum_value_promise.as_u64() as i64, source: output.source as i32, spending_priority: output.spending_priority.into(), + payment_id: Some(output.payment_id.to_bytes()), }; Ok(output) diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index c6108fb797..360a25e312 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -54,7 +54,7 @@ use tari_core::{ key_manager::TransactionKeyManagerInterface, tari_amount::MicroMinotari, transaction_components::{ - encrypted_data::PaymentId, + encrypted_data::{PaymentId, TxType}, CodeTemplateRegistration, KernelFeatures, OutputFeatures, @@ -825,7 +825,10 @@ where binary_url, }, UtxoSelectionCriteria::default(), - PaymentId::open_from_str(&format!("Template Registration: {}", template_name)), + PaymentId::open( + &format!("Template Registration: {}", template_name), + TxType::CodeTemplateRegistration, + ), send_transaction_join_handles, transaction_broadcast_join_handles, reply_channel.take().expect("Reply channel is not set"), @@ -1115,7 +1118,15 @@ where let (fee, transaction) = self .resources .output_manager_service - .create_pay_to_self_transaction(tx_id, amount, selection_criteria, output_features, fee_per_gram, None) + .create_pay_to_self_transaction( + tx_id, + amount, + selection_criteria, + output_features, + fee_per_gram, + None, + payment_id.clone(), + ) .await?; // Notify that the transaction was successfully resolved. @@ -1463,6 +1474,8 @@ where let minimum_value_promise = MicroMinotari::zero(); // Prepare sender part of the transaction + let payment_id = + PaymentId::add_sender_address(payment_id, self.resources.interactive_tari_address.clone(), None); let mut stp = self .resources .output_manager_service @@ -1477,7 +1490,7 @@ where covenant.clone(), minimum_value_promise, destination.clone(), - PaymentId::Empty, + payment_id.clone(), ) .await?; @@ -1561,7 +1574,7 @@ where .encrypt_data_for_recovery( &self.resources.transaction_key_manager_service, Some(&encryption_key), - PaymentId::Address(self.resources.interactive_tari_address.clone()), + payment_id.clone(), ) .await? .with_input_data(ExecutionStack::default()) @@ -1673,15 +1686,18 @@ where payment_id: PaymentId, ) -> Result { let tx_id = TxId::new_random(); - let payment_id = match payment_id { - PaymentId::Open(v) => PaymentId::AddressAndData { - sender_address: self.resources.interactive_tari_address.clone(), - user_data: v, - }, - PaymentId::Empty => PaymentId::AddressAndData { - sender_address: self.resources.interactive_tari_address.clone(), - user_data: vec![], - }, + let payment_id = match payment_id.clone() { + PaymentId::Open { .. } | PaymentId::Empty => PaymentId::add_sender_address( + payment_id, + self.resources.interactive_tari_address.clone(), + if dest_address == self.resources.one_sided_tari_address || + dest_address == self.resources.interactive_tari_address + { + Some(TxType::PaymentToSelf) + } else { + Some(TxType::PaymentToOther) + }, + ), _ => payment_id, }; self.verify_send(&dest_address, TariAddressFeatures::create_one_sided_only())?; @@ -1915,7 +1931,11 @@ where >, ) -> Result { let tx_id = TxId::new_random(); - let payment_id = PaymentId::Address(self.resources.interactive_tari_address.clone()); + let payment_id = PaymentId::AddressAndData { + sender_address: self.resources.interactive_tari_address.clone(), + tx_type: TxType::PaymentToOther, + user_data: vec![], + }; self.verify_send(&dest_address, TariAddressFeatures::create_one_sided_only())?; // Prepare sender part of the transaction @@ -2158,7 +2178,17 @@ where >, ) -> Result<(TxId, BurntProof), TransactionServiceError> { let tx_id = TxId::new_random(); - trace!(target: LOG_TARGET, "Burning transaction start - TxId: {}", tx_id); + let payment_id = PaymentId::add_sender_address( + payment_id, + self.resources.interactive_tari_address.clone(), + Some(TxType::Burn), + ); + trace!( + target: LOG_TARGET, + "Burning transaction start - TxId: {}, amount: {}, fee per gram: {}, payment id: {}, claim pk: {}, \ + selection: {}", + tx_id, amount, fee_per_gram, payment_id, claim_public_key.clone().unwrap_or_default(), selection_criteria + ); let output_features = claim_public_key .as_ref() .cloned() @@ -2253,7 +2283,7 @@ where .encrypt_data_for_recovery( &self.resources.transaction_key_manager_service, Some(&recovery_key_id), - PaymentId::Address(self.resources.interactive_tari_address.clone()), + payment_id.clone(), ) .await? .with_input_data(Default::default()) @@ -3044,28 +3074,59 @@ where }; // we should only be able to recover 1 output per tx, but we use the vec here to be safe let mut source_address = None; + let mut destination_address = None; let mut payment_id = None; let mut amount = None; for ro in recovered { if source_address.is_none() { + payment_id = Some(ro.output.payment_id.clone()); match &ro.output.payment_id { PaymentId::AddressAndData { sender_address: address, + tx_type: _, user_data: _, - } | - PaymentId::Address(address) => { + } => { source_address = Some(address.clone()); - payment_id = Some(ro.output.payment_id.clone()); + destination_address = Some(self.resources.one_sided_tari_address.clone()); amount = Some(ro.output.value); }, PaymentId::TransactionInfo { recipient_address, amount: tx_amount, + tx_type, + sender_one_sided, .. } => { - source_address = Some(recipient_address.clone()); - payment_id = Some(ro.output.payment_id.clone()); amount = Some(*tx_amount); + let own_address = if *sender_one_sided { + self.resources.one_sided_tari_address.clone() + } else { + self.resources.interactive_tari_address.clone() + }; + match tx_type { + TxType::PaymentToOther => { + source_address = Some(own_address.clone()); + destination_address = Some(recipient_address.clone()); + }, + TxType::Burn => { + source_address = Some(own_address.clone()); + destination_address = Some(TariAddress::default()); + }, + TxType::PaymentToSelf | + TxType::CoinSplit | + TxType::CoinJoin | + TxType::ValidatorNodeRegistration | + TxType::CodeTemplateRegistration | + TxType::ClaimAtomicSwap | + TxType::HtlcAtomicSwapRefund => { + source_address = Some(own_address.clone()); + destination_address = Some(own_address.clone()); + }, + TxType::ImportedUtxoNoneRewindable => { + source_address = Some(TariAddress::default()); + destination_address = Some(recipient_address.clone()); + }, + } }, _ => payment_id = Some(ro.output.payment_id.clone()), }; @@ -3074,7 +3135,7 @@ where let completed_transaction = CompletedTransaction::new( tx_id, source_address.clone().unwrap_or_default(), - self.resources.one_sided_tari_address.clone(), + destination_address.clone().unwrap_or_default(), amount.unwrap_or_default(), transaction.body.get_total_fee()?, transaction.clone(), @@ -3533,17 +3594,33 @@ where let (direction, amount, destination_address) = if let PaymentId::TransactionInfo { recipient_address, amount, - burn, + tx_type, .. } = payment_id.clone() { ( - TransactionDirection::Outbound, + match tx_type { + TxType::PaymentToOther | TxType::Burn => TransactionDirection::Outbound, + TxType::PaymentToSelf | + TxType::CoinSplit | + TxType::CoinJoin | + TxType::ValidatorNodeRegistration | + TxType::CodeTemplateRegistration | + TxType::ClaimAtomicSwap | + TxType::HtlcAtomicSwapRefund | + TxType::ImportedUtxoNoneRewindable => TransactionDirection::Inbound, + }, amount, - if burn { - TariAddress::default() - } else { - recipient_address + match tx_type { + TxType::PaymentToOther | TxType::ImportedUtxoNoneRewindable => recipient_address.clone(), + TxType::Burn => TariAddress::default(), + TxType::PaymentToSelf | + TxType::CoinSplit | + TxType::CoinJoin | + TxType::ValidatorNodeRegistration | + TxType::CodeTemplateRegistration | + TxType::ClaimAtomicSwap | + TxType::HtlcAtomicSwapRefund => self.resources.one_sided_tari_address.clone(), }, ) } else { diff --git a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs index 038b339ac3..6e50798ea2 100644 --- a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs +++ b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs @@ -2200,7 +2200,11 @@ mod test { key_manager::create_memory_db_key_manager, tari_amount::MicroMinotari, test_helpers::{create_wallet_output_with_data, TestParams}, - transaction_components::{encrypted_data::PaymentId, OutputFeatures, Transaction}, + transaction_components::{ + encrypted_data::{PaymentId, TxType}, + OutputFeatures, + Transaction, + }, transaction_protocol::sender::TransactionSenderMessage, ReceiverTransactionProtocol, SenderTransactionProtocol, @@ -2280,7 +2284,7 @@ mod test { builder .with_lock_height(0) .with_fee_per_gram(MicroMinotari::from(177 / 5)) - .with_payment_id(PaymentId::open_from_str("Yo!")) + .with_payment_id(PaymentId::open("Yo!", TxType::PaymentToOther)) .with_input(input) .await .unwrap() @@ -2315,7 +2319,7 @@ mod test { fee: stp.get_fee_amount().unwrap(), sender_protocol: stp.clone(), status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -2334,7 +2338,7 @@ mod test { fee: stp.get_fee_amount().unwrap(), sender_protocol: stp.clone(), status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -2400,7 +2404,7 @@ mod test { amount, receiver_protocol: rtp.clone(), status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -2418,7 +2422,7 @@ mod test { amount, receiver_protocol: rtp, status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -2489,7 +2493,7 @@ mod test { mined_height: None, mined_in_block: None, mined_timestamp: None, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), }; let source_address = TariAddress::new_dual_address_with_default_features( PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), @@ -2519,7 +2523,7 @@ mod test { mined_height: None, mined_in_block: None, mined_timestamp: None, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), }; CompletedTransactionSql::try_from(completed_tx1.clone(), &cipher) @@ -2679,7 +2683,7 @@ mod test { amount: MicroMinotari::from(100), receiver_protocol: ReceiverTransactionProtocol::new_placeholder(), status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -2707,7 +2711,7 @@ mod test { fee: MicroMinotari::from(10), sender_protocol: SenderTransactionProtocol::new_placeholder(), status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -2758,7 +2762,7 @@ mod test { mined_height: None, mined_in_block: None, mined_timestamp: None, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), }; let completed_tx_sql = CompletedTransactionSql::try_from(completed_tx.clone(), &cipher).unwrap(); @@ -2822,7 +2826,7 @@ mod test { amount: MicroMinotari::from(100), receiver_protocol: ReceiverTransactionProtocol::new_placeholder(), status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -2845,7 +2849,7 @@ mod test { fee: MicroMinotari::from(10), sender_protocol: SenderTransactionProtocol::new_placeholder(), status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -2890,7 +2894,7 @@ mod test { mined_height: None, mined_in_block: None, mined_timestamp: None, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), }; let completed_tx_sql = CompletedTransactionSql::try_from(completed_tx, &cipher).unwrap(); @@ -3032,7 +3036,7 @@ mod test { mined_height: None, mined_in_block: None, mined_timestamp: None, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), }; let completed_tx_sql = CompletedTransactionSql::try_from(completed_tx.clone(), &cipher).unwrap(); diff --git a/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs b/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs index 5e74dba12b..965c6cc4cc 100644 --- a/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs +++ b/base_layer/wallet/src/utxo_scanner_service/utxo_scanner_task.rs @@ -644,8 +644,7 @@ where PaymentId::AddressAndData { sender_address: address, .. - } | - PaymentId::Address(address) => address.clone(), + } => address.clone(), PaymentId::TransactionInfo { .. } => self.resources.one_sided_tari_address.clone(), _ => TariAddress::default(), } diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index ca5d4bc5f7..50b48a3bc4 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -56,7 +56,12 @@ use tari_core::{ transactions::{ key_manager::{SecretTransactionKeyManagerInterface, TariKeyId, TransactionKeyManagerInitializer}, tari_amount::MicroMinotari, - transaction_components::{encrypted_data::PaymentId, EncryptedData, OutputFeatures, UnblindedOutput}, + transaction_components::{ + encrypted_data::{PaymentId, TxType}, + EncryptedData, + OutputFeatures, + UnblindedOutput, + }, CryptoFactories, }, }; @@ -760,16 +765,20 @@ where fee_per_gram: MicroMinotari, payment_id: Option, ) -> Result { + let payment_id = payment_id.unwrap_or(PaymentId::open( + &format!("Coin join {} outputs", commitments.len()), + TxType::CoinJoin, + )); let coin_join_tx = self .output_manager_service - .create_coin_join(commitments, fee_per_gram) + .create_coin_join(commitments, fee_per_gram, payment_id.clone()) .await; match coin_join_tx { Ok((tx_id, tx, output_value)) => { let coin_tx = self .transaction_service - .submit_transaction(tx_id, tx, output_value, payment_id.unwrap_or_default()) + .submit_transaction(tx_id, tx, output_value, payment_id) .await; match coin_tx { diff --git a/base_layer/wallet/tests/transaction_service_tests/service.rs b/base_layer/wallet/tests/transaction_service_tests/service.rs index 4fd0ae0149..af1f920488 100644 --- a/base_layer/wallet/tests/transaction_service_tests/service.rs +++ b/base_layer/wallet/tests/transaction_service_tests/service.rs @@ -130,7 +130,7 @@ use tari_core::{ tari_amount::*, test_helpers::{create_wallet_output_with_data, TestParams}, transaction_components::{ - encrypted_data::PaymentId, + encrypted_data::{PaymentId, TxType}, KernelBuilder, OutputFeatures, RangeProofType, @@ -626,7 +626,7 @@ async fn manage_single_transaction() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroMinotari::from(4), - PaymentId::open_from_str("TAKE MAH MONEYS!"), + PaymentId::open("TAKE MAH MONEYS!", TxType::PaymentToOther), ) .await .expect("Alice sending tx"); @@ -783,7 +783,7 @@ async fn large_interactive_transaction() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroMinotari::from(1), - PaymentId::open_from_str("TAKE MAH MONEYS!"), + PaymentId::open("TAKE MAH MONEYS!", TxType::PaymentToOther), ) .await .expect("Alice sending large tx"); @@ -944,7 +944,7 @@ async fn test_spend_dust_to_self_in_oversized_transaction() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - PaymentId::open_from_str("TAKE MAH _OWN_ MONEYS!"), + PaymentId::open("TAKE MAH _OWN_ MONEYS!", TxType::PaymentToOther), ) .await .is_err()); @@ -1041,7 +1041,7 @@ async fn test_spend_dust_to_other_in_oversized_transaction() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - PaymentId::open_from_str("GIVE MAH _OWN_ MONEYS AWAY!"), + PaymentId::open("GIVE MAH _OWN_ MONEYS AWAY!", TxType::PaymentToOther), ) .await .unwrap(); @@ -1156,7 +1156,7 @@ async fn test_spend_dust_happy_path() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - PaymentId::open_from_str("TAKE MAH _OWN_ MONEYS!"), + PaymentId::open("TAKE MAH _OWN_ MONEYS!", TxType::PaymentToOther), ) .await .unwrap(); @@ -1200,7 +1200,7 @@ async fn test_spend_dust_happy_path() { UtxoSelectionCriteria::default(), OutputFeatures::default(), fee_per_gram, - PaymentId::open_from_str("GIVE MAH _OWN_ MONEYS AWAY!"), + PaymentId::open("GIVE MAH _OWN_ MONEYS AWAY!", TxType::PaymentToOther), ) .await .unwrap(); @@ -1300,7 +1300,7 @@ async fn single_transaction_to_self() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 20.into(), - PaymentId::open_from_str("TAKE MAH _OWN_ MONEYS!"), + PaymentId::open("TAKE MAH _OWN_ MONEYS!", TxType::PaymentToOther), ) .await .expect("Alice sending tx"); @@ -1389,7 +1389,7 @@ async fn large_coin_split_transaction() { tx_id, coin_split_tx, amount, - PaymentId::open_from_str("large coin-split"), + PaymentId::open("large coin-split", TxType::CoinSplit), ) .await .expect("Alice sending coin-split tx"); @@ -1630,7 +1630,7 @@ async fn send_one_sided_transaction_to_other() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 20.into(), - PaymentId::open_from_str("SEE IF YOU CAN CATCH THIS ONE..... SIDED TX!"), + PaymentId::open("SEE IF YOU CAN CATCH THIS ONE..... SIDED TX!", TxType::PaymentToOther), ) .await .expect("Alice sending one-sided tx to Bob"); @@ -2249,7 +2249,7 @@ async fn manage_multiple_transactions() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroMinotari::from(20), - PaymentId::open_from_str("a to b 1"), + PaymentId::open("a to b 1", TxType::PaymentToOther), ) .await .unwrap(); @@ -2266,7 +2266,7 @@ async fn manage_multiple_transactions() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroMinotari::from(20), - PaymentId::open_from_str("a to c 1"), + PaymentId::open("a to c 1", TxType::PaymentToOther), ) .await .unwrap(); @@ -2285,7 +2285,7 @@ async fn manage_multiple_transactions() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroMinotari::from(20), - PaymentId::open_from_str("b to a 1"), + PaymentId::open("b to a 1", TxType::PaymentToOther), ) .await .unwrap(); @@ -2296,7 +2296,7 @@ async fn manage_multiple_transactions() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroMinotari::from(20), - PaymentId::open_from_str("a to b 2"), + PaymentId::open("a to b 2", TxType::PaymentToOther), ) .await .unwrap(); @@ -2874,7 +2874,7 @@ async fn discovery_async_return_test() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroMinotari::from(20), - PaymentId::open_from_str("Discovery Tx!"), + PaymentId::open("Discovery Tx!", TxType::PaymentToOther), ) .await .unwrap(); @@ -2913,7 +2913,7 @@ async fn discovery_async_return_test() { UtxoSelectionCriteria::default(), OutputFeatures::default(), MicroMinotari::from(20), - PaymentId::open_from_str("Discovery Tx2!"), + PaymentId::open("Discovery Tx2!", TxType::PaymentToOther), ) .await .unwrap(); @@ -3014,7 +3014,7 @@ async fn test_power_mode_updates() { mined_height: None, mined_in_block: None, mined_timestamp: None, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), }; let source_address = TariAddress::new_dual_address_with_default_features( @@ -3045,7 +3045,7 @@ async fn test_power_mode_updates() { mined_height: None, mined_in_block: None, mined_timestamp: None, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), }; tx_backend @@ -3219,7 +3219,7 @@ async fn test_transaction_cancellation() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 100 * uT, - PaymentId::open_from_str("Testing Message"), + PaymentId::open("Testing Message", TxType::PaymentToOther), ) .await .unwrap(); @@ -3325,7 +3325,7 @@ async fn test_transaction_cancellation() { builder .with_lock_height(0) .with_fee_per_gram(MicroMinotari::from(5)) - .with_payment_id(PaymentId::open_from_str("Yo!")) + .with_payment_id(PaymentId::open("Yo!", TxType::PaymentToOther)) .with_input(input) .await .unwrap() @@ -3412,7 +3412,7 @@ async fn test_transaction_cancellation() { builder .with_lock_height(0) .with_fee_per_gram(MicroMinotari::from(5)) - .with_payment_id(PaymentId::open_from_str("Yo!")) + .with_payment_id(PaymentId::open("Yo!", TxType::PaymentToOther)) .with_input(input) .await .unwrap() @@ -3569,7 +3569,7 @@ async fn test_direct_vs_saf_send_of_tx_reply_and_finalize() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 100 * uT, - PaymentId::open_from_str("Testing Message"), + PaymentId::open("Testing Message", TxType::PaymentToOther), ) .await .unwrap(); @@ -3771,7 +3771,7 @@ async fn test_direct_vs_saf_send_of_tx_reply_and_finalize() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 100 * uT, - PaymentId::open_from_str("Testing Message"), + PaymentId::open("Testing Message", TxType::PaymentToOther), ) .await .unwrap(); @@ -3960,7 +3960,7 @@ async fn test_tx_direct_send_behaviour() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 100 * uT, - PaymentId::open_from_str("Testing Message1"), + PaymentId::open("Testing Message1", TxType::PaymentToOther), ) .await .unwrap(); @@ -4004,7 +4004,7 @@ async fn test_tx_direct_send_behaviour() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 100 * uT, - PaymentId::open_from_str("Testing Message2"), + PaymentId::open("Testing Message2", TxType::PaymentToOther), ) .await .unwrap(); @@ -4053,7 +4053,7 @@ async fn test_tx_direct_send_behaviour() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 100 * uT, - PaymentId::open_from_str("Testing Message3"), + PaymentId::open("Testing Message3", TxType::PaymentToOther), ) .await .unwrap(); @@ -4102,7 +4102,7 @@ async fn test_tx_direct_send_behaviour() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 100 * uT, - PaymentId::open_from_str("Testing Message4"), + PaymentId::open("Testing Message4", TxType::PaymentToOther), ) .await .unwrap(); @@ -4429,7 +4429,7 @@ async fn test_transaction_resending() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 100 * uT, - PaymentId::open_from_str("Testing Message"), + PaymentId::open("Testing Message", TxType::PaymentToOther), ) .await .unwrap(); @@ -4617,7 +4617,7 @@ async fn test_resend_on_startup() { builder .with_lock_height(0) .with_fee_per_gram(MicroMinotari::from(177 / 5)) - .with_payment_id(PaymentId::open_from_str("Yo!")) + .with_payment_id(PaymentId::open("Yo!", TxType::PaymentToOther)) .with_input(input) .await .unwrap() @@ -4657,7 +4657,7 @@ async fn test_resend_on_startup() { fee: stp.get_fee_amount().unwrap(), sender_protocol: stp, status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -4790,7 +4790,7 @@ async fn test_resend_on_startup() { amount, receiver_protocol: rtp, status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo2"), + payment_id: PaymentId::open("Yo2", TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -4952,7 +4952,7 @@ async fn test_replying_to_cancelled_tx() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 100 * uT, - PaymentId::open_from_str("Testing Message"), + PaymentId::open("Testing Message", TxType::PaymentToOther), ) .await .unwrap(); @@ -5092,7 +5092,7 @@ async fn test_transaction_timeout_cancellation() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 20 * uT, - PaymentId::open_from_str("Testing Message"), + PaymentId::open("Testing Message", TxType::PaymentToOther), ) .await .unwrap(); @@ -5147,7 +5147,7 @@ async fn test_transaction_timeout_cancellation() { builder .with_lock_height(0) .with_fee_per_gram(MicroMinotari::from(177 / 5)) - .with_payment_id(PaymentId::open_from_str("Yo!")) + .with_payment_id(PaymentId::open("Yo!", TxType::PaymentToOther)) .with_input(input) .await .unwrap() @@ -5187,7 +5187,7 @@ async fn test_transaction_timeout_cancellation() { fee: stp.get_fee_amount().unwrap(), sender_protocol: stp, status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), timestamp: Utc::now().checked_sub_signed(ChronoDuration::seconds(20)).unwrap(), cancelled: false, direct_send_success: false, @@ -5387,7 +5387,7 @@ async fn transaction_service_tx_broadcast() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 100 * uT, - PaymentId::open_from_str("Testing Message"), + PaymentId::open("Testing Message", TxType::PaymentToOther), ) .await .unwrap(); @@ -5448,7 +5448,7 @@ async fn transaction_service_tx_broadcast() { UtxoSelectionCriteria::default(), OutputFeatures::default(), 20 * uT, - PaymentId::open_from_str("Testing Message2"), + PaymentId::open("Testing Message2", TxType::PaymentToOther), ) .await .unwrap(); @@ -5739,7 +5739,7 @@ async fn broadcast_all_completed_transactions_on_startup() { mined_height: None, mined_in_block: None, mined_timestamp: None, - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), }; let completed_tx2 = CompletedTransaction { @@ -5877,7 +5877,7 @@ async fn test_update_faux_tx_on_oms_validation() { uo_1.to_transaction_output(&alice_ts_interface.key_manager_handle) .await .unwrap(), - PaymentId::open_from_str("blah"), + PaymentId::open("blah", TxType::PaymentToOther), ) .await .unwrap(); @@ -5893,7 +5893,7 @@ async fn test_update_faux_tx_on_oms_validation() { uo_2.to_transaction_output(&alice_ts_interface.key_manager_handle) .await .unwrap(), - PaymentId::open_from_str("one-sided 1"), + PaymentId::open("one-sided 1", TxType::PaymentToOther), ) .await .unwrap(); @@ -5909,7 +5909,7 @@ async fn test_update_faux_tx_on_oms_validation() { uo_3.to_transaction_output(&alice_ts_interface.key_manager_handle) .await .unwrap(), - PaymentId::open_from_str("one-sided 2"), + PaymentId::open("one-sided 2", TxType::PaymentToOther), ) .await .unwrap(); @@ -6052,7 +6052,7 @@ async fn test_update_coinbase_tx_on_oms_validation() { uo_1.to_transaction_output(&alice_ts_interface.key_manager_handle) .await .unwrap(), - PaymentId::open_from_str("coinbase_confirmed"), + PaymentId::open("coinbase_confirmed", TxType::PaymentToOther), ) .await .unwrap(); @@ -6068,7 +6068,7 @@ async fn test_update_coinbase_tx_on_oms_validation() { uo_2.to_transaction_output(&alice_ts_interface.key_manager_handle) .await .unwrap(), - PaymentId::open_from_str("one-coinbase_unconfirmed 1"), + PaymentId::open("one-coinbase_unconfirmed 1", TxType::PaymentToOther), ) .await .unwrap(); @@ -6084,7 +6084,7 @@ async fn test_update_coinbase_tx_on_oms_validation() { uo_3.to_transaction_output(&alice_ts_interface.key_manager_handle) .await .unwrap(), - PaymentId::open_from_str("Coinbase_not_mined"), + PaymentId::open("Coinbase_not_mined", TxType::PaymentToOther), ) .await .unwrap(); diff --git a/base_layer/wallet/tests/transaction_service_tests/storage.rs b/base_layer/wallet/tests/transaction_service_tests/storage.rs index bed5f10ba4..70985ae14a 100644 --- a/base_layer/wallet/tests/transaction_service_tests/storage.rs +++ b/base_layer/wallet/tests/transaction_service_tests/storage.rs @@ -54,7 +54,7 @@ use tari_core::{ tari_amount::{uT, MicroMinotari}, test_helpers::{create_wallet_output_with_data, TestParams}, transaction_components::{ - encrypted_data::PaymentId, + encrypted_data::{PaymentId, TxType}, OutputFeatures, RangeProofType, Transaction, @@ -92,7 +92,7 @@ pub async fn test_db_backend(backend: T) { builder .with_lock_height(0) .with_fee_per_gram(MicroMinotari::from(177 / 5)) - .with_payment_id(PaymentId::open_from_str("Yo!")) + .with_payment_id(PaymentId::open("Yo!", TxType::PaymentToOther)) .with_input(input) .await .unwrap() @@ -141,7 +141,7 @@ pub async fn test_db_backend(backend: T) { fee: stp.clone().get_fee_amount().unwrap(), sender_protocol: stp.clone(), status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str(messages[i]), + payment_id: PaymentId::open(messages[i], TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -254,7 +254,7 @@ pub async fn test_db_backend(backend: T) { amount: amounts[i], receiver_protocol: rtp.clone(), status: TransactionStatus::Pending, - payment_id: PaymentId::open_from_str(messages[i]), + payment_id: PaymentId::open(messages[i], TxType::PaymentToOther), timestamp: Utc::now(), cancelled: false, direct_send_success: false, @@ -345,7 +345,7 @@ pub async fn test_db_backend(backend: T) { mined_height: None, mined_in_block: None, mined_timestamp: None, - payment_id: PaymentId::open_from_str(messages[i]), + payment_id: PaymentId::open(messages[i], TxType::PaymentToOther), }); db.complete_outbound_transaction(outbound_txs[i].tx_id, completed_txs[i].clone()) .unwrap(); @@ -445,7 +445,7 @@ pub async fn test_db_backend(backend: T) { 22 * uT, rtp, TransactionStatus::Pending, - PaymentId::open_from_str("To be cancelled"), + PaymentId::open("To be cancelled", TxType::PaymentToOther), Utc::now(), ), ) @@ -498,7 +498,7 @@ pub async fn test_db_backend(backend: T) { stp.get_fee_amount().unwrap(), stp, TransactionStatus::Pending, - PaymentId::open_from_str("To be cancelled"), + PaymentId::open("To be cancelled", TxType::PaymentToOther), Utc::now(), false, ), @@ -595,7 +595,7 @@ async fn import_tx_and_read_it_from_db() { TransactionDirection::Inbound, Some(5), Some(DateTime::from_timestamp(0, 0).unwrap()), - PaymentId::open_from_str("message"), + PaymentId::open("message", TxType::PaymentToOther), ) .unwrap(); @@ -624,7 +624,7 @@ async fn import_tx_and_read_it_from_db() { TransactionDirection::Inbound, Some(6), Some(DateTime::from_timestamp(0, 0).unwrap()), - PaymentId::open_from_str("message"), + PaymentId::open("message", TxType::PaymentToOther), ) .unwrap(); @@ -653,7 +653,7 @@ async fn import_tx_and_read_it_from_db() { TransactionDirection::Inbound, Some(7), Some(DateTime::from_timestamp(0, 0).unwrap()), - PaymentId::open_from_str("message"), + PaymentId::open("message", TxType::PaymentToOther), ) .unwrap(); diff --git a/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs b/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs index b94d648e81..d28c2de721 100644 --- a/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs +++ b/base_layer/wallet/tests/transaction_service_tests/transaction_protocols.rs @@ -81,7 +81,10 @@ use tari_core::{ key_manager::{create_memory_db_key_manager, MemoryDbKeyManager, TransactionKeyManagerInterface}, tari_amount::{uT, MicroMinotari, T}, test_helpers::schema_to_transaction, - transaction_components::{encrypted_data::PaymentId, OutputFeatures}, + transaction_components::{ + encrypted_data::{PaymentId, TxType}, + OutputFeatures, + }, CryptoFactories, }, txn_schema, @@ -235,7 +238,7 @@ pub async fn add_transaction_to_database( TransactionDirection::Outbound, None, None, - PaymentId::open_from_str("Test"), + PaymentId::open("Test", TxType::PaymentToOther), ) .unwrap(); db.insert_completed_transaction(tx_id, completed_tx1).unwrap(); diff --git a/base_layer/wallet_ffi/src/callback_handler_tests.rs b/base_layer/wallet_ffi/src/callback_handler_tests.rs index 8133305e0d..61731212fb 100644 --- a/base_layer/wallet_ffi/src/callback_handler_tests.rs +++ b/base_layer/wallet_ffi/src/callback_handler_tests.rs @@ -49,7 +49,10 @@ mod test { }; use tari_core::transactions::{ tari_amount::{uT, MicroMinotari}, - transaction_components::{encrypted_data::PaymentId, Transaction}, + transaction_components::{ + encrypted_data::{PaymentId, TxType}, + Transaction, + }, ReceiverTransactionProtocol, SenderTransactionProtocol, }; @@ -319,7 +322,7 @@ mod test { 22 * uT, rtp, TransactionStatus::Pending, - PaymentId::open_from_str("1"), + PaymentId::open("1", TxType::PaymentToOther), Utc::now(), ); db.add_pending_inbound_transaction(1u64.into(), inbound_tx.clone()) @@ -353,7 +356,7 @@ mod test { TransactionDirection::Inbound, None, None, - PaymentId::open_from_str("2"), + PaymentId::open("2", TxType::PaymentToOther), ) .unwrap(); db.insert_completed_transaction(2u64.into(), completed_tx.clone()) @@ -372,7 +375,7 @@ mod test { 23 * uT, stp, TransactionStatus::Pending, - PaymentId::open_from_str("3"), + PaymentId::open("3", TxType::PaymentToOther), Utc::now(), false, ); @@ -424,7 +427,7 @@ mod test { TransactionDirection::Inbound, Some(2), Some(DateTime::from_timestamp(0, 0).unwrap_or(DateTime::::MIN_UTC)), - PaymentId::open_from_str("6"), + PaymentId::open("6", TxType::PaymentToOther), ) .unwrap(); db.insert_completed_transaction(6u64.into(), faux_unconfirmed_tx.clone()) @@ -458,7 +461,7 @@ mod test { TransactionDirection::Inbound, Some(5), Some(DateTime::from_timestamp(0, 0).unwrap()), - PaymentId::open_from_str("7"), + PaymentId::open("7", TxType::PaymentToOther), ) .unwrap(); db.insert_completed_transaction(7u64.into(), faux_confirmed_tx.clone()) diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs index 95c3dc1843..209b686f25 100644 --- a/base_layer/wallet_ffi/src/lib.rs +++ b/base_layer/wallet_ffi/src/lib.rs @@ -143,9 +143,10 @@ use tari_core::{ borsh::FromBytes, consensus::ConsensusManager, transactions::{ + key_manager::TransactionKeyManagerInterface, tari_amount::MicroMinotari, transaction_components::{ - encrypted_data::PaymentId, + encrypted_data::{PaymentId, TxType}, CoinBaseExtra, OutputFeatures, OutputFeaturesVersion, @@ -346,11 +347,9 @@ impl From for TariUtxo { coinbase_extra: CString::new(x.wallet_output.features.coinbase_extra.to_hex()) .expect("failed to obtain hex from a commitment") .into_raw(), - payment_id: CString::new( - String::from_utf8(x.payment_id.to_bytes()).unwrap_or_else(|_| "Invalid".to_string()), - ) - .expect("failed to obtain string from a payment id") - .into_raw(), + payment_id: CString::new(format!("{}", x.payment_id)) + .expect("failed to obtain string from a payment id") + .into_raw(), } } } @@ -2175,7 +2174,7 @@ pub unsafe extern "C" fn wallet_import_external_utxo_as_non_rewindable( .block_on((*wallet).wallet.import_unblinded_output_as_non_rewindable( (*output).clone(), source_address, - PaymentId::open_from_str(&payment_id_string), + PaymentId::open(&payment_id_string, TxType::ImportedUtxoNoneRewindable), )) { Ok(tx_id) => tx_id.as_u64(), Err(e) => { @@ -2580,7 +2579,7 @@ pub unsafe extern "C" fn covenant_destroy(covenant: *mut TariCovenant) { /// `encrypted_data_bytes` - The encrypted_data bytes as a ByteVector /// /// ## Returns -/// `TariEncryptedOpenings` - Returns encrypted data. Note that it will be ptr::null_mut() if any argument is +/// `TariEncryptedOpenings` - Returns encrypted data. Note that it will be ptr::null_mut() if any argument is /// null or if there was an error with the contents of bytes /// /// # Safety @@ -2612,6 +2611,81 @@ pub unsafe extern "C" fn encrypted_data_create_from_bytes( } } +/// Extract the transaction type from a TariEncryptedOpenings +/// +/// ## Arguments +/// `encrypted_data` - The encrypted data +/// `commitment_bytes` - The public commitment component as a ByteVector +/// `wallet` - The TariWallet pointe +/// +/// ## Returns +/// `0` => `PaymentToOther`, +/// `1` => `PaymentToSelf`, +/// `2` => `Burn`, +/// `3` => `CoinSplit`, +/// `4` => `CoinJoin`, +/// `5` => `ValidatorNodeRegistration`, +/// `6` => `ClaimAtomicSwap`, +/// `7` => `HtlcAtomicSwapRefund`, +/// `8` => `CodeTemplateRegistration`, +/// `9` => `ImportedUtxoNoneRewindable`, +/// `99` => `None` +/// +/// # Safety +/// None +#[no_mangle] +pub unsafe extern "C" fn transaction_type_from_encrypted_data( + encrypted_data: *const TariEncryptedOpenings, + commitment_bytes: *const ByteVector, + wallet: *mut TariWallet, + error_out: *mut c_int, +) -> c_uint { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + + let mut transaction_type = 99; + + if encrypted_data.is_null() { + error = LibWalletError::from(InterfaceError::NullError("encrypted_data".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + } else { + match Commitment::from_canonical_bytes(&(*commitment_bytes).0.clone()) { + Ok(commitment) => { + match (*wallet).runtime.block_on( + (*wallet) + .wallet + .key_manager_service + .extract_payment_id_from_encrypted_data(&(*encrypted_data), &commitment, None), + ) { + Ok(payment_id) => { + if let PaymentId::Open { tx_type, .. } | + PaymentId::AddressAndData { tx_type, .. } | + PaymentId::TransactionInfo { tx_type, .. } = payment_id + { + transaction_type = c_uint::from(tx_type.as_u8()); + } + }, + Err(e) => { + error!(target: LOG_TARGET, "Error extracting payment id from encrypted data: {:?}", e); + error = LibWalletError::from(WalletError::TransactionServiceError( + TransactionServiceError::TransactionError(e), + )) + .code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + }, + Err(e) => { + error!(target: LOG_TARGET, "Error creating a commitment from bytes: {:?}", e); + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + } + + transaction_type +} + /// Creates a ByteVector containing the encrypted_data bytes from a TariEncryptedOpenings /// /// ## Arguments @@ -6532,7 +6606,14 @@ pub unsafe extern "C" fn wallet_coin_split( commitments, number_of_splits, MicroMinotari(fee_per_gram), - PaymentId::Empty, + PaymentId::open( + &format!("{} even coin splits", number_of_splits), + if number_of_splits > 1 { + TxType::CoinSplit + } else { + TxType::CoinJoin + }, + ), )) { Ok(tx_id) => { ptr::replace(error_ptr, 0); @@ -6597,10 +6678,15 @@ pub unsafe extern "C" fn wallet_coin_join( }, }; - match (*wallet) - .runtime - .block_on((*wallet).wallet.coin_join(commitments, fee_per_gram.into(), None)) - { + let commitments_len = commitments.len(); + match (*wallet).runtime.block_on((*wallet).wallet.coin_join( + commitments, + fee_per_gram.into(), + Some(PaymentId::open( + &format!("Coin join {} outputs", commitments_len), + TxType::CoinJoin, + )), + )) { Ok(tx_id) => { ptr::replace(error_ptr, 0); tx_id.as_u64() @@ -7319,10 +7405,10 @@ pub unsafe extern "C" fn wallet_send_transaction( }; let payment_id = if payment_id_string.is_null() { - PaymentId::Empty + PaymentId::open("", TxType::PaymentToOther) } else { match CStr::from_ptr(payment_id_string).to_str() { - Ok(v) => PaymentId::open_from_str(v), + Ok(v) => PaymentId::open(v, TxType::PaymentToOther), _ => { error = LibWalletError::from(InterfaceError::NullError("payment_id".to_string())).code; ptr::swap(error_out, &mut error as *mut c_int); @@ -11082,6 +11168,7 @@ mod test { 0, key_manager, vec![i, i + 1, i + 2, i + 3, i + 4], + None, )); test_outputs.push(uout.clone()); alice_wallet_runtime @@ -11261,6 +11348,7 @@ mod test { 0, &(*alice_wallet).wallet.key_manager_service, vec![], + None, )); (*alice_wallet) .runtime @@ -11324,6 +11412,157 @@ mod test { } } + #[test] + #[allow(clippy::too_many_lines, clippy::needless_collect)] + fn test_wallet_transaction_type_from_encrypted_data() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let mut recovery_in_progress = true; + let recovery_in_progress_ptr = &mut recovery_in_progress as *mut bool; + + let secret_key_alice = private_key_generate(); + let db_name_alice = CString::new(random::string(8).as_str()).unwrap(); + let db_name_alice_str: *const c_char = CString::into_raw(db_name_alice) as *const c_char; + let alice_temp_dir = tempdir().unwrap(); + let db_path_alice = CString::new(alice_temp_dir.path().to_str().unwrap()).unwrap(); + let db_path_alice_str: *const c_char = CString::into_raw(db_path_alice) as *const c_char; + let transport_config_alice = transport_memory_create(); + let address_alice = transport_memory_get_address(transport_config_alice, error_ptr); + let address_alice_str = CStr::from_ptr(address_alice).to_str().unwrap().to_owned(); + let address_alice_str: *const c_char = CString::new(address_alice_str).unwrap().into_raw() as *const c_char; + let network = CString::new(NETWORK_STRING).unwrap(); + let network_str: *const c_char = CString::into_raw(network) as *const c_char; + + let alice_config = comms_config_create( + address_alice_str, + transport_config_alice, + db_name_alice_str, + db_path_alice_str, + 20, + 10800, + false, + error_ptr, + ); + + let passphrase: *const c_char = + CString::into_raw(CString::new("The master and margarita").unwrap()) as *const c_char; + let dns_string: *const c_char = CString::into_raw(CString::new("").unwrap()) as *const c_char; + let void_ptr: *mut c_void = &mut (5) as *mut _ as *mut c_void; + let alice_wallet = wallet_create( + void_ptr, + alice_config, + ptr::null(), + 0, + 0, + 0, + passphrase, + ptr::null(), + ptr::null(), + network_str, + dns_string, + ptr::null(), + true, + received_tx_callback, + received_tx_reply_callback, + received_tx_finalized_callback, + broadcast_callback, + mined_callback, + mined_unconfirmed_callback, + scanned_callback, + scanned_unconfirmed_callback, + transaction_send_result_callback, + tx_cancellation_callback, + txo_validation_complete_callback, + contacts_liveness_data_updated_callback, + balance_updated_callback, + transaction_validation_complete_callback, + saf_messages_received_callback, + connectivity_status_callback, + wallet_scanned_height_callback, + base_node_state_callback, + recovery_in_progress_ptr, + error_ptr, + ); + + assert_eq!(error, 0); + + // Tests for transaction type extraction from encrypted data + for tx_type in [ + TxType::PaymentToOther, + TxType::PaymentToSelf, + TxType::Burn, + TxType::CoinSplit, + TxType::CoinJoin, + TxType::ValidatorNodeRegistration, + TxType::ClaimAtomicSwap, + TxType::HtlcAtomicSwapRefund, + TxType::CodeTemplateRegistration, + TxType::ImportedUtxoNoneRewindable, + ] { + for payment_id in [ + PaymentId::Open { + user_data: "hallo world".as_bytes().to_vec(), + tx_type: tx_type.clone(), + }, + PaymentId::AddressAndData { + sender_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk") + .unwrap(), + tx_type: tx_type.clone(), + user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + }, + PaymentId::TransactionInfo { + recipient_address: TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk") + .unwrap(), + sender_one_sided: false, + amount: MicroMinotari::from(123456), + fee: MicroMinotari::from(123), + weight: 19000, + inputs_count: 712, + outputs_count: 3, + tx_type: tx_type.clone(), + user_data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + }, + ] { + let wallet_output = (*alice_wallet).runtime.block_on(create_test_input( + 15000.into(), + 0, + &(*alice_wallet).wallet.key_manager_service, + vec![], + Some(payment_id.clone()), + )); + assert_eq!(wallet_output.payment_id, payment_id); + let utxo = (*alice_wallet) + .runtime + .block_on(wallet_output.to_transaction_output(&(*alice_wallet).wallet.key_manager_service)) + .unwrap(); + let commitment_bytes = Box::into_raw(Box::new(ByteVector(utxo.commitment.to_vec()))); + let encrypted_data_ptr = Box::into_raw(Box::new(utxo.encrypted_data)); + let transaction_type_extracted = transaction_type_from_encrypted_data( + encrypted_data_ptr, + commitment_bytes, + alice_wallet, + error_ptr, + ); + assert_eq!(error, 0); + assert_eq!(transaction_type_extracted, u32::from(tx_type.as_u8())); + + encrypted_data_destroy(encrypted_data_ptr); + byte_vector_destroy(commitment_bytes); + } + } + + string_destroy(network_str as *mut c_char); + string_destroy(db_name_alice_str as *mut c_char); + string_destroy(db_path_alice_str as *mut c_char); + string_destroy(address_alice_str as *mut c_char); + private_key_destroy(secret_key_alice); + transport_config_destroy(transport_config_alice); + comms_config_destroy(alice_config); + wallet_destroy(alice_wallet); + } + } + #[test] #[allow(clippy::too_many_lines, clippy::needless_collect)] fn test_wallet_coin_join() { @@ -11404,6 +11643,7 @@ mod test { 0, &(*alice_wallet).wallet.key_manager_service, vec![], + None, )); (*alice_wallet) .runtime @@ -11478,6 +11718,32 @@ mod test { assert_eq!(error, 0); assert!(result > 0); + // Verify payment ID is correctly set in the db and corresponds to the embedded value in encrypted data + let utxos_from_db = (*alice_wallet) + .wallet + .output_db + .fetch_outputs_by_query(OutputBackendQuery { + status: vec![OutputStatus::EncumberedToBeReceived], + ..Default::default() + }) + .unwrap(); + for utxo in &utxos_from_db { + let extracted_payment_id = (*alice_wallet) + .runtime + .block_on( + (*alice_wallet) + .wallet + .key_manager_service + .extract_payment_id_from_encrypted_data( + &utxo.wallet_output.encrypted_data, + &utxo.commitment, + None, + ), + ) + .unwrap(); + assert_eq!(utxo.payment_id, extracted_payment_id); + } + let unspent_outputs = (*alice_wallet) .wallet .output_db @@ -11533,6 +11799,27 @@ mod test { // checking fee assert_eq!(pre_join_total_amount - post_join_total_amount, (*preview).fee); + // Verify payment ID is correctly set and can be accessed via the FFI + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 20, + TariUtxoSort::ValueAsc, + Box::into_raw(Box::new(TariVector::from(vec![OutputStatus::EncumberedToBeReceived]))), + 0, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); + for (utxo, utxo_from_db) in utxos.iter().zip(utxos_from_db.iter()) { + let payment_id_c_str: &str = CStr::from_ptr(utxo.payment_id).to_str().unwrap(); + assert_eq!( + OutputStatus::try_from(i32::from(utxo.status)).unwrap(), + OutputStatus::EncumberedToBeReceived + ); + assert_eq!(payment_id_c_str, &format!("{}", utxo_from_db.payment_id)); + assert!(payment_id_c_str.contains("CoinJoin")); + } + destroy_tari_vector(outputs); destroy_tari_vector(commitments); destroy_tari_coin_preview(preview); @@ -11627,6 +11914,7 @@ mod test { 0, &(*alice_wallet).wallet.key_manager_service, vec![], + None, )); (*alice_wallet) .runtime @@ -11704,6 +11992,32 @@ mod test { assert_eq!(error, 0); assert!(result > 0); + // Verify payment ID is correctly set in the db and corresponds to the embedded value in encrypted data + let utxos_from_db = (*alice_wallet) + .wallet + .output_db + .fetch_outputs_by_query(OutputBackendQuery { + status: vec![OutputStatus::EncumberedToBeReceived], + ..Default::default() + }) + .unwrap(); + for utxo in &utxos_from_db { + let extracted_payment_id = (*alice_wallet) + .runtime + .block_on( + (*alice_wallet) + .wallet + .key_manager_service + .extract_payment_id_from_encrypted_data( + &utxo.wallet_output.encrypted_data, + &utxo.commitment, + None, + ), + ) + .unwrap(); + assert_eq!(utxo.payment_id, extracted_payment_id); + } + let unspent_outputs = (*alice_wallet) .wallet .output_db @@ -11765,6 +12079,27 @@ mod test { // checking fee assert_eq!(pre_split_total_amount - post_split_total_amount, (*preview).fee); + // Verify payment ID is correctly set and can be accessed via the FFI + let outputs = wallet_get_utxos( + alice_wallet, + 0, + 20, + TariUtxoSort::ValueAsc, + Box::into_raw(Box::new(TariVector::from(vec![OutputStatus::EncumberedToBeReceived]))), + 0, + error_ptr, + ); + let utxos: &[TariUtxo] = slice::from_raw_parts_mut((*outputs).ptr as *mut TariUtxo, (*outputs).len); + for (utxo, utxo_from_db) in utxos.iter().zip(utxos_from_db.iter()) { + let payment_id_c_str: &str = CStr::from_ptr(utxo.payment_id).to_str().unwrap(); + assert_eq!( + OutputStatus::try_from(i32::from(utxo.status)).unwrap(), + OutputStatus::EncumberedToBeReceived + ); + assert_eq!(payment_id_c_str, &format!("{}", utxo_from_db.payment_id)); + assert!(payment_id_c_str.contains("CoinSplit")); + } + destroy_tari_vector(outputs); destroy_tari_vector(commitments); destroy_tari_coin_preview(preview); @@ -11857,17 +12192,16 @@ mod test { for i in 1..=5 { (*alice_wallet) .runtime - .block_on( - (*alice_wallet).wallet.output_manager_service.add_output( - (*alice_wallet).runtime.block_on(create_test_input( - (15000 * i).into(), - 0, - key_manager, - vec![], - )), + .block_on((*alice_wallet).wallet.output_manager_service.add_output( + (*alice_wallet).runtime.block_on(create_test_input( + (15000 * i).into(), + 0, + key_manager, + vec![], None, - ), - ) + )), + None, + )) .unwrap(); } diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h index 0cf8a8c2d9..85416a798b 100644 --- a/base_layer/wallet_ffi/wallet.h +++ b/base_layer/wallet_ffi/wallet.h @@ -1396,7 +1396,7 @@ void covenant_destroy(TariCovenant *covenant); * `encrypted_data_bytes` - The encrypted_data bytes as a ByteVector * * ## Returns - * `TariEncryptedOpenings` - Returns encrypted data. Note that it will be ptr::null_mut() if any argument is + * `TariEncryptedOpenings` - Returns encrypted data. Note that it will be ptr::null_mut() if any argument is * null or if there was an error with the contents of bytes * * # Safety @@ -1406,6 +1406,35 @@ void covenant_destroy(TariCovenant *covenant); TariEncryptedOpenings *encrypted_data_create_from_bytes(const struct ByteVector *encrypted_data_bytes, int *error_out); +/** + * Extract the transaction type from a TariEncryptedOpenings + * + * ## Arguments + * `encrypted_data` - The encrypted data + * `commitment_bytes` - The public commitment component as a ByteVector + * `wallet` - The TariWallet pointe + * + * ## Returns + * `0` => `PaymentToOther`, + * `1` => `PaymentToSelf`, + * `2` => `Burn`, + * `3` => `CoinSplit`, + * `4` => `CoinJoin`, + * `5` => `ValidatorNodeRegistration`, + * `6` => `ClaimAtomicSwap`, + * `7` => `HtlcAtomicSwapRefund`, + * `8` => `CodeTemplateRegistration`, + * `9` => `ImportedUtxoNoneRewindable`, + * `99` => `None` + * + * # Safety + * None + */ +unsigned int transaction_type_from_encrypted_data(const TariEncryptedOpenings *encrypted_data, + const struct ByteVector *commitment_bytes, + struct TariWallet *wallet, + int *error_out); + /** * Creates a ByteVector containing the encrypted_data bytes from a TariEncryptedOpenings * diff --git a/common/config/presets/c_base_node_c.toml b/common/config/presets/c_base_node_c.toml index fe7c1e5ea3..56b39b78ba 100644 --- a/common/config/presets/c_base_node_c.toml +++ b/common/config/presets/c_base_node_c.toml @@ -39,7 +39,7 @@ # (min value = 30, default value = 1500). #buffer_size = 1500 -# Liveness meta data auto ping interval between peers (default = 30 s) +# Liveness metadata auto ping interval between peers (default = 30 s) #metadata_auto_ping_interval = 30 # Obscure GRPC error responses (default = false) diff --git a/integration_tests/src/ffi/ffi_import.rs b/integration_tests/src/ffi/ffi_import.rs index 660302cda1..d737292a28 100644 --- a/integration_tests/src/ffi/ffi_import.rs +++ b/integration_tests/src/ffi/ffi_import.rs @@ -166,6 +166,12 @@ extern "C" { encrypted_data_bytes: *const ByteVector, error_out: *mut c_int, ) -> *mut TariEncryptedOpenings; + pub fn transaction_type_from_encrypted_data( + encrypted_data: *const TariEncryptedOpenings, + commitment_bytes: *const ByteVector, + wallet: *mut TariWallet, + error_out: *mut c_int, + ) -> c_uint; pub fn encrypted_data_as_bytes( encrypted_data: *const TariEncryptedOpenings, error_out: *mut c_int, diff --git a/integration_tests/tests/steps/wallet_ffi_steps.rs b/integration_tests/tests/steps/wallet_ffi_steps.rs index 4d4b7c346d..679b0761b8 100644 --- a/integration_tests/tests/steps/wallet_ffi_steps.rs +++ b/integration_tests/tests/steps/wallet_ffi_steps.rs @@ -24,7 +24,7 @@ use std::{convert::TryFrom, io::BufRead, ptr::null, time::Duration}; use cucumber::{given, then, when}; use tari_common_types::tari_address::TariAddress; -use tari_core::transactions::transaction_components::encrypted_data::PaymentId; +use tari_core::transactions::transaction_components::encrypted_data::{PaymentId, TxType}; use tari_integration_tests::{ wallet_ffi::{create_contact, create_seed_words, get_mnemonic_word_list_for_language, spawn_wallet_ffi}, TariWorld, @@ -210,7 +210,10 @@ async fn ffi_check_no_contact(world: &mut TariWorld, alias: String, wallet: Stri async fn ffi_send_transaction(world: &mut TariWorld, amount: u64, wallet: String, dest: String, fee: u64) { let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); let dest_pub_key = world.get_wallet_address(&dest).await.unwrap(); - let payment_id = PaymentId::open_from_str(&format!("Send from ffi {} to ${} at fee ${}", wallet, dest, fee)); + let payment_id = PaymentId::open( + &format!("Send from ffi {} to ${} at fee ${}", wallet, dest, fee), + TxType::PaymentToOther, + ); let tx_id = ffi_wallet.send_transaction(dest_pub_key, amount, fee, payment_id, false); assert_ne!(tx_id, 0, "Send transaction was not successful"); } @@ -220,7 +223,10 @@ async fn ffi_send_transaction(world: &mut TariWorld, amount: u64, wallet: String async fn ffi_send_one_sided_transaction(world: &mut TariWorld, amount: u64, wallet: String, dest: String, fee: u64) { let ffi_wallet = world.get_ffi_wallet(&wallet).unwrap(); let dest_pub_key = world.get_wallet_address(&dest).await.unwrap(); - let payment_id = PaymentId::open_from_str(&format!("Send from ffi {} to ${} at fee ${}", wallet, dest, fee)); + let payment_id = PaymentId::open( + &format!("Send from ffi {} to ${} at fee ${}", wallet, dest, fee), + TxType::PaymentToOther, + ); let tx_id = ffi_wallet.send_transaction(dest_pub_key, amount, fee, payment_id, true); assert_ne!(tx_id, 0, "Send transaction was not successful"); } diff --git a/integration_tests/tests/steps/wallet_steps.rs b/integration_tests/tests/steps/wallet_steps.rs index 10daa109bc..01f24d8554 100644 --- a/integration_tests/tests/steps/wallet_steps.rs +++ b/integration_tests/tests/steps/wallet_steps.rs @@ -48,7 +48,7 @@ use tari_core::{ transactions::{ tari_amount::MicroMinotari, transaction_components::{ - encrypted_data::PaymentId, + encrypted_data::{PaymentId, TxType}, CoinBaseExtra, EncryptedData, OutputFeatures, @@ -705,12 +705,15 @@ async fn send_amount_from_source_wallet_to_dest_wallet_without_broadcast( amount, fee_per_gram: fee, payment_type: 0, // normal mimblewimble payment type - payment_id: PaymentId::open_from_str(&format!( - "transfer amount {} from {} to {}", - amount, - source_wallet.as_str(), - dest_wallet.as_str() - )) + payment_id: PaymentId::open( + &format!( + "transfer amount {} from {} to {}", + amount, + source_wallet.as_str(), + dest_wallet.as_str() + ), + TxType::PaymentToOther, + ) .to_bytes(), }; let transfer_req = TransferRequest { @@ -766,12 +769,15 @@ async fn send_one_sided_transaction_from_source_wallet_to_dest_wallt( amount, fee_per_gram: fee, payment_type: 1, // one sided transaction - payment_id: PaymentId::open_from_str(&format!( - "One sided transfer amount {} from {} to {}", - amount, - source_wallet.as_str(), - dest_wallet.as_str() - )) + payment_id: PaymentId::open( + &format!( + "One sided transfer amount {} from {} to {}", + amount, + source_wallet.as_str(), + dest_wallet.as_str() + ), + TxType::PaymentToOther, + ) .to_bytes(), }; let transfer_req = TransferRequest { @@ -865,13 +871,16 @@ async fn send_amount_from_wallet_to_wallet_at_fee( amount, fee_per_gram, payment_type: 0, // mimblewimble transaction - payment_id: PaymentId::open_from_str(&format!( - "Transfer amount {} from {} to {} as fee {}", - amount, - sender.as_str(), - receiver.as_str(), - fee_per_gram - )) + payment_id: PaymentId::open( + &format!( + "Transfer amount {} from {} to {} as fee {}", + amount, + sender.as_str(), + receiver.as_str(), + fee_per_gram + ), + TxType::PaymentToOther, + ) .to_bytes(), }; let transfer_req = TransferRequest { @@ -1296,12 +1305,15 @@ async fn send_num_transactions_to_wallets_at_fee( amount, fee_per_gram, payment_type: 0, // standard mimblewimble transaction - payment_id: PaymentId::open_from_str(&format!( - "transfer amount {} from {} to {}", - amount, - sender_wallet.as_str(), - receiver_wallet.as_str() - )) + payment_id: PaymentId::open( + &format!( + "transfer amount {} from {} to {}", + amount, + sender_wallet.as_str(), + receiver_wallet.as_str() + ), + TxType::PaymentToOther, + ) .to_bytes(), }; let transfer_req = TransferRequest { @@ -1438,12 +1450,15 @@ async fn transfer_tari_from_wallet_to_receiver(world: &mut TariWorld, amount: u6 amount: amount * 1_000_000_u64, // 1T = 1_000_000uT fee_per_gram: 10, // as in the js cucumber tests payment_type: 0, // normal mimblewimble payment type - payment_id: PaymentId::open_from_str(&format!( - "transfer amount {} from {} to {}", - amount, - sender.as_str(), - receiver.as_str() - )) + payment_id: PaymentId::open( + &format!( + "transfer amount {} from {} to {}", + amount, + sender.as_str(), + receiver.as_str() + ), + TxType::PaymentToOther, + ) .to_bytes(), }; let transfer_req = TransferRequest { @@ -1633,12 +1648,15 @@ async fn transfer_from_wallet_to_two_recipients_at_fee( amount, fee_per_gram, payment_type: 0, // normal mimblewimble payment type - payment_id: PaymentId::open_from_str(&format!( - "transfer amount {} from {} to {}", - amount, - sender.as_str(), - receiver1.as_str() - )) + payment_id: PaymentId::open( + &format!( + "transfer amount {} from {} to {}", + amount, + sender.as_str(), + receiver1.as_str() + ), + TxType::PaymentToOther, + ) .to_bytes(), }; @@ -1647,12 +1665,15 @@ async fn transfer_from_wallet_to_two_recipients_at_fee( amount, fee_per_gram, payment_type: 0, // normal mimblewimble payment type - payment_id: PaymentId::open_from_str(&format!( - "transfer amount {} from {} to {}", - amount, - sender.as_str(), - receiver2.as_str() - )) + payment_id: PaymentId::open( + &format!( + "transfer amount {} from {} to {}", + amount, + sender.as_str(), + receiver2.as_str() + ), + TxType::PaymentToOther, + ) .to_bytes(), }; let transfer_req = TransferRequest { @@ -1764,8 +1785,11 @@ async fn transfer_tari_to_self(world: &mut TariWorld, amount: u64, sender: Strin amount, fee_per_gram, payment_type: 0, // normal mimblewimble payment type - payment_id: PaymentId::open_from_str(&format!("transfer amount {} from {} to self", amount, sender.as_str())) - .to_bytes(), + payment_id: PaymentId::open( + &format!("transfer amount {} from {} to self", amount, sender.as_str()), + TxType::PaymentToSelf, + ) + .to_bytes(), }; let transfer_req = TransferRequest { recipients: vec![payment_recipient], @@ -1844,13 +1868,16 @@ async fn htlc_transaction(world: &mut TariWorld, amount: u64, sender: String, re amount, fee_per_gram, payment_type: 0, // normal mimblewimble transaction - payment_id: PaymentId::open_from_str(&format!( - "Atomic Swap from {} to {} with amount {} at fee {}", - sender.as_str(), - receiver.as_str(), - amount, - fee_per_gram - )) + payment_id: PaymentId::open( + &format!( + "Atomic Swap from {} to {} with amount {} at fee {}", + sender.as_str(), + receiver.as_str(), + amount, + fee_per_gram + ), + TxType::PaymentToOther, + ) .to_bytes(), }; @@ -2135,12 +2162,15 @@ async fn send_one_sided_stealth_transaction( amount, fee_per_gram, payment_type: 2, // one sided stealth transaction - payment_id: PaymentId::open_from_str(&format!( - "One sided stealth transfer amount {} from {} to {}", - amount, - sender.as_str(), - receiver.as_str() - )) + payment_id: PaymentId::open( + &format!( + "One sided stealth transfer amount {} from {} to {}", + amount, + sender.as_str(), + receiver.as_str() + ), + TxType::PaymentToOther, + ) .to_bytes(), }; let transfer_req = TransferRequest { @@ -2327,8 +2357,11 @@ async fn import_wallet_unspent_outputs(world: &mut TariWorld, wallet_a: String, .iter() .map(|o| grpc::UnblindedOutput::try_from(o.clone()).expect("Unable to make grpc conversion")) .collect::>(), - payment_id: PaymentId::open_from_str(&format!("I import {} unspent outputs to {}", wallet_a, wallet_b)) - .to_bytes(), + payment_id: PaymentId::open( + &format!("I import {} unspent outputs to {}", wallet_a, wallet_b), + TxType::ImportedUtxoNoneRewindable, + ) + .to_bytes(), }; world.last_imported_tx_ids = wallet_b_client @@ -2449,8 +2482,11 @@ async fn import_wallet_spent_outputs(world: &mut TariWorld, wallet_a: String, wa .iter() .map(|o| grpc::UnblindedOutput::try_from(o.clone()).expect("Unable to make grpc conversion")) .collect::>(), - payment_id: PaymentId::open_from_str(&format!("I import {} spent outputs to {}", wallet_a, wallet_b)) - .to_bytes(), + payment_id: PaymentId::open( + &format!("I import {} spent outputs to {}", wallet_a, wallet_b), + TxType::ImportedUtxoNoneRewindable, + ) + .to_bytes(), }; world.last_imported_tx_ids = wallet_b_client @@ -2570,10 +2606,13 @@ async fn import_unspent_outputs_as_pre_mine(world: &mut TariWorld, wallet_a: Str .iter() .map(|o| grpc::UnblindedOutput::try_from(o.clone()).expect("Unable to make grpc conversion")) .collect::>(), - payment_id: PaymentId::open_from_str(&format!( - "I import {} unspent outputs as pre_mine outputs to {}", - wallet_a, wallet_b - )) + payment_id: PaymentId::open( + &format!( + "I import {} unspent outputs as pre_mine outputs to {}", + wallet_a, wallet_b + ), + TxType::ImportedUtxoNoneRewindable, + ) .to_bytes(), }; @@ -2661,13 +2700,16 @@ async fn multi_send_txs_from_wallet( amount, fee_per_gram, payment_type: 0, // mimblewimble transaction - payment_id: PaymentId::open_from_str(&format!( - "I send multi-transfers with amount {} from {} to {} with fee per gram {}", - amount, - sender.as_str(), - receiver.as_str(), - fee_per_gram - )) + payment_id: PaymentId::open( + &format!( + "I send multi-transfers with amount {} from {} to {} with fee per gram {}", + amount, + sender.as_str(), + receiver.as_str(), + fee_per_gram + ), + TxType::PaymentToOther, + ) .to_bytes(), }; @@ -2828,7 +2870,7 @@ async fn burn_transaction(world: &mut TariWorld, amount: u64, wallet: String, fe amount, fee_per_gram: fee, claim_public_key: identity.public_key, - payment_id: PaymentId::open_from_str("Burning some tari").to_bytes(), + payment_id: PaymentId::open("Burning some tari", TxType::Burn).to_bytes(), }; let result = client.create_burn_transaction(req).await.unwrap(); From 12c9e1ecd923c3ea66623196d2ef696465307db1 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Fri, 17 Jan 2025 09:56:30 +0200 Subject: [PATCH 2/4] Update applications/minotari_console_wallet/src/ui/components/transactions_tab.rs --- .../src/ui/components/transactions_tab.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs b/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs index ae2ef57c33..dfa2145256 100644 --- a/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs +++ b/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs @@ -230,8 +230,6 @@ impl TransactionsTab { transaction_type = tx_type.clone(); }; - // TODO: Remove when backwards compatibility for `PaymentId::Open` and `PaymentId::AddressAndData` is no - // TODO: longer required. if let Some(PaymentId::Open { .. } | PaymentId::AddressAndData { .. }) = tx.payment_id.clone() { if transaction_type == TxType::PaymentToSelf && tx.source_address != tx.destination_address { transaction_type = TxType::PaymentToOther; From 0ee24f5bda47638b461631e518a74a5b8264bfe1 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Fri, 17 Jan 2025 09:56:37 +0200 Subject: [PATCH 3/4] Update base_layer/core/src/transactions/transaction_components/encrypted_data.rs --- .../src/transactions/transaction_components/encrypted_data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base_layer/core/src/transactions/transaction_components/encrypted_data.rs b/base_layer/core/src/transactions/transaction_components/encrypted_data.rs index e18405d796..2428f22886 100644 --- a/base_layer/core/src/transactions/transaction_components/encrypted_data.rs +++ b/base_layer/core/src/transactions/transaction_components/encrypted_data.rs @@ -163,7 +163,7 @@ pub enum PaymentId { U64(u64), /// A u256 number. U256(U256), - /// Open - the user optionally specifies a human-readable string 'user_data' ('tx_type' is added by the system). + /// Open - the user optionally specifies 'user_data' ('tx_type' is added by the system). Open { user_data: Vec, tx_type: TxType }, /// This payment ID is automatically generated by the system for output UTXOs. The optional user specified /// `PaymentId::Open` payment ID will be assigned to `tx_type` and `user_data`; the system adds in the sender From ae4463cf14bc0b444ceea61e4e14ce3322092b6d Mon Sep 17 00:00:00 2001 From: Hansie Odendaal Date: Fri, 17 Jan 2025 10:16:35 +0200 Subject: [PATCH 4/4] fix merge conflicts --- base_layer/wallet/tests/transaction_service_tests/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base_layer/wallet/tests/transaction_service_tests/service.rs b/base_layer/wallet/tests/transaction_service_tests/service.rs index 1b56b0b644..f6898c52f6 100644 --- a/base_layer/wallet/tests/transaction_service_tests/service.rs +++ b/base_layer/wallet/tests/transaction_service_tests/service.rs @@ -6261,7 +6261,7 @@ async fn test_completed_transactions_ordering() { mined_height: None, mined_in_block: None, mined_timestamp: DateTime::::from_timestamp(random_timestamp + 100i64, 0), - payment_id: PaymentId::open_from_str("Yo!"), + payment_id: PaymentId::open("Yo!", TxType::PaymentToOther), }; tx_backend