diff --git a/orm/migrations/2025-01-15-130138_transaction_kind_mixed_transfer/down.sql b/orm/migrations/2025-01-15-130138_transaction_kind_mixed_transfer/down.sql new file mode 100644 index 00000000..c7c9cbeb --- /dev/null +++ b/orm/migrations/2025-01-15-130138_transaction_kind_mixed_transfer/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +SELECT 1; \ No newline at end of file diff --git a/orm/migrations/2025-01-15-130138_transaction_kind_mixed_transfer/up.sql b/orm/migrations/2025-01-15-130138_transaction_kind_mixed_transfer/up.sql new file mode 100644 index 00000000..76cfe3b6 --- /dev/null +++ b/orm/migrations/2025-01-15-130138_transaction_kind_mixed_transfer/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TYPE TRANSACTION_KIND ADD VALUE 'mixed_transfer'; \ No newline at end of file diff --git a/orm/src/transactions.rs b/orm/src/transactions.rs index 1509ead1..599bab56 100644 --- a/orm/src/transactions.rs +++ b/orm/src/transactions.rs @@ -16,6 +16,7 @@ pub enum TransactionKindDb { ShieldedTransfer, ShieldingTransfer, UnshieldingTransfer, + MixedTransfer, IbcMsgTransfer, Bond, Redelegation, @@ -41,6 +42,11 @@ impl From for TransactionKindDb { Self::TransparentTransfer } TransactionKind::ShieldedTransfer(_) => Self::ShieldedTransfer, + TransactionKind::UnshieldingTransfer(_) => { + Self::UnshieldingTransfer + } + TransactionKind::ShieldingTransfer(_) => Self::ShieldingTransfer, + TransactionKind::MixedTransfer(_) => Self::MixedTransfer, TransactionKind::IbcMsgTransfer(_) => Self::IbcMsgTransfer, TransactionKind::Bond(_) => Self::Bond, TransactionKind::Redelegation(_) => Self::Redelegation, diff --git a/shared/src/block.rs b/shared/src/block.rs index 24f0b7f4..30d78376 100644 --- a/shared/src/block.rs +++ b/shared/src/block.rs @@ -5,7 +5,7 @@ use namada_ibc::apps::transfer::types::packet::PacketData; use namada_ibc::core::channel::types::msgs::{MsgRecvPacket, PacketMsg}; use namada_ibc::core::handler::types::msgs::MsgEnvelope; use namada_ibc::IbcMessage; -use namada_sdk::address::Address; +use namada_sdk::address::{Address, InternalAddress}; use namada_sdk::borsh::BorshDeserialize; use namada_sdk::token::Transfer; use subtle_encoding::hex; @@ -112,13 +112,12 @@ impl Block { pub fn from( block_response: &TendermintBlockResponse, block_results: &BlockResult, - proposer_address_namada: &Option, /* Provide the namada address - * of the proposer, if - * available */ + proposer_address_namada: &Option, checksums: Checksums, epoch: Epoch, block_height: BlockHeight, ) -> Self { + let masp_address = Address::Internal(InternalAddress::Masp); let transactions = block_response .block .data @@ -131,6 +130,7 @@ impl Block { block_height, checksums.clone(), block_results, + &masp_address, ) .map_err(|reason| { tracing::info!("Couldn't deserialize tx due to {}", reason); @@ -208,8 +208,121 @@ impl Block { vec![] } } - TransactionKind::ShieldedTransfer(_shielded_transfer) => { - vec![] + TransactionKind::MixedTransfer(transparent_transfer) => { + if let Some(data) = transparent_transfer { + let sources = data + .sources + .0 + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + let targets = data + .targets + .0 + .keys() + .map(|account| { + TransactionTarget::received( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + [sources, targets].concat() + } else { + vec![] + } + } + TransactionKind::ShieldedTransfer(transparent_transfer) => { + if let Some(data) = transparent_transfer { + let sources = data + .sources + .0 + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + let targets = data + .targets + .0 + .keys() + .map(|account| { + TransactionTarget::received( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + [sources, targets].concat() + } else { + vec![] + } + } + TransactionKind::UnshieldingTransfer(transparent_transfer) => { + if let Some(data) = transparent_transfer { + let sources = data + .sources + .0 + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + let targets = data + .targets + .0 + .keys() + .map(|account| { + TransactionTarget::received( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + [sources, targets].concat() + } else { + vec![] + } + } + TransactionKind::ShieldingTransfer(transparent_transfer) => { + if let Some(data) = transparent_transfer { + let sources = data + .sources + .0 + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + let targets = data + .targets + .0 + .keys() + .map(|account| { + TransactionTarget::received( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + [sources, targets].concat() + } else { + vec![] + } } TransactionKind::IbcMsgTransfer(ibc_message) => { if let Some(data) = ibc_message { diff --git a/shared/src/ser.rs b/shared/src/ser.rs index a9e9ee60..27ef6588 100644 --- a/shared/src/ser.rs +++ b/shared/src/ser.rs @@ -68,7 +68,7 @@ impl<'de> Deserialize<'de> for AccountsMap { } #[derive(Deserialize, Serialize, Debug, Clone)] -pub struct TransparentTransfer { +pub struct TransferData { /// Sources of this transfer pub sources: AccountsMap, /// Targets of this transfer @@ -77,13 +77,13 @@ pub struct TransparentTransfer { pub shielded_section_hash: Option, } -impl From for TransparentTransfer { +impl From for TransferData { fn from(transfer: NamadaTransfer) -> Self { let sources = AccountsMap(transfer.sources); let targets = AccountsMap(transfer.targets); let shielded_section_hash = transfer.shielded_section_hash; - TransparentTransfer { + TransferData { sources, targets, shielded_section_hash, diff --git a/shared/src/transaction.rs b/shared/src/transaction.rs index 5ab4ad34..bb5d7305 100644 --- a/shared/src/transaction.rs +++ b/shared/src/transaction.rs @@ -5,7 +5,6 @@ use namada_governance::{InitProposalData, VoteProposalData}; use namada_sdk::address::Address; use namada_sdk::borsh::BorshDeserialize; use namada_sdk::key::common::PublicKey; -use namada_sdk::masp::ShieldedTransfer; use namada_sdk::token::Transfer; use namada_sdk::uint::Uint; use namada_tx::data::pos::{ @@ -21,7 +20,7 @@ use crate::block::BlockHeight; use crate::block_result::{BlockResult, TxEventStatusCode}; use crate::checksums::Checksums; use crate::id::Id; -use crate::ser::{IbcMessage, TransparentTransfer}; +use crate::ser::{IbcMessage, TransferData}; // We wrap public key in a struct so we serialize data as object instead of // string @@ -33,10 +32,11 @@ pub struct RevealPkData { #[derive(Serialize, Debug, Clone)] #[serde(untagged)] pub enum TransactionKind { - TransparentTransfer(Option), - // TODO: remove once ShieldedTransfer can be serialized - #[serde(skip)] - ShieldedTransfer(Option), + TransparentTransfer(Option), + ShieldedTransfer(Option), + ShieldingTransfer(Option), + UnshieldingTransfer(Option), + MixedTransfer(Option), IbcMsgTransfer(Option>), Bond(Option), Redelegation(Option), @@ -60,16 +60,60 @@ impl TransactionKind { serde_json::to_string(&self).ok() } - pub fn from(tx_kind_name: &str, data: &[u8]) -> Self { + pub fn from( + tx_kind_name: &str, + data: &[u8], + masp_address: &Address, + ) -> Self { match tx_kind_name { "tx_transfer" => { - let data = if let Ok(data) = Transfer::try_from_slice(data) { - Some(TransparentTransfer::from(data)) + if let Ok(data) = Transfer::try_from_slice(data) { + let has_shielded_section = + data.shielded_section_hash.is_some(); + let all_sources_are_masp = data + .sources + .iter() + .all(|(acc, _)| acc.owner.eq(masp_address)); + let any_sources_are_masp = data + .sources + .iter() + .all(|(acc, _)| acc.owner.eq(masp_address)); + let all_targets_are_masp = data + .targets + .iter() + .all(|(acc, _)| acc.owner.eq(masp_address)); + let any_targets_are_masp = data + .targets + .iter() + .all(|(acc, _)| acc.owner.eq(masp_address)); + if all_sources_are_masp + && all_targets_are_masp + && has_shielded_section + { + TransactionKind::ShieldedTransfer(Some(data.into())) + } else if all_sources_are_masp + && !any_targets_are_masp + && has_shielded_section + { + TransactionKind::UnshieldingTransfer(Some(data.into())) + } else if !any_sources_are_masp + && all_targets_are_masp + && has_shielded_section + { + TransactionKind::ShieldingTransfer(Some(data.into())) + } else if !any_sources_are_masp + && !any_targets_are_masp + && !has_shielded_section + { + TransactionKind::TransparentTransfer(Some(data.into())) + } else { + TransactionKind::MixedTransfer(Some(data.into())) + } } else { - None - }; - TransactionKind::TransparentTransfer(data) + TransactionKind::Unknown + } } + "tx_bond" => { let data = if let Ok(data) = Bond::try_from_slice(data) { Some(data) @@ -299,6 +343,7 @@ impl Transaction { block_height: BlockHeight, checksums: Checksums, block_results: &BlockResult, + masp_address: &Address, ) -> Result<(WrapperTransaction, Vec), String> { let transaction = Tx::try_from(raw_tx_bytes).map_err(|e| e.to_string())?; @@ -379,7 +424,11 @@ impl Transaction { if let Some(tx_kind_name) = checksums.get_name_by_id(&id) { - TransactionKind::from(&tx_kind_name, &tx_data) + TransactionKind::from( + &tx_kind_name, + &tx_data, + masp_address, + ) } else { TransactionKind::Unknown } diff --git a/transactions/src/services/tx.rs b/transactions/src/services/tx.rs index 2ed2645c..7b56c4e5 100644 --- a/transactions/src/services/tx.rs +++ b/transactions/src/services/tx.rs @@ -133,12 +133,10 @@ pub fn get_gas_estimates( && inner_tx.wrapper_id.eq(&wrapper_tx.tx_id) }) .for_each(|tx| match tx.kind { - TransactionKind::TransparentTransfer(_) => { + TransactionKind::TransparentTransfer(_) + | TransactionKind::MixedTransfer(_) => { gas_estimate.increase_transparent_transfer() } - TransactionKind::ShieldedTransfer(_) => { - gas_estimate.increase_shielded_transfer() - } TransactionKind::IbcMsgTransfer(_) => { gas_estimate.increase_ibc_msg_transfer() } @@ -161,6 +159,15 @@ pub fn get_gas_estimates( TransactionKind::RevealPk(_) => { gas_estimate.increase_reveal_pk() } + TransactionKind::ShieldedTransfer(_) => { + gas_estimate.increase_shielded_transfer() + } + TransactionKind::ShieldingTransfer(_) => { + gas_estimate.increase_shielding_transfer() + } + TransactionKind::UnshieldingTransfer(_) => { + gas_estimate.increase_unshielding_transfer() + } _ => (), }); gas_estimate diff --git a/webserver/src/response/transaction.rs b/webserver/src/response/transaction.rs index 156161f4..eabff16e 100644 --- a/webserver/src/response/transaction.rs +++ b/webserver/src/response/transaction.rs @@ -18,6 +18,7 @@ pub enum TransactionKind { ShieldedTransfer, ShieldingTransfer, UnshieldingTransfer, + MixedTransfer, Bond, Redelegation, Unbond, @@ -99,6 +100,7 @@ impl From for TransactionKind { TransactionKindDb::ShieldedTransfer => Self::ShieldedTransfer, TransactionKindDb::ShieldingTransfer => Self::ShieldingTransfer, TransactionKindDb::UnshieldingTransfer => Self::UnshieldingTransfer, + TransactionKindDb::MixedTransfer => Self::MixedTransfer, TransactionKindDb::Bond => Self::Bond, TransactionKindDb::Redelegation => Self::Redelegation, TransactionKindDb::Unbond => Self::Unbond,