diff --git a/crates/core/src/token.rs b/crates/core/src/token.rs index aead0e1b37f..5c855fdb8f6 100644 --- a/crates/core/src/token.rs +++ b/crates/core/src/token.rs @@ -942,6 +942,15 @@ impl From for IbcAmount { } } +impl TryFrom for Amount { + type Error = AmountParseError; + + fn try_from(amount: IbcAmount) -> Result { + let uint = Uint(primitive_types::U256::from(amount).0); + Self::from_uint(uint, 0) + } +} + impl From for IbcAmount { fn from(amount: DenominatedAmount) -> Self { amount.amount.into() diff --git a/crates/namada/src/ledger/native_vp/masp.rs b/crates/namada/src/ledger/native_vp/masp.rs index ffc092515e9..90d533a4254 100644 --- a/crates/namada/src/ledger/native_vp/masp.rs +++ b/crates/namada/src/ledger/native_vp/masp.rs @@ -16,18 +16,16 @@ use namada_core::address::Address; use namada_core::arith::{checked, CheckedAdd, CheckedSub}; use namada_core::booleans::BoolResultUnitExt; use namada_core::collections::HashSet; -use namada_core::ibc::apps::transfer::types::is_sender_chain_source; -use namada_core::ibc::apps::transfer::types::msgs::transfer::MsgTransfer as IbcMsgTransfer; use namada_core::ibc::apps::transfer::types::packet::PacketData; use namada_core::masp::{addr_taddr, encode_asset_type, ibc_taddr, MaspEpoch}; use namada_core::storage::Key; use namada_gas::GasMetering; use namada_governance::storage::is_proposal_accepted; -use namada_ibc::apps::transfer::types::PrefixedDenom; use namada_ibc::core::channel::types::msgs::MsgRecvPacket as IbcMsgRecvPacket; -use namada_ibc::core::host::types::identifiers::Sequence; +use namada_ibc::core::host::types::identifiers::{ChannelId, PortId}; use namada_ibc::event::{IbcEvent, PacketAck}; -use namada_ibc::{IbcCommonContext, IbcMessage}; +use namada_ibc::storage::ibc_token; +use namada_ibc::IbcMessage; use namada_sdk::masp::{verify_shielded_tx, TAddrData}; use namada_state::{ConversionState, OptionExt, ResultExt, StateRead}; use namada_token::read_denom; @@ -42,18 +40,16 @@ use token::storage_key::{ use token::Amount; use crate::address::{InternalAddress, IBC, MASP}; -use crate::ibc::{MsgRecvPacket, MsgTransfer}; +use crate::ibc::MsgRecvPacket; use crate::ledger::ibc::storage; use crate::ledger::ibc::storage::{ - ibc_trace_key, ibc_trace_key_prefix, is_ibc_commitment_key, - is_ibc_trace_key, + ibc_trace_key, ibc_trace_key_prefix, is_ibc_trace_key, }; use crate::ledger::native_vp; -use crate::ledger::native_vp::ibc::context::VpValidationContext; use crate::ledger::native_vp::{Ctx, NativeVp}; use crate::sdk::ibc::core::channel::types::acknowledgement::AcknowledgementStatus; use crate::sdk::ibc::core::channel::types::commitment::{ - compute_ack_commitment, AcknowledgementCommitment, PacketCommitment, + compute_ack_commitment, AcknowledgementCommitment, }; use crate::token; use crate::token::MaspDigitPos; @@ -337,105 +333,28 @@ where Ok(token.as_ref().to_string()) } - // Find the given IBC message in the changed keys and return the associated - // sequence number - fn search_ibc_transfer( - &self, - message: &IbcMsgTransfer, - keys_changed: &BTreeSet, - ) -> Result> { - // Compute the packet commitment for this message - let packet_data_bytes = serde_json::to_vec(&message.packet_data) - .map_err(native_vp::Error::new)?; - let packet_commitment = - VpValidationContext::<'a, 'a, S, CA>::compute_packet_commitment( - &packet_data_bytes, - &message.timeout_height_on_b, - &message.timeout_timestamp_on_b, - ); - // Try to find a key change with the same port, channel, and commitment - // as this message and note its sequence number - for key in keys_changed { - let Some(path) = is_ibc_commitment_key(key) else { - continue; - }; - if path.port_id == message.port_id_on_a - && path.channel_id == message.chan_id_on_a - { - let Some(storage_commitment): Option = - self.ctx.read_bytes_post(key)?.map(Into::into) - else { - // Ignore this event if it does not exist - continue; - }; - if packet_commitment == storage_commitment { - return Ok(Some(path.sequence)); - } - } - } - Ok(None) - } - - // Try to determine which address would cause query_ibc_denom to yield the - // supplied denom - fn reverse_query_ibc_denom( - denom: &PrefixedDenom, - ibc_denoms: &BTreeMap, - ) -> Option
{ - ibc_denoms - .get(&denom.to_string()) - .cloned() - // If the reverse lookup failed, then guess the Address - // that might have yielded the IBC denom. However, - // guessing an IBC token address cannot possibly be - // correct due to the structure of query_ibc_denom - .or_else(|| { - Address::decode(denom.to_string()).ok().filter(|x| { - !matches!( - x, - Address::Internal(InternalAddress::IbcToken(_)) - ) - }) - }) - } - // Apply the given send packet to the changed balances structure fn apply_send_packet( &self, mut acc: ChangedBalances, - msg: &MsgTransfer, - keys_changed: &BTreeSet, + src_port_id: &PortId, + src_channel_id: &ChannelId, + ibc_trace: impl AsRef, + amount: Amount, + receiver: impl AsRef, ) -> Result { - // If a key change with the same port, channel, and commitment as this - // message cannot be found, then ignore this message - if self - .search_ibc_transfer(&msg.message, keys_changed)? - .is_none() - { - return Ok(acc); - }; - - // Since IBC denominations are derived from Addresses - // when sending, we have to do a reverse look-up of the - // relevant token Address - let Some(token) = Self::reverse_query_ibc_denom( - &msg.message.packet_data.token.denom, - &acc.ibc_denoms, - ) else { - return Ok(acc); + let token = if ibc_trace.as_ref().contains('/') { + Address::decode(ibc_trace.as_ref()).into_storage_result()? + } else { + ibc_token(ibc_trace.as_ref()) }; - let delta = ValueSum::from_pair( - token.clone(), - Amount::from_uint(Uint(*msg.message.packet_data.token.amount), 0) - .unwrap(), - ); + let delta = ValueSum::from_pair(token.clone(), amount); // If there is a transfer to the IBC account, then deduplicate the // balance increase since we already accounted for it above - if is_sender_chain_source( - msg.message.port_id_on_a.clone(), - msg.message.chan_id_on_a.clone(), - &msg.message.packet_data.token.denom, - ) { + if !ibc_trace + .as_ref() + .starts_with(&format!("{src_port_id}/{src_channel_id}")) + { let post_entry = acc.post.entry(addr_taddr(IBC)).or_insert(ValueSum::zero()); *post_entry = @@ -444,7 +363,7 @@ where // Required for the packet's receiver to get funds let post_entry = acc .post - .entry(ibc_taddr(msg.message.packet_data.receiver.to_string())) + .entry(ibc_taddr(receiver.as_ref().to_string())) .or_insert(ValueSum::zero()); // Enable funds to be received by the receiver in the // IBC packet @@ -538,8 +457,43 @@ where // to this event let receiver = msg.message.packet_data.receiver.to_string(); let addr = TAddrData::Ibc(receiver.clone()); - acc.decoder.insert(ibc_taddr(receiver), addr); - acc = self.apply_send_packet(acc, msg, keys_changed)?; + acc.decoder.insert(ibc_taddr(receiver.clone()), addr); + let ibc_trace = msg.message.packet_data.token.denom.to_string(); + let amount = msg + .message + .packet_data + .token + .amount + .try_into() + .into_storage_result()?; + acc = self.apply_send_packet( + acc, + &msg.message.port_id_on_a, + &msg.message.chan_id_on_a, + ibc_trace, + amount, + receiver, + )?; + } + IbcMessage::NftTransfer(msg) => { + let receiver = msg.message.packet_data.receiver.to_string(); + let addr = TAddrData::Ibc(receiver.clone()); + acc.decoder.insert(ibc_taddr(receiver.clone()), addr); + for token_id in &msg.message.packet_data.token_ids.0 { + let ibc_trace = format!( + "{}/{}", + msg.message.packet_data.class_id, token_id + ); + let amount = Amount::from_u64(1); + acc = self.apply_send_packet( + acc, + &msg.message.port_id_on_a, + &msg.message.chan_id_on_a, + ibc_trace, + amount, + &receiver, + )?; + } } // This event is emitted on the receiver IbcMessage::RecvPacket(msg) => {