Skip to content

Commit

Permalink
support NftTransfer
Browse files Browse the repository at this point in the history
  • Loading branch information
yito88 committed Jun 18, 2024
1 parent 08e5664 commit 14fe5d5
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 104 deletions.
9 changes: 9 additions & 0 deletions crates/core/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,15 @@ impl From<Amount> for IbcAmount {
}
}

impl TryFrom<IbcAmount> for Amount {
type Error = AmountParseError;

fn try_from(amount: IbcAmount) -> Result<Self, Self::Error> {
let uint = Uint(primitive_types::U256::from(amount).0);
Self::from_uint(uint, 0)
}
}

impl From<DenominatedAmount> for IbcAmount {
fn from(amount: DenominatedAmount) -> Self {
amount.amount.into()
Expand Down
162 changes: 58 additions & 104 deletions crates/namada/src/ledger/native_vp/masp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<Key>,
) -> Result<Option<Sequence>> {
// 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<PacketCommitment> =
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<String, Address>,
) -> Option<Address> {
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<Key>,
src_port_id: &PortId,
src_channel_id: &ChannelId,
ibc_trace: impl AsRef<str>,
amount: Amount,
receiver: impl AsRef<str>,
) -> Result<ChangedBalances> {
// 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 =
Expand All @@ -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
Expand Down Expand Up @@ -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) => {
Expand Down

0 comments on commit 14fe5d5

Please sign in to comment.