diff --git a/orm/migrations/2025-01-16-131336_transaction_ibc_types/down.sql b/orm/migrations/2025-01-16-131336_transaction_ibc_types/down.sql new file mode 100644 index 00000000..c7c9cbeb --- /dev/null +++ b/orm/migrations/2025-01-16-131336_transaction_ibc_types/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-16-131336_transaction_ibc_types/up.sql b/orm/migrations/2025-01-16-131336_transaction_ibc_types/up.sql new file mode 100644 index 00000000..dd15f662 --- /dev/null +++ b/orm/migrations/2025-01-16-131336_transaction_ibc_types/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +ALTER TYPE TRANSACTION_KIND ADD VALUE 'ibc_transparent_transfer'; +ALTER TYPE TRANSACTION_KIND ADD VALUE 'ibc_shielding_transfer'; +ALTER TYPE TRANSACTION_KIND ADD VALUE 'ibc_unshielding_transfer'; \ No newline at end of file diff --git a/orm/src/schema.rs b/orm/src/schema.rs index 4904ddbd..d5f44870 100644 --- a/orm/src/schema.rs +++ b/orm/src/schema.rs @@ -1,91 +1,47 @@ // @generated automatically by Diesel CLI. pub mod sql_types { - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "crawler_name"))] pub struct CrawlerName; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "governance_kind"))] pub struct GovernanceKind; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "governance_result"))] pub struct GovernanceResult; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "governance_tally_type"))] pub struct GovernanceTallyType; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "history_kind"))] pub struct HistoryKind; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "ibc_status"))] pub struct IbcStatus; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "token_type"))] pub struct TokenType; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "transaction_kind"))] pub struct TransactionKind; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "transaction_result"))] pub struct TransactionResult; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "validator_state"))] pub struct ValidatorState; - #[derive( - diesel::query_builder::QueryId, - std::fmt::Debug, - diesel::sql_types::SqlType, - )] + #[derive(diesel::query_builder::QueryId, std::fmt::Debug, diesel::sql_types::SqlType)] #[diesel(postgres_type(name = "vote_kind"))] pub struct VoteKind; } diff --git a/orm/src/transactions.rs b/orm/src/transactions.rs index 599bab56..17b08595 100644 --- a/orm/src/transactions.rs +++ b/orm/src/transactions.rs @@ -18,6 +18,9 @@ pub enum TransactionKindDb { UnshieldingTransfer, MixedTransfer, IbcMsgTransfer, + IbcTransparentTransfer, + IbcShieldingTransfer, + IbcUnshieldingTransfer, Bond, Redelegation, Unbond, @@ -48,6 +51,9 @@ impl From for TransactionKindDb { TransactionKind::ShieldingTransfer(_) => Self::ShieldingTransfer, TransactionKind::MixedTransfer(_) => Self::MixedTransfer, TransactionKind::IbcMsgTransfer(_) => Self::IbcMsgTransfer, + TransactionKind::IbcTrasparentTransfer(_) => Self::IbcTransparentTransfer, + TransactionKind::IbcShieldingTransfer(_) => Self::IbcShieldingTransfer, + TransactionKind::IbcUnshieldingTransfer(_) => Self::IbcUnshieldingTransfer, TransactionKind::Bond(_) => Self::Bond, TransactionKind::Redelegation(_) => Self::Redelegation, TransactionKind::Unbond(_) => Self::Unbond, diff --git a/shared/src/block.rs b/shared/src/block.rs index 30d78376..4bacd70b 100644 --- a/shared/src/block.rs +++ b/shared/src/block.rs @@ -389,6 +389,201 @@ impl Block { vec![] } } + TransactionKind::IbcTrasparentTransfer((ibc_message, _)) => { + if let Some(data) = ibc_message { + match data.0 { + IbcMessage::Transfer(transfer) => { + let sources = transfer + .clone() + .transfer + .unwrap_or_default() + .sources + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + let targets = transfer + .transfer + .unwrap_or_default() + .targets + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + [sources, targets].concat() + } + IbcMessage::NftTransfer(transfer) => { + let sources = transfer + .clone() + .transfer + .unwrap_or_default() + .sources + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + let targets = transfer + .transfer + .unwrap_or_default() + .targets + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + [sources, targets].concat() + } + _ => vec![], + } + } else { + vec![] + } + } + TransactionKind::IbcShieldingTransfer((ibc_message, _)) => { + if let Some(data) = ibc_message { + match data.0 { + IbcMessage::Transfer(transfer) => { + let sources = transfer + .clone() + .transfer + .unwrap_or_default() + .sources + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + let targets = transfer + .transfer + .unwrap_or_default() + .targets + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + [sources, targets].concat() + } + IbcMessage::NftTransfer(transfer) => { + let sources = transfer + .clone() + .transfer + .unwrap_or_default() + .sources + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + let targets = transfer + .transfer + .unwrap_or_default() + .targets + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + [sources, targets].concat() + } + _ => vec![], + } + } else { + vec![] + } + } + TransactionKind::IbcUnshieldingTransfer((ibc_message, _)) => { + if let Some(data) = ibc_message { + match data.0 { + IbcMessage::Transfer(transfer) => { + let sources = transfer + .clone() + .transfer + .unwrap_or_default() + .sources + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + let targets = transfer + .transfer + .unwrap_or_default() + .targets + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + [sources, targets].concat() + } + IbcMessage::NftTransfer(transfer) => { + let sources = transfer + .clone() + .transfer + .unwrap_or_default() + .sources + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + let targets = transfer + .transfer + .unwrap_or_default() + .targets + .keys() + .map(|account| { + TransactionTarget::sent( + tx.tx_id.clone(), + account.owner.to_string(), + ) + }) + .collect::>(); + [sources, targets].concat() + } + _ => vec![], + } + } else { + vec![] + } + } TransactionKind::Bond(bond) => { if let Some(data) = bond { let source = diff --git a/shared/src/transaction.rs b/shared/src/transaction.rs index c0554d31..aa8c00dd 100644 --- a/shared/src/transaction.rs +++ b/shared/src/transaction.rs @@ -38,6 +38,9 @@ pub enum TransactionKind { UnshieldingTransfer(Option), MixedTransfer(Option), IbcMsgTransfer(Option>), + IbcTrasparentTransfer((Option>, TransferData)), + IbcShieldingTransfer((Option>, TransferData)), + IbcUnshieldingTransfer((Option>, TransferData)), Bond(Option), Redelegation(Option), Unbond(Option), @@ -222,15 +225,152 @@ impl TransactionKind { TransactionKind::ReactivateValidator(data) } "tx_ibc" => { - let data = if let Ok(data) = + if let Ok(ibc_data) = namada_ibc::decode_message::(data) { - Some(data) + match ibc_data.clone() { + namada_ibc::IbcMessage::Envelope(msg_envelope) => { + TransactionKind::IbcMsgTransfer( + Some(ibc_data).map(IbcMessage), + ) + } + namada_ibc::IbcMessage::Transfer(transfer) => { + if let Some(data) = transfer.transfer { + let has_shielded_section = + data.shielded_section_hash.is_some(); + + let ( + all_sources_are_masp, + any_sources_are_masp, + ) = data.sources.iter().fold( + (true, false), + |(all, any), (acc, _)| { + let is_masp = + acc.owner.eq(masp_address); + (all && is_masp, any || is_masp) + }, + ); + + let ( + all_targets_are_masp, + any_targets_are_masp, + ) = data.targets.iter().fold( + (true, false), + |(all, any), (acc, _)| { + let is_masp = + acc.owner.eq(masp_address); + (all && is_masp, any || is_masp) + }, + ); + + match ( + all_sources_are_masp, + any_sources_are_masp, + all_targets_are_masp, + any_targets_are_masp, + has_shielded_section, + ) { + (true, _, _, false, true) => { + TransactionKind::IbcUnshieldingTransfer( + ( + Some(ibc_data).map(IbcMessage), + data.into(), + ), + ) + } + (false, _, true, _, true) => { + TransactionKind::IbcShieldingTransfer(( + Some(ibc_data).map(IbcMessage), + data.into(), + )) + } + (false, _, false, _, false) => { + TransactionKind::IbcTrasparentTransfer( + ( + Some(ibc_data).map(IbcMessage), + data.into(), + ), + ) + } + _ => TransactionKind::MixedTransfer(Some( + data.into(), + )), + } + } else { + TransactionKind::IbcMsgTransfer(None) + } + } + namada_ibc::IbcMessage::NftTransfer(transfer) => { + if let Some(data) = transfer.transfer { + let has_shielded_section = + data.shielded_section_hash.is_some(); + + let ( + all_sources_are_masp, + any_sources_are_masp, + ) = data.sources.iter().fold( + (true, false), + |(all, any), (acc, _)| { + let is_masp = + acc.owner.eq(masp_address); + (all && is_masp, any || is_masp) + }, + ); + + let ( + all_targets_are_masp, + any_targets_are_masp, + ) = data.targets.iter().fold( + (true, false), + |(all, any), (acc, _)| { + let is_masp = + acc.owner.eq(masp_address); + (all && is_masp, any || is_masp) + }, + ); + + match ( + all_sources_are_masp, + any_sources_are_masp, + all_targets_are_masp, + any_targets_are_masp, + has_shielded_section, + ) { + (true, _, _, false, true) => { + TransactionKind::IbcUnshieldingTransfer( + ( + Some(ibc_data).map(IbcMessage), + data.into(), + ), + ) + } + (false, _, true, _, true) => { + TransactionKind::IbcShieldingTransfer(( + Some(ibc_data).map(IbcMessage), + data.into(), + )) + } + (false, _, false, _, false) => { + TransactionKind::IbcTrasparentTransfer( + ( + Some(ibc_data).map(IbcMessage), + data.into(), + ), + ) + } + _ => TransactionKind::MixedTransfer(Some( + data.into(), + )), + } + } else { + TransactionKind::IbcMsgTransfer(None) + } + } + } } else { tracing::warn!("Cannot deserialize IBC transfer"); - None - }; - TransactionKind::IbcMsgTransfer(data.map(IbcMessage)) + TransactionKind::IbcMsgTransfer(None) + } } "tx_unjail_validator" => { let data = if let Ok(data) = Address::try_from_slice(data) { diff --git a/webserver/src/response/transaction.rs b/webserver/src/response/transaction.rs index eabff16e..759dede4 100644 --- a/webserver/src/response/transaction.rs +++ b/webserver/src/response/transaction.rs @@ -30,6 +30,9 @@ pub enum TransactionKind { ChangeCommission, RevealPk, IbcMsgTransfer, + IbcTransparentTransfer, + IbcShieldingTransfer, + IbcUnshieldingTransfer, BecomeValidator, DeactivateValidator, ReactivateValidator, @@ -113,6 +116,9 @@ impl From for TransactionKind { TransactionKindDb::RevealPk => Self::RevealPk, TransactionKindDb::Unknown => Self::Unknown, TransactionKindDb::IbcMsgTransfer => Self::IbcMsgTransfer, + TransactionKindDb::IbcTransparentTransfer => Self::IbcTransparentTransfer, + TransactionKindDb::IbcShieldingTransfer => Self::IbcShieldingTransfer, + TransactionKindDb::IbcUnshieldingTransfer => Self::IbcUnshieldingTransfer, TransactionKindDb::BecomeValidator => Self::BecomeValidator, TransactionKindDb::ReactivateValidator => Self::ReactivateValidator, TransactionKindDb::DeactivateValidator => Self::DeactivateValidator,