From e97afc5b35abe152536449fa69045f85f2a114b2 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Fri, 16 Aug 2024 17:25:28 +0200 Subject: [PATCH] feat!: add source address to encrypted data (#6472) Description --- This adds the source address to encrypted data Motivation and Context --- This allows a one-sided payment to send over its address to the receiving wallet It also allows a wallet recovering funds to see where the funds came from. How Has This Been Tested? --- unit test and manual --- .../src/ui/components/transactions_tab.rs | 8 +- .../src/tari_address/dual_address.rs | 18 +-- .../common_types/src/tari_address/mod.rs | 34 ++++-- .../src/tari_address/single_address.rs | 18 +-- .../unconfirmed_pool/unconfirmed_pool.rs | 2 + .../core/src/transactions/test_helpers.rs | 3 + .../transaction_components/encrypted_data.rs | 110 ++++++++++++++++-- .../proto/transaction_sender.proto | 2 + .../proto/transaction_sender.rs | 10 +- .../transaction_protocol/recipient.rs | 3 +- .../transaction_protocol/sender.rs | 16 ++- .../transaction_protocol/single_receiver.rs | 4 +- .../transaction_initializer.rs | 30 ++++- .../core/tests/tests/block_validation.rs | 4 +- .../core/tests/tests/node_comms_interface.rs | 3 +- .../wallet/src/output_manager_service/mod.rs | 2 + .../src/output_manager_service/resources.rs | 3 + .../src/output_manager_service/service.rs | 56 +++++++-- .../protocols/transaction_receive_protocol.rs | 3 +- .../wallet/src/transaction_service/service.rs | 54 +++++---- .../transaction_service/storage/sqlite_db.rs | 1 + .../tasks/send_transaction_reply.rs | 2 +- .../utxo_scanner_service/utxo_scanner_task.rs | 9 +- .../output_manager_service_tests/service.rs | 5 + .../transaction_service_tests/service.rs | 6 + .../transaction_service_tests/storage.rs | 1 + 26 files changed, 311 insertions(+), 96 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 7d934f1651..c7ea9d74ff 100644 --- a/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs +++ b/applications/minotari_console_wallet/src/ui/components/transactions_tab.rs @@ -7,7 +7,6 @@ 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 tokio::runtime::Handle; use tui::{ backend::Backend, @@ -442,12 +441,13 @@ impl TransactionsTab { let payment_id = match tx.payment_id.clone() { Some(v) => { - if let PaymentId::Open(bytes) = v { + let bytes = v.get_data(); + if bytes.is_empty() { + format!("#{}", v) + } else { String::from_utf8(bytes) .unwrap_or_else(|_| "Invalid string".to_string()) .to_string() - } else { - format!("#{}", v) } }, None => "None".to_string(), diff --git a/base_layer/common_types/src/tari_address/dual_address.rs b/base_layer/common_types/src/tari_address/dual_address.rs index d8f7aace39..892e2d0c1c 100644 --- a/base_layer/common_types/src/tari_address/dual_address.rs +++ b/base_layer/common_types/src/tari_address/dual_address.rs @@ -35,7 +35,7 @@ use crate::{ TariAddressFeatures, INTERNAL_DUAL_BASE58_MAX_SIZE, INTERNAL_DUAL_BASE58_MIN_SIZE, - INTERNAL_DUAL_SIZE, + TARI_ADDRESS_INTERNAL_DUAL_SIZE, }, types::PublicKey, }; @@ -77,12 +77,12 @@ impl DualAddress { /// helper function to convert emojis to u8 pub fn emoji_to_bytes(emoji: &str) -> Result, TariAddressError> { // The string must be the correct size, including the checksum - if emoji.chars().count() != INTERNAL_DUAL_SIZE { + if emoji.chars().count() != TARI_ADDRESS_INTERNAL_DUAL_SIZE { return Err(TariAddressError::InvalidSize); } // Convert the emoji string to a byte array - let mut bytes = Vec::::with_capacity(INTERNAL_DUAL_SIZE); + let mut bytes = Vec::::with_capacity(TARI_ADDRESS_INTERNAL_DUAL_SIZE); for c in emoji.chars() { if let Some(i) = REVERSE_EMOJI.get(&c) { bytes.push(*i); @@ -130,7 +130,7 @@ impl DualAddress { /// Construct Tari Address from bytes pub fn from_bytes(bytes: &[u8]) -> Result where Self: Sized { - if bytes.len() != INTERNAL_DUAL_SIZE { + if bytes.len() != TARI_ADDRESS_INTERNAL_DUAL_SIZE { return Err(TariAddressError::InvalidSize); } if validate_checksum(bytes).is_err() { @@ -151,8 +151,8 @@ impl DualAddress { } /// Convert Tari Address to bytes - pub fn to_bytes(&self) -> [u8; INTERNAL_DUAL_SIZE] { - let mut buf = [0u8; INTERNAL_DUAL_SIZE]; + pub fn to_bytes(&self) -> [u8; TARI_ADDRESS_INTERNAL_DUAL_SIZE] { + let mut buf = [0u8; TARI_ADDRESS_INTERNAL_DUAL_SIZE]; buf[0] = self.network.as_byte(); buf[1] = self.features.0; buf[2..34].copy_from_slice(self.public_view_key.as_bytes()); @@ -231,7 +231,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_DUAL_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_DUAL_SIZE); let features = emoji_id_from_public_key.features(); assert_eq!(features, TariAddressFeatures::create_interactive_and_one_sided()); @@ -259,7 +259,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_DUAL_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_DUAL_SIZE); let features = emoji_id_from_public_key.features(); assert_eq!(features, TariAddressFeatures::create_interactive_only()); @@ -288,7 +288,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_DUAL_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_DUAL_SIZE); let features = emoji_id_from_public_key.features(); assert_eq!(features, TariAddressFeatures::create_one_sided_only()); diff --git a/base_layer/common_types/src/tari_address/mod.rs b/base_layer/common_types/src/tari_address/mod.rs index 27795af58d..e28da48930 100644 --- a/base_layer/common_types/src/tari_address/mod.rs +++ b/base_layer/common_types/src/tari_address/mod.rs @@ -42,8 +42,8 @@ use crate::{ types::PublicKey, }; -const INTERNAL_DUAL_SIZE: usize = 67; // number of bytes used for the internal representation -const INTERNAL_SINGLE_SIZE: usize = 35; // number of bytes used for the internal representation +pub const TARI_ADDRESS_INTERNAL_DUAL_SIZE: usize = 67; // number of bytes used for the internal representation +pub const TARI_ADDRESS_INTERNAL_SINGLE_SIZE: usize = 35; // number of bytes used for the internal representation const INTERNAL_DUAL_BASE58_MIN_SIZE: usize = 89; // number of bytes used for the internal representation const INTERNAL_DUAL_BASE58_MAX_SIZE: usize = 91; // number of bytes used for the internal representation const INTERNAL_SINGLE_MIN_BASE58_SIZE: usize = 45; // number of bytes used for the internal representation @@ -152,13 +152,23 @@ impl TariAddress { TariAddress::Single(SingleAddress::new_with_interactive_only(spend_key, network)) } + /// Gets the bytes size of the Tari Address + pub fn get_size(&self) -> usize { + match self { + TariAddress::Dual(_) => TARI_ADDRESS_INTERNAL_DUAL_SIZE, + TariAddress::Single(_) => TARI_ADDRESS_INTERNAL_SINGLE_SIZE, + } + } + /// helper function to convert emojis to u8 fn emoji_to_bytes(emoji: &str) -> Result, TariAddressError> { // The string must be the correct size, including the checksum - if !(emoji.chars().count() == INTERNAL_SINGLE_SIZE || emoji.chars().count() == INTERNAL_DUAL_SIZE) { + if !(emoji.chars().count() == TARI_ADDRESS_INTERNAL_SINGLE_SIZE || + emoji.chars().count() == TARI_ADDRESS_INTERNAL_DUAL_SIZE) + { return Err(TariAddressError::InvalidSize); } - if emoji.chars().count() == INTERNAL_SINGLE_SIZE { + if emoji.chars().count() == TARI_ADDRESS_INTERNAL_SINGLE_SIZE { SingleAddress::emoji_to_bytes(emoji) } else { DualAddress::emoji_to_bytes(emoji) @@ -229,10 +239,10 @@ impl TariAddress { /// Construct Tari Address from bytes pub fn from_bytes(bytes: &[u8]) -> Result where Self: Sized { - if !(bytes.len() == INTERNAL_SINGLE_SIZE || bytes.len() == INTERNAL_DUAL_SIZE) { + if !(bytes.len() == TARI_ADDRESS_INTERNAL_SINGLE_SIZE || bytes.len() == TARI_ADDRESS_INTERNAL_DUAL_SIZE) { return Err(TariAddressError::InvalidSize); } - if bytes.len() == INTERNAL_SINGLE_SIZE { + if bytes.len() == TARI_ADDRESS_INTERNAL_SINGLE_SIZE { Ok(TariAddress::Single(SingleAddress::from_bytes(bytes)?)) } else { Ok(TariAddress::Dual(DualAddress::from_bytes(bytes)?)) @@ -345,7 +355,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_SINGLE_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_SINGLE_SIZE); // Generate an emoji ID from the emoji string and ensure we recover it let emoji_id_from_emoji_string = TariAddress::from_emoji_string(&emoji_string).unwrap(); @@ -370,7 +380,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_SINGLE_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_SINGLE_SIZE); // Generate an emoji ID from the emoji string and ensure we recover it let emoji_id_from_emoji_string = TariAddress::from_emoji_string(&emoji_string).unwrap(); @@ -395,7 +405,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_SINGLE_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_SINGLE_SIZE); // Generate an emoji ID from the emoji string and ensure we recover it let emoji_id_from_emoji_string = TariAddress::from_emoji_string(&emoji_string).unwrap(); @@ -424,7 +434,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_DUAL_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_DUAL_SIZE); let features = emoji_id_from_public_key.features(); assert_eq!(features, TariAddressFeatures::create_interactive_and_one_sided()); @@ -453,7 +463,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_DUAL_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_DUAL_SIZE); let features = emoji_id_from_public_key.features(); assert_eq!(features, TariAddressFeatures::create_interactive_only()); @@ -482,7 +492,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_DUAL_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_DUAL_SIZE); let features = emoji_id_from_public_key.features(); assert_eq!(features, TariAddressFeatures::create_one_sided_only()); diff --git a/base_layer/common_types/src/tari_address/single_address.rs b/base_layer/common_types/src/tari_address/single_address.rs index 4bb527a176..4cc13f7a17 100644 --- a/base_layer/common_types/src/tari_address/single_address.rs +++ b/base_layer/common_types/src/tari_address/single_address.rs @@ -35,7 +35,7 @@ use crate::{ TariAddressFeatures, INTERNAL_SINGLE_MAX_BASE58_SIZE, INTERNAL_SINGLE_MIN_BASE58_SIZE, - INTERNAL_SINGLE_SIZE, + TARI_ADDRESS_INTERNAL_SINGLE_SIZE, }, types::PublicKey, }; @@ -69,12 +69,12 @@ impl SingleAddress { /// helper function to convert emojis to u8 pub fn emoji_to_bytes(emoji: &str) -> Result, TariAddressError> { // The string must be the correct size, including the checksum - if emoji.chars().count() != INTERNAL_SINGLE_SIZE { + if emoji.chars().count() != TARI_ADDRESS_INTERNAL_SINGLE_SIZE { return Err(TariAddressError::InvalidSize); } // Convert the emoji string to a byte array - let mut bytes = Vec::::with_capacity(INTERNAL_SINGLE_SIZE); + let mut bytes = Vec::::with_capacity(TARI_ADDRESS_INTERNAL_SINGLE_SIZE); for c in emoji.chars() { if let Some(i) = REVERSE_EMOJI.get(&c) { bytes.push(*i); @@ -117,7 +117,7 @@ impl SingleAddress { /// Construct Tari Address from bytes pub fn from_bytes(bytes: &[u8]) -> Result where Self: Sized { - if bytes.len() != INTERNAL_SINGLE_SIZE { + if bytes.len() != TARI_ADDRESS_INTERNAL_SINGLE_SIZE { return Err(TariAddressError::InvalidSize); } if validate_checksum(bytes).is_err() { @@ -135,8 +135,8 @@ impl SingleAddress { } /// Convert Tari Address to bytes - pub fn to_bytes(&self) -> [u8; INTERNAL_SINGLE_SIZE] { - let mut buf = [0u8; INTERNAL_SINGLE_SIZE]; + pub fn to_bytes(&self) -> [u8; TARI_ADDRESS_INTERNAL_SINGLE_SIZE] { + let mut buf = [0u8; TARI_ADDRESS_INTERNAL_SINGLE_SIZE]; buf[0] = self.network.as_byte(); buf[1] = self.features.0; buf[2..34].copy_from_slice(self.public_spend_key.as_bytes()); @@ -214,7 +214,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_SINGLE_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_SINGLE_SIZE); // Generate an emoji ID from the emoji string and ensure we recover it let emoji_id_from_emoji_string = SingleAddress::from_emoji_string(&emoji_string).unwrap(); @@ -239,7 +239,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_SINGLE_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_SINGLE_SIZE); // Generate an emoji ID from the emoji string and ensure we recover it let emoji_id_from_emoji_string = SingleAddress::from_emoji_string(&emoji_string).unwrap(); assert_eq!(emoji_id_from_emoji_string.to_emoji_string(), emoji_string); @@ -263,7 +263,7 @@ mod test { // Check the size of the corresponding emoji string let emoji_string = emoji_id_from_public_key.to_emoji_string(); - assert_eq!(emoji_string.chars().count(), INTERNAL_SINGLE_SIZE); + assert_eq!(emoji_string.chars().count(), TARI_ADDRESS_INTERNAL_SINGLE_SIZE); // Generate an emoji ID from the emoji string and ensure we recover it let emoji_id_from_emoji_string = SingleAddress::from_emoji_string(&emoji_string).unwrap(); diff --git a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs index ee1ccf9d2f..ed4e8fae20 100644 --- a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs +++ b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs @@ -861,6 +861,7 @@ impl UnconfirmedPool { #[cfg(test)] mod test { use tari_common::configuration::Network; + use tari_common_types::tari_address::TariAddress; use tari_script::{ExecutionStack, TariScript}; use super::*; @@ -993,6 +994,7 @@ mod test { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ); let test_params = TestParams::new(&key_manager).await; diff --git a/base_layer/core/src/transactions/test_helpers.rs b/base_layer/core/src/transactions/test_helpers.rs index e0ab41f49d..8f318cda0d 100644 --- a/base_layer/core/src/transactions/test_helpers.rs +++ b/base_layer/core/src/transactions/test_helpers.rs @@ -27,6 +27,7 @@ use tari_common::configuration::Network; use tari_common_sqlite::{error::SqliteStorageError, sqlite_connection_pool::PooledDbConnection}; use tari_common_types::{ key_branches::TransactionKeyManagerBranch, + tari_address::TariAddress, types::{Commitment, PrivateKey, PublicKey, Signature}, }; use tari_crypto::keys::{PublicKey as PK, SecretKey}; @@ -650,6 +651,7 @@ pub async fn create_transaction_with( change.script_key_id, change.commitment_mask_key_id, Covenant::default(), + TariAddress::default(), ); for input in inputs { stx_builder.with_input(input).await.unwrap(); @@ -720,6 +722,7 @@ pub async fn create_stx_protocol_internal( change.script_key_id, change.commitment_mask_key_id, Covenant::default(), + TariAddress::default(), ); for tx_input in &schema.from { 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 6b8b3a762a..5ad5bef4c4 100644 --- a/base_layer/core/src/transactions/transaction_components/encrypted_data.rs +++ b/base_layer/core/src/transactions/transaction_components/encrypted_data.rs @@ -45,7 +45,7 @@ use digest::{consts::U32, generic_array::GenericArray, FixedOutput}; use primitive_types::U256; use serde::{Deserialize, Serialize}; use tari_common_types::{ - tari_address::dual_address::DualAddress, + tari_address::{TariAddress, TARI_ADDRESS_INTERNAL_DUAL_SIZE, TARI_ADDRESS_INTERNAL_SINGLE_SIZE}, types::{Commitment, PrivateKey}, MaxSizeBytes, }; @@ -84,8 +84,9 @@ pub enum PaymentId { Empty, U64(u64), U256(U256), - Address(DualAddress), + Address(TariAddress), Open(Vec), + AddressAndData(TariAddress, Vec), } impl PaymentId { @@ -94,8 +95,17 @@ impl PaymentId { PaymentId::Empty => 0, PaymentId::U64(_) => size_of::(), PaymentId::U256(_) => size_of::(), - PaymentId::Address(_) => 67, + PaymentId::Address(a) => a.get_size(), PaymentId::Open(v) => v.len(), + PaymentId::AddressAndData(a, v) => a.get_size() + v.len(), + } + } + + pub fn get_data(&self) -> Vec { + match &self { + PaymentId::Empty | PaymentId::U64(_) | PaymentId::U256(_) | PaymentId::Address(_) => Vec::new(), + PaymentId::Open(v) => v.clone(), + PaymentId::AddressAndData(_v, d) => d.clone(), } } @@ -108,8 +118,13 @@ impl PaymentId { v.to_little_endian(&mut bytes); bytes }, - PaymentId::Address(v) => v.to_bytes().to_vec(), + PaymentId::Address(v) => v.to_vec(), PaymentId::Open(v) => v.clone(), + PaymentId::AddressAndData(v, d) => { + let mut bytes = v.to_vec(); + bytes.extend_from_slice(d); + bytes + }, } } @@ -125,12 +140,42 @@ impl PaymentId { let v = U256::from_little_endian(bytes); Ok(PaymentId::U256(v)) }, - 67 => { + TARI_ADDRESS_INTERNAL_DUAL_SIZE => { + let v = + TariAddress::from_bytes(bytes).map_err(|e| EncryptedDataError::ByteArrayError(e.to_string()))?; + Ok(PaymentId::Address(v)) + }, + TARI_ADDRESS_INTERNAL_SINGLE_SIZE => { let v = - DualAddress::from_bytes(bytes).map_err(|e| EncryptedDataError::ByteArrayError(e.to_string()))?; + TariAddress::from_bytes(bytes).map_err(|e| EncryptedDataError::ByteArrayError(e.to_string()))?; Ok(PaymentId::Address(v)) }, - _ => Ok(PaymentId::Open(bytes.to_vec())), + len if len < TARI_ADDRESS_INTERNAL_SINGLE_SIZE => Ok(PaymentId::Open(bytes.to_vec())), + len if len < TARI_ADDRESS_INTERNAL_DUAL_SIZE => { + if let Ok(address) = TariAddress::from_bytes(&bytes[0..TARI_ADDRESS_INTERNAL_SINGLE_SIZE]) { + Ok(PaymentId::AddressAndData( + address, + bytes[TARI_ADDRESS_INTERNAL_SINGLE_SIZE..].to_vec(), + )) + } else { + Ok(PaymentId::Open(bytes.to_vec())) + } + }, + _ => { + if let Ok(address) = TariAddress::from_bytes(&bytes[0..TARI_ADDRESS_INTERNAL_SINGLE_SIZE]) { + Ok(PaymentId::AddressAndData( + address, + bytes[TARI_ADDRESS_INTERNAL_SINGLE_SIZE..].to_vec(), + )) + } else if let Ok(address) = TariAddress::from_bytes(&bytes[0..TARI_ADDRESS_INTERNAL_DUAL_SIZE]) { + Ok(PaymentId::AddressAndData( + address, + bytes[TARI_ADDRESS_INTERNAL_DUAL_SIZE..].to_vec(), + )) + } else { + Ok(PaymentId::Open(bytes.to_vec())) + } + }, } } } @@ -143,6 +188,7 @@ impl Display for PaymentId { PaymentId::U256(v) => write!(f, "{}", v), PaymentId::Address(v) => write!(f, "{}", v.to_emoji_string()), PaymentId::Open(v) => write!(f, "byte vector of len: {}", v.len()), + PaymentId::AddressAndData(v, d) => write!(f, "From {} with data: {:?}", v.to_emoji_string(), d), } } } @@ -357,13 +403,36 @@ mod test { U256::from_dec_str("465465489789785458694894263185648978947864164681631").expect("Should not fail"), ), PaymentId::Address( - DualAddress::from_base58( + 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::AddressAndData( + TariAddress::from_base58( + "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", + ) + .unwrap(), + vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ), + PaymentId::AddressAndData( + TariAddress::from_base58( + "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", + ) + .unwrap(), + vec![1; 189], + ), + PaymentId::AddressAndData( + TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), + vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ), + PaymentId::AddressAndData( + TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), + vec![1; 189], + ), ] { for (value, mask) in [ (0, PrivateKey::default()), @@ -404,13 +473,36 @@ mod test { U256::from_dec_str("465465489789785458694894263185648978947864164681631").expect("Should not fail"), ), PaymentId::Address( - DualAddress::from_base58( + 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::AddressAndData( + TariAddress::from_base58( + "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", + ) + .unwrap(), + vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ), + PaymentId::AddressAndData( + TariAddress::from_base58( + "f425UWsDp714RiN53c1G6ek57rfFnotB5NCMyrn4iDgbR8i2sXVHa4xSsedd66o9KmkRgErQnyDdCaAdNLzcKrj7eUb", + ) + .unwrap(), + vec![1; 189], + ), + PaymentId::AddressAndData( + TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), + vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ), + PaymentId::AddressAndData( + TariAddress::from_base58("f3S7XTiyKQauZpDUjdR8NbcQ33MYJigiWiS44ccZCxwAAjk").unwrap(), + vec![1; 189], + ), ] { for (value, mask) in [ (0, PrivateKey::default()), diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto index 8925decd13..3679e27711 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto @@ -38,6 +38,8 @@ message SingleRoundSenderData { uint32 output_version = 13; // The version of this transaction kernel uint32 kernel_version = 14; + // the sender address + string sender_address = 15; } message TransactionSenderMessage { diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs index 2f7c4f86bb..1217f45e18 100644 --- a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs @@ -20,11 +20,14 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::convert::{TryFrom, TryInto}; +use std::{ + convert::{TryFrom, TryInto}, + str::FromStr, +}; use borsh::{BorshDeserialize, BorshSerialize}; use proto::transaction_sender_message::Message as ProtoTxnSenderMessage; -use tari_common_types::types::PublicKey; +use tari_common_types::{tari_address::TariAddress, types::PublicKey}; use tari_script::TariScript; use tari_utilities::ByteArray; @@ -116,6 +119,7 @@ impl TryFrom for SingleRoundSenderData { .map_err(|_| "Transaction kernel version overflow")? .try_into() .map_err(|_| "Invalid transaction kernel version")?; + let sender_address = TariAddress::from_str(&data.sender_address).map_err(|err| err.to_string())?; Ok(Self { tx_id: data.tx_id.into(), @@ -132,6 +136,7 @@ impl TryFrom for SingleRoundSenderData { minimum_value_promise: data.minimum_value_promise.into(), output_version, kernel_version, + sender_address, }) } } @@ -159,6 +164,7 @@ impl TryFrom for proto::SingleRoundSenderData { minimum_value_promise: sender_data.minimum_value_promise.into(), output_version: sender_data.output_version.as_u8().into(), kernel_version: sender_data.kernel_version.as_u8().into(), + sender_address: sender_data.sender_address.to_base58(), }) } } diff --git a/base_layer/core/src/transactions/transaction_protocol/recipient.rs b/base_layer/core/src/transactions/transaction_protocol/recipient.rs index af3cac201b..d5c4ea1d4d 100644 --- a/base_layer/core/src/transactions/transaction_protocol/recipient.rs +++ b/base_layer/core/src/transactions/transaction_protocol/recipient.rs @@ -165,7 +165,7 @@ impl ReceiverTransactionProtocol { #[cfg(test)] mod test { - use tari_common_types::types::PublicKey; + use tari_common_types::{tari_address::TariAddress, types::PublicKey}; use tari_crypto::keys::PublicKey as PublicKeyTrait; use tari_key_manager::key_manager_service::KeyManagerInterface; use tari_script::TariScript; @@ -211,6 +211,7 @@ mod test { minimum_value_promise: MicroMinotari::zero(), output_version: TransactionOutputVersion::get_current_version(), kernel_version: TransactionKernelVersion::get_current_version(), + sender_address: TariAddress::default(), }; let sender_info = TransactionSenderMessage::Single(Box::new(msg.clone())); let params = UtxoTestParams { diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index bca80c2d01..dc34b9b93b 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -24,6 +24,7 @@ use std::fmt; use serde::{Deserialize, Serialize}; use tari_common_types::{ + tari_address::TariAddress, transaction::TxId, types::{ComAndPubSignature, PrivateKey, PublicKey, Signature}, }; @@ -102,6 +103,8 @@ pub(super) struct RawTransactionInfo { pub metadata: TransactionMetadata, /// A user message sent to the receiver pub text_message: String, + /// The senders address + pub sender_address: TariAddress, } impl RawTransactionInfo { @@ -148,6 +151,8 @@ pub struct SingleRoundSenderData { pub output_version: TransactionOutputVersion, /// The version of this transaction kernel pub kernel_version: TransactionKernelVersion, + /// The senders address + pub sender_address: TariAddress, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -448,6 +453,7 @@ impl SenderTransactionProtocol { minimum_value_promise: recipient_minimum_value_promise, output_version, kernel_version, + sender_address: info.sender_address.clone(), }) }, _ => Err(TPE::InvalidStateError), @@ -870,7 +876,7 @@ impl fmt::Display for SenderState { #[cfg(test)] mod test { - use tari_common_types::{key_branches::TransactionKeyManagerBranch, types::PrivateKey}; + use tari_common_types::{key_branches::TransactionKeyManagerBranch, tari_address::TariAddress, types::PrivateKey}; use tari_crypto::signatures::CommitmentAndPublicKeySignature; use tari_key_manager::key_manager_service::KeyManagerInterface; use tari_script::{inputs, script, ExecutionStack, TariScript}; @@ -1061,6 +1067,7 @@ mod test { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_input(input) .await @@ -1138,6 +1145,7 @@ mod test { a_change_key.script_key_id, a_change_key.commitment_mask_key_id, Covenant::default(), + TariAddress::default(), ); let mut alice = builder.build().await.unwrap(); assert!(alice.is_single_round_message_ready()); @@ -1210,6 +1218,7 @@ mod test { } #[tokio::test] + #[allow(clippy::too_many_lines)] async fn single_recipient_with_change() { let rules = create_consensus_rules(); let key_manager = create_memory_db_key_manager().unwrap(); @@ -1241,6 +1250,7 @@ mod test { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_input(input) .await @@ -1346,6 +1356,7 @@ mod test { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_input(input) .await @@ -1451,6 +1462,7 @@ mod test { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_input(input) .await @@ -1489,6 +1501,7 @@ mod test { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_input(input) .await @@ -1534,6 +1547,7 @@ mod test { change_params.script_key_id.clone(), change_params.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_input(input) .await diff --git a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index d8791eff52..c5c5c4573b 100644 --- a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -143,7 +143,7 @@ impl SingleReceiverTransactionProtocol { #[cfg(test)] mod test { - use tari_common_types::types::PublicKey; + use tari_common_types::{tari_address::TariAddress, types::PublicKey}; use tari_crypto::{keys::PublicKey as PublicKeyTrait, signatures::CommitmentAndPublicKeySignature}; use tari_key_manager::key_manager_service::KeyManagerInterface; use tari_script::{script, ExecutionStack}; @@ -251,6 +251,7 @@ mod test { } #[tokio::test] + #[allow(clippy::too_many_lines)] async fn valid_request() { let key_manager: crate::transactions::key_manager::TransactionKeyManagerWrapper< tari_key_manager::key_manager_service::storage::sqlite_db::KeyManagerSqliteDatabase< @@ -293,6 +294,7 @@ mod test { minimum_value_promise: MicroMinotari::zero(), output_version: TransactionOutputVersion::get_current_version(), kernel_version: TransactionKernelVersion::get_current_version(), + sender_address: TariAddress::default(), }; let bob_public_key = key_manager .get_public_key_at_key_id(&test_params.sender_offset_key_id) 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 1c669ab8fd..dae717fb5f 100644 --- a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs +++ b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs @@ -26,6 +26,7 @@ use log::*; use serde::{Deserialize, Serialize}; use tari_common_types::{ key_branches::TransactionKeyManagerBranch, + tari_address::TariAddress, transaction::TxId, types::{Commitment, PrivateKey, PublicKey, Signature}, }; @@ -66,6 +67,7 @@ pub(super) struct ChangeDetails { change_input_data: ExecutionStack, change_script_key_id: TariKeyId, change_covenant: Covenant, + own_address: TariAddress, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] @@ -102,6 +104,7 @@ pub struct SenderTransactionInitializer { burn_commitment: Option, fee: Fee, key_manager: KM, + sender_address: TariAddress, } pub struct BuildError { @@ -132,6 +135,7 @@ where KM: TransactionKeyManagerInterface kernel_features: KernelFeatures::empty(), burn_commitment: None, tx_id: None, + sender_address: TariAddress::default(), key_manager, } } @@ -143,6 +147,12 @@ where KM: TransactionKeyManagerInterface self } + /// Set the sender's address + pub fn with_sender_address(&mut self, sender_address: TariAddress) -> &mut Self { + self.sender_address = sender_address; + self + } + /// Set the spending script of the ith recipient's output, a script offset will be generated for this recipient at /// the same time. This method will silently fail if `receiver_index` >= num_receivers. pub async fn with_recipient_data( @@ -223,6 +233,7 @@ where KM: TransactionKeyManagerInterface change_script_key_id: TariKeyId, change_commitment_mask_key_id: TariKeyId, change_covenant: Covenant, + own_address: TariAddress, ) -> &mut Self { let details = ChangeDetails { change_commitment_mask_key_id, @@ -230,6 +241,7 @@ where KM: TransactionKeyManagerInterface change_input_data, change_script_key_id, change_covenant, + own_address, }; self.change = Some(details); self @@ -383,10 +395,18 @@ where KM: TransactionKeyManagerInterface .ok_or("Change covenant was not provided")? .change_covenant .clone(); + let address = self + .change + .as_ref() + .ok_or("address was not provided")? + .own_address + .clone(); + + let payment_id = PaymentId::Address(address); let encrypted_data = self .key_manager - .encrypt_data_for_recovery(&change_key_id, None, v.as_u64(), PaymentId::Empty) + .encrypt_data_for_recovery(&change_key_id, None, v.as_u64(), payment_id.clone()) .await .map_err(|e| e.to_string())?; @@ -430,7 +450,7 @@ where KM: TransactionKeyManagerInterface covenant, encrypted_data, minimum_value_promise, - PaymentId::Empty, + payment_id, &self.key_manager, ) .await @@ -574,6 +594,7 @@ where KM: TransactionKeyManagerInterface inputs: self.inputs, outputs: self.sender_custom_outputs, text_message: self.recipient_text_message.unwrap_or_default(), + sender_address: self.sender_address.clone(), }; let state = SenderState::Initializing(Box::new(sender_info)); @@ -588,6 +609,7 @@ where KM: TransactionKeyManagerInterface #[cfg(test)] mod test { + use tari_common_types::tari_address::TariAddress; use tari_script::{inputs, script}; use crate::{ @@ -664,6 +686,7 @@ mod test { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ); let result = builder.build().await.unwrap(); // Peek inside and check the results @@ -849,6 +872,7 @@ mod test { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_fee_per_gram(MicroMinotari(1)) .with_recipient_data( @@ -898,6 +922,7 @@ mod test { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_fee_per_gram(MicroMinotari(1)) .with_recipient_data( @@ -964,6 +989,7 @@ mod test { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_fee_per_gram(fee_per_gram) .with_recipient_data( diff --git a/base_layer/core/tests/tests/block_validation.rs b/base_layer/core/tests/tests/block_validation.rs index 1cdec8cfde..a84db528a1 100644 --- a/base_layer/core/tests/tests/block_validation.rs +++ b/base_layer/core/tests/tests/block_validation.rs @@ -342,7 +342,7 @@ async fn test_orphan_validator() { let key_manager = create_memory_db_key_manager().unwrap(); let network = Network::Igor; let consensus_constants = ConsensusConstantsBuilder::new(network) - .with_max_block_transaction_weight(325) + .with_max_block_transaction_weight(334) .build(); let (genesis, outputs) = create_genesis_block_with_utxos(&[T, T, T], &consensus_constants, &key_manager).await; let network = Network::LocalNet; @@ -938,7 +938,7 @@ async fn test_block_sync_body_validator() { matches!( err, ValidationError::BlockTooLarge { actual_weight, max_weight } if - actual_weight == 455 && max_weight == 400 + actual_weight == 467 && max_weight == 400 ), "{}", err diff --git a/base_layer/core/tests/tests/node_comms_interface.rs b/base_layer/core/tests/tests/node_comms_interface.rs index 971901622c..be07a36f0e 100644 --- a/base_layer/core/tests/tests/node_comms_interface.rs +++ b/base_layer/core/tests/tests/node_comms_interface.rs @@ -23,7 +23,7 @@ use std::sync::{Arc, RwLock}; use tari_common::configuration::Network; -use tari_common_types::key_branches::TransactionKeyManagerBranch; +use tari_common_types::{key_branches::TransactionKeyManagerBranch, tari_address::TariAddress}; use tari_comms::test_utils::mocks::create_connectivity_mock; use tari_core::{ base_node::comms_interface::{ @@ -286,6 +286,7 @@ async fn initialize_sender_transaction_protocol_for_overflow_test( change.script_key_id, change.commitment_mask_key_id, Covenant::default(), + TariAddress::default(), ); for tx_input in &txn_schema.from { diff --git a/base_layer/wallet/src/output_manager_service/mod.rs b/base_layer/wallet/src/output_manager_service/mod.rs index c748236356..f4226350a4 100644 --- a/base_layer/wallet/src/output_manager_service/mod.rs +++ b/base_layer/wallet/src/output_manager_service/mod.rs @@ -119,6 +119,7 @@ where let factories = self.factories.clone(); let config = self.config.clone(); let constants = self.network.create_consensus_constants().pop().unwrap(); + let network = self.network.as_network(); context.spawn_when_ready(move |handles| async move { let base_node_service_handle = handles.expect_handle::(); let connectivity = handles.expect_handle::(); @@ -133,6 +134,7 @@ where constants, handles.get_shutdown_signal(), base_node_service_handle, + network, connectivity, key_manager, ) diff --git a/base_layer/wallet/src/output_manager_service/resources.rs b/base_layer/wallet/src/output_manager_service/resources.rs index 8f55251e9e..bbac2b117a 100644 --- a/base_layer/wallet/src/output_manager_service/resources.rs +++ b/base_layer/wallet/src/output_manager_service/resources.rs @@ -20,6 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use tari_common_types::tari_address::TariAddress; use tari_core::{consensus::ConsensusConstants, transactions::CryptoFactories}; use tari_shutdown::ShutdownSignal; @@ -40,4 +41,6 @@ pub(crate) struct OutputManagerResources Result { + let view_key = key_manager.get_view_key().await?; + let spend_key = key_manager.get_spend_key().await?; + let comms_key = key_manager.get_comms_key().await?; + let interactive_features = if spend_key == comms_key { + TariAddressFeatures::create_interactive_and_one_sided() + } else { + TariAddressFeatures::create_one_sided_only() + }; + let one_sided_tari_address = TariAddress::new_dual_address( + view_key.pub_key.clone(), + comms_key.pub_key, + network, + TariAddressFeatures::create_one_sided_only(), + ); + let interactive_tari_address = + TariAddress::new_dual_address(view_key.pub_key, spend_key.pub_key, network, interactive_features); let resources = OutputManagerResources { config, db, @@ -156,6 +174,8 @@ where key_manager, consensus_constants, shutdown_signal, + one_sided_tari_address, + interactive_tari_address, }; Ok(Self { @@ -727,6 +747,7 @@ where } /// Request a receiver transaction be generated from the supplied Sender Message + #[allow(clippy::too_many_lines)] async fn get_default_recipient_transaction( &mut self, sender_message: TransactionSenderMessage, @@ -735,22 +756,18 @@ where Some(data) => data, _ => return Err(OutputManagerError::InvalidSenderMessage), }; - // Confirm covenant is default if single_round_sender_data.covenant != Covenant::default() { return Err(OutputManagerError::InvalidCovenant); } - // Confirm output features is default if single_round_sender_data.features != OutputFeatures::default() { return Err(OutputManagerError::InvalidOutputFeatures); } - // Confirm lock height is 0 if single_round_sender_data.metadata.lock_height != 0 { return Err(OutputManagerError::InvalidLockHeight); } - // Confirm kernel features if single_round_sender_data.metadata.kernel_features != KernelFeatures::default() { return Err(OutputManagerError::InvalidKernelFeatures); @@ -771,7 +788,7 @@ where } else { return Err(OutputManagerError::InvalidScriptHash); }; - + let payment_id = PaymentId::Address(single_round_sender_data.sender_address.clone()); let encrypted_data = self .resources .key_manager @@ -779,7 +796,7 @@ where &spending_key.key_id, None, single_round_sender_data.amount.as_u64(), - PaymentId::Empty, + payment_id.clone(), ) .await .unwrap(); @@ -821,7 +838,7 @@ where single_round_sender_data.covenant.clone(), encrypted_data, minimum_value_promise, - PaymentId::Empty, + payment_id, &self.resources.key_manager, ) .await?; @@ -992,6 +1009,7 @@ where amount, ) .await? + .with_sender_address(self.resources.interactive_tari_address.clone()) .with_message(message) .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_lock_height(tx_meta.lock_height) @@ -1019,6 +1037,7 @@ where change_script_key.key_id, change_commitment_mask_key.key_id, Covenant::default(), + self.resources.interactive_tari_address.clone(), ); let stp = builder @@ -1126,6 +1145,7 @@ where change_script_key.key_id, change_commitment_mask_key.key_id, Covenant::default(), + self.resources.interactive_tari_address.clone(), ); } @@ -1386,6 +1406,7 @@ where TariKeyId::default(), TariKeyId::default(), Covenant::default(), + self.resources.interactive_tari_address.clone(), ); let mut stp = builder .build() @@ -1668,6 +1689,7 @@ where .with_prevent_fee_gt_amount(self.resources.config.prevent_fee_gt_amount) .with_input(input.clone()) .await? + .with_sender_address(self.resources.one_sided_tari_address.clone()) .with_recipient_data( push_pubkey_script(recipient_address.public_spend_key()), output_features, @@ -1682,6 +1704,7 @@ where TariKeyId::default(), TariKeyId::default(), Covenant::default(), + self.resources.one_sided_tari_address.clone(), ); let mut stp = builder .build() @@ -1745,6 +1768,11 @@ where ); // Create the output with a partially signed metadata signature + let payment_id = match payment_id { + PaymentId::Open(v) => PaymentId::AddressAndData(self.resources.interactive_tari_address.clone(), v), + PaymentId::Empty => PaymentId::Address(self.resources.one_sided_tari_address.clone()), + _ => payment_id, + }; let output = WalletOutputBuilder::new(amount, spending_key_id) .with_features( sender_message @@ -1758,7 +1786,7 @@ where .encrypt_data_for_recovery( &self.resources.key_manager, Some(&encryption_key_id), - payment_id.clone(), + payment_id, ) .await? .with_input_data(ExecutionStack::default()) // Just a placeholder in the wallet @@ -1872,6 +1900,7 @@ where change_script_public_key.key_id.clone(), change_commitment_mask_key_id.key_id, Covenant::default(), + self.resources.interactive_tari_address.clone(), ); let mut stp = builder @@ -2506,6 +2535,7 @@ where change_script.key_id, change_mask.key_id, Covenant::default(), + self.resources.interactive_tari_address.clone(), ); } @@ -2583,11 +2613,11 @@ 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 encrypted_data = self .resources .key_manager - .encrypt_data_for_recovery(&commitment_mask_key.key_id, None, amount.as_u64(), PaymentId::Empty) + .encrypt_data_for_recovery(&commitment_mask_key.key_id, None, amount.as_u64(), payment_id.clone()) .await?; let minimum_value_promise = MicroMinotari::zero(); let metadata_message = TransactionOutput::metadata_signature_message_from_parts( @@ -2630,7 +2660,7 @@ where covenant, encrypted_data, minimum_value_promise, - PaymentId::Empty, + payment_id, &self.resources.key_manager, ) .await?, @@ -2845,6 +2875,7 @@ where change_script_key.key_id, change_commitment_mask_key.key_id, Covenant::default(), + self.resources.interactive_tari_address.clone(), ); let mut stp = builder @@ -2929,6 +2960,7 @@ where change_script_key.key_id, change_commitment_mask_key.key_id, Covenant::default(), + self.resources.interactive_tari_address.clone(), ); let mut stp = builder diff --git a/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs b/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs index 09fc1b4cbd..37b03fa6fb 100644 --- a/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs +++ b/base_layer/wallet/src/transaction_service/protocols/transaction_receive_protocol.rs @@ -54,7 +54,6 @@ use crate::{ utc::utc_duration_since, }, }; - const LOG_TARGET: &str = "wallet::transaction_service::protocols::receive_protocol"; #[derive(Debug, PartialEq)] @@ -315,7 +314,7 @@ where tokio::select! { Some((address, tx_id, tx)) = receiver.recv() => { incoming_finalized_transaction = Some(tx); - if inbound_tx.source_address.public_spend_key() != address.public_spend_key() { + if inbound_tx.source_address.comms_public_key() != address.public_spend_key() { warn!( target: LOG_TARGET, "Finalized Transaction did not come from the expected Public Key" diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 55a4f14a10..3813882b0e 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -175,7 +175,8 @@ pub struct TransactionService< pending_transaction_reply_senders: HashMap>, base_node_response_senders: HashMap)>, send_transaction_cancellation_senders: HashMap>, - finalized_transaction_senders: HashMap>, + #[allow(clippy::type_complexity)] + finalized_transaction_senders: HashMap)>, receiver_transaction_cancellation_senders: HashMap>, active_transaction_broadcast_protocols: HashSet, timeout_update_watch: Watch, @@ -1533,7 +1534,7 @@ where .encrypt_data_for_recovery( &self.resources.transaction_key_manager_service, Some(&encryption_key), - PaymentId::Empty, + PaymentId::Address(self.resources.interactive_tari_address.clone()), ) .await? .with_input_data(ExecutionStack::default()) @@ -1647,6 +1648,11 @@ where payment_id: PaymentId, ) -> Result { let tx_id = TxId::new_random(); + let payment_id = match payment_id { + PaymentId::Open(v) => PaymentId::AddressAndData(self.resources.interactive_tari_address.clone(), v), + PaymentId::Empty => PaymentId::Address(self.resources.interactive_tari_address.clone()), + _ => payment_id, + }; self.verify_send(&dest_address, TariAddressFeatures::create_one_sided_only())?; // For a stealth transaction, the script is not provided because the public key that should be included @@ -2005,7 +2011,7 @@ where .encrypt_data_for_recovery( &self.resources.transaction_key_manager_service, Some(&recovery_key_id), - PaymentId::Empty, + PaymentId::Address(self.resources.interactive_tari_address.clone()), ) .await? .with_input_data(Default::default()) @@ -2239,6 +2245,7 @@ where e ))); } + let recipient_reply: RecipientSignedMessage = recipient_reply .unwrap() .try_into() @@ -2249,7 +2256,6 @@ where // First we check if this Reply is for a cancelled Pending Outbound Tx or a Completed Tx let cancelled_outbound_tx = self.db.get_cancelled_pending_outbound_transaction(tx_id); let completed_tx = self.db.get_completed_transaction_cancelled_or_not(tx_id); - // This closure will check if the timestamps are beyond the cooldown period let check_cooldown = |timestamp: Option| { if let Some(t) = timestamp { @@ -2352,7 +2358,6 @@ where None => return Err(TransactionServiceError::TransactionDoesNotExistError), Some(s) => s, }; - sender .send((source_pubkey, recipient_reply)) .await @@ -2674,19 +2679,24 @@ where return Err(TransactionServiceError::RepeatedMessageError); } + let source_address = if data.sender_address.comms_public_key() == &source_pubkey { + data.sender_address.clone() + } else { + TariAddress::new_single_address( + source_pubkey, + self.resources.interactive_tari_address.network(), + TariAddressFeatures::INTERACTIVE, + ) + }; let (tx_finalized_sender, tx_finalized_receiver) = mpsc::channel(100); let (cancellation_sender, cancellation_receiver) = oneshot::channel(); self.finalized_transaction_senders - .insert(data.tx_id, tx_finalized_sender); + .insert(data.tx_id, (source_address.clone(), tx_finalized_sender)); self.receiver_transaction_cancellation_senders .insert(data.tx_id, cancellation_sender); // We are recieving an interactive transaction from someone on our network, so we assume its features are // interactive and its the same network - let source_address = TariAddress::new_single_address( - source_pubkey, - self.resources.interactive_tari_address.network(), - TariAddressFeatures::INTERACTIVE, - ); + let protocol = TransactionReceiveProtocol::new( data.tx_id, source_address, @@ -2740,19 +2750,12 @@ where ) })?; - // assuming since we talked to the node, that it has an interactive address, we dont know what the view key is - // but we know its interactive, so make the view key 0, and the spend key the source public key. - let source_address = TariAddress::new_single_address( - source_pubkey, - self.resources.interactive_tari_address.network(), - TariAddressFeatures::INTERACTIVE, - ); - let sender = match self.finalized_transaction_senders.get_mut(&tx_id) { + let (source, sender) = match self.finalized_transaction_senders.get_mut(&tx_id) { None => { // First check if perhaps we know about this inbound transaction but it was cancelled match self.db.get_cancelled_pending_inbound_transaction(tx_id) { Ok(t) => { - if t.source_address != source_address { + if t.source_address.comms_public_key() != &source_pubkey { debug!( target: LOG_TARGET, "Received Finalized Transaction for a cancelled pending Inbound Transaction (TxId: \ @@ -2772,7 +2775,7 @@ where .output_manager_service .reinstate_cancelled_inbound_transaction_outputs(tx_id) .await?; - self.restart_receive_transaction_protocol(tx_id, source_address.clone(), join_handles); + self.restart_receive_transaction_protocol(tx_id, t.source_address.clone(), join_handles); match self.finalized_transaction_senders.get_mut(&tx_id) { None => return Err(TransactionServiceError::TransactionDoesNotExistError), Some(s) => s, @@ -2783,9 +2786,11 @@ where }, Some(s) => s, }; - + if source_pubkey != *source.comms_public_key() { + return Err(TransactionServiceError::InvalidSourcePublicKey); + } sender - .send((source_address, tx_id, transaction)) + .send((source.clone(), tx_id, transaction)) .await .map_err(|_| TransactionServiceError::ProtocolChannelError)?; @@ -2882,7 +2887,8 @@ where ); let (tx_finalized_sender, tx_finalized_receiver) = mpsc::channel(100); let (cancellation_sender, cancellation_receiver) = oneshot::channel(); - self.finalized_transaction_senders.insert(tx_id, tx_finalized_sender); + self.finalized_transaction_senders + .insert(tx_id, (source_address.clone(), tx_finalized_sender)); self.receiver_transaction_cancellation_senders .insert(tx_id, cancellation_sender); let protocol = TransactionReceiveProtocol::new( 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 8b2f37ba27..4ffbcbb224 100644 --- a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs +++ b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs @@ -2318,6 +2318,7 @@ mod test { change.script_key_id, change.commitment_mask_key_id, Default::default(), + TariAddress::default(), ); let mut stp = builder.build().await.unwrap(); diff --git a/base_layer/wallet/src/transaction_service/tasks/send_transaction_reply.rs b/base_layer/wallet/src/transaction_service/tasks/send_transaction_reply.rs index 6cf5b7a34c..a14595942f 100644 --- a/base_layer/wallet/src/transaction_service/tasks/send_transaction_reply.rs +++ b/base_layer/wallet/src/transaction_service/tasks/send_transaction_reply.rs @@ -67,7 +67,7 @@ pub async fn send_transaction_reply( TransactionRoutingMechanism::StoreAndForwardOnly => { send_transaction_reply_store_and_forward( inbound_transaction.tx_id, - inbound_transaction.source_address.public_spend_key().clone(), + inbound_transaction.source_address.comms_public_key().clone(), proto_message.clone(), &mut outbound_message_service, ) 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 606cc25f82..19187c61b2 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 @@ -47,7 +47,7 @@ use tari_core::{ proto::base_node::SyncUtxosByBlockRequest, transactions::{ tari_amount::MicroMinotari, - transaction_components::{TransactionOutput, WalletOutput}, + transaction_components::{encrypted_data::PaymentId, TransactionOutput, WalletOutput}, }, }; use tari_key_manager::get_birthday_from_unix_epoch_in_seconds; @@ -638,9 +638,10 @@ where // It's a coinbase, so we know we mined it (we do mining with cold wallets). self.resources.one_sided_tari_address.clone() } else { - // Because we do not know the source public key we are making it the default key of zeroes to make it - // clear this value is a placeholder. - TariAddress::default() + match &wo.payment_id { + PaymentId::AddressAndData(address, _) | PaymentId::Address(address) => address.clone(), + _ => TariAddress::default(), + } }; match self .import_key_manager_utxo_to_transaction_service( diff --git a/base_layer/wallet/tests/output_manager_service_tests/service.rs b/base_layer/wallet/tests/output_manager_service_tests/service.rs index 7fabad69ce..42827d0369 100644 --- a/base_layer/wallet/tests/output_manager_service_tests/service.rs +++ b/base_layer/wallet/tests/output_manager_service_tests/service.rs @@ -42,8 +42,10 @@ use minotari_wallet::{ transaction_service::handle::TransactionServiceHandle, }; use rand::{rngs::OsRng, RngCore}; +use tari_common::configuration::Network; use tari_common_types::{ key_branches::TransactionKeyManagerBranch, + tari_address::TariAddress, transaction::TxId, types::{ComAndPubSignature, FixedHash, PublicKey}, }; @@ -164,6 +166,7 @@ async fn setup_output_manager_service( constants, shutdown.to_signal(), basenode_service_handle, + Network::LocalNet, wallet_connectivity_mock.clone(), key_manager.clone(), ) @@ -227,6 +230,7 @@ pub async fn setup_oms_with_bn_state( constants, shutdown.to_signal(), base_node_service_handle.clone(), + Network::LocalNet, connectivity, key_manager.clone(), ) @@ -275,6 +279,7 @@ async fn generate_sender_transaction_message( change.script_key_id, change.commitment_mask_key_id, Covenant::default(), + TariAddress::default(), ); let mut stp = builder.build().await.unwrap(); diff --git a/base_layer/wallet/tests/transaction_service_tests/service.rs b/base_layer/wallet/tests/transaction_service_tests/service.rs index b1906edab8..09fd092737 100644 --- a/base_layer/wallet/tests/transaction_service_tests/service.rs +++ b/base_layer/wallet/tests/transaction_service_tests/service.rs @@ -396,6 +396,7 @@ async fn setup_transaction_service_no_comms( constants, shutdown.to_signal(), base_node_service_handle.clone(), + Network::LocalNet, wallet_connectivity_service_mock.clone(), key_manager.clone(), ) @@ -3335,6 +3336,7 @@ async fn test_transaction_cancellation() { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_recipient_data( script!(Nop), @@ -3420,6 +3422,7 @@ async fn test_transaction_cancellation() { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_recipient_data( script!(Nop), @@ -4200,6 +4203,7 @@ async fn test_restarting_transaction_protocols() { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ); let mut bob_stp = builder.build().await.unwrap(); let msg = bob_stp.build_single_round_message(&key_manager).await.unwrap(); @@ -4621,6 +4625,7 @@ async fn test_resend_on_startup() { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_recipient_data( script!(Nop), @@ -5149,6 +5154,7 @@ async fn test_transaction_timeout_cancellation() { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ) .with_recipient_data( script!(Nop), diff --git a/base_layer/wallet/tests/transaction_service_tests/storage.rs b/base_layer/wallet/tests/transaction_service_tests/storage.rs index fb76deff7c..7369ceb3b6 100644 --- a/base_layer/wallet/tests/transaction_service_tests/storage.rs +++ b/base_layer/wallet/tests/transaction_service_tests/storage.rs @@ -112,6 +112,7 @@ pub async fn test_db_backend(backend: T) { change.script_key_id.clone(), change.commitment_mask_key_id.clone(), Covenant::default(), + TariAddress::default(), ); let stp = builder.build().await.unwrap();