Skip to content

Commit

Permalink
Merge branch 'yuji/ibc_shielded_transfer' (#2322)
Browse files Browse the repository at this point in the history
* origin/yuji/ibc_shielded_transfer:
  add SDK changelog
  check epoch
  add changelog
  fix target addr for shielded_parts
  decode MsgShieldedTransfer
  add MsgShieldedTransfer
  • Loading branch information
brentstone committed Dec 29, 2023
2 parents 8c02f9b + 57f94e9 commit 3e63dd3
Show file tree
Hide file tree
Showing 12 changed files with 278 additions and 109 deletions.
2 changes: 2 additions & 0 deletions .changelog/unreleased/SDK/2321-ibc_shielded_transfer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- ibc-transfer can set a spending key to the source
([\#2321](https://github.com/anoma/namada/issues/2321))
2 changes: 2 additions & 0 deletions .changelog/unreleased/features/2321-ibc_shielded_transfer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- IBC transfer from a spending key
([\#2321](https://github.com/anoma/namada/issues/2321))
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3897,7 +3897,7 @@ pub mod args {
let chain_ctx = ctx.borrow_mut_chain_or_exit();
TxIbcTransfer::<SdkTypes> {
tx,
source: chain_ctx.get(&self.source),
source: chain_ctx.get_cached(&self.source),
receiver: self.receiver,
token: chain_ctx.get(&self.token),
amount: self.amount,
Expand All @@ -3914,7 +3914,7 @@ pub mod args {
impl Args for TxIbcTransfer<CliTypes> {
fn parse(matches: &ArgMatches) -> Self {
let tx = Tx::parse(matches);
let source = SOURCE.parse(matches);
let source = TRANSFER_SOURCE.parse(matches);
let receiver = RECEIVER.parse(matches);
let token = TOKEN.parse(matches);
let amount = InputAmount::Unvalidated(AMOUNT.parse(matches));
Expand Down
11 changes: 9 additions & 2 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -961,8 +961,13 @@ pub async fn submit_ibc_transfer<N: Namada>(
where
<N::Client as namada::ledger::queries::Client>::Error: std::fmt::Display,
{
submit_reveal_aux(namada, args.tx.clone(), &args.source).await?;
let (mut tx, signing_data) = args.build(namada).await?;
submit_reveal_aux(
namada,
args.tx.clone(),
&args.source.effective_address(),
)
.await?;
let (mut tx, signing_data, _) = args.build(namada).await?;

if args.tx.dump_tx {
tx::dump_tx(namada.io(), &args.tx, tx);
Expand All @@ -971,6 +976,8 @@ where

namada.submit(tx, &args.tx).await?;
}
// NOTE that the tx could fail when its submission epoch doesn't match
// construction epoch

Ok(())
}
Expand Down
90 changes: 67 additions & 23 deletions core/src/ledger/ibc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::fmt::Debug;
use std::rc::Rc;
use std::str::FromStr;

use borsh::BorshDeserialize;
pub use context::common::IbcCommonContext;
use context::router::IbcRouter;
pub use context::storage::{IbcStorageContext, ProofSpec};
Expand Down Expand Up @@ -37,16 +38,16 @@ use crate::ibc::core::router::types::module::ModuleId;
use crate::ibc::primitives::proto::Any;
use crate::types::address::{Address, MASP};
use crate::types::ibc::{
get_shielded_transfer, is_ibc_denom, EVENT_TYPE_DENOM_TRACE,
EVENT_TYPE_PACKET,
get_shielded_transfer, is_ibc_denom, MsgShieldedTransfer,
EVENT_TYPE_DENOM_TRACE, EVENT_TYPE_PACKET,
};
use crate::types::masp::PaymentAddress;

#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum Error {
#[error("Decoding IBC data error: {0}")]
DecodingData(prost::DecodeError),
#[error("Decoding IBC data error")]
DecodingData,
#[error("Decoding message error: {0}")]
DecodingMessage(RouterError),
#[error("IBC context error: {0}")]
Expand Down Expand Up @@ -99,28 +100,37 @@ where

/// Execute according to the message in an IBC transaction or VP
pub fn execute(&mut self, tx_data: &[u8]) -> Result<(), Error> {
let any_msg = Any::decode(tx_data).map_err(Error::DecodingData)?;
match MsgTransfer::try_from(any_msg.clone()) {
Ok(msg) => {
let message = decode_message(tx_data)?;
match &message {
IbcMessage::Transfer(msg) => {
let mut token_transfer_ctx =
TokenTransferContext::new(self.ctx.inner.clone());
send_transfer_execute(
&mut self.ctx,
&mut token_transfer_ctx,
msg,
msg.clone(),
)
.map_err(Error::TokenTransfer)
}
Err(_) => {
let envelope = MsgEnvelope::try_from(any_msg)
.map_err(Error::DecodingMessage)?;
IbcMessage::ShieldedTransfer(msg) => {
let mut token_transfer_ctx =
TokenTransferContext::new(self.ctx.inner.clone());
send_transfer_execute(
&mut self.ctx,
&mut token_transfer_ctx,
msg.message.clone(),
)
.map_err(Error::TokenTransfer)?;
self.handle_masp_tx(message)
}
IbcMessage::Envelope(envelope) => {
execute(&mut self.ctx, &mut self.router, envelope.clone())
.map_err(|e| Error::Context(Box::new(e)))?;
// For receiving the token to a shielded address
self.handle_masp_tx(&envelope)?;
// the current ibc-rs execution doesn't store the denom for the
// token hash when transfer with MsgRecvPacket
self.store_denom(&envelope)
self.store_denom(envelope)?;
// For receiving the token to a shielded address
self.handle_masp_tx(message)
}
}
}
Expand Down Expand Up @@ -218,27 +228,35 @@ where

/// Validate according to the message in IBC VP
pub fn validate(&self, tx_data: &[u8]) -> Result<(), Error> {
let any_msg = Any::decode(tx_data).map_err(Error::DecodingData)?;
match MsgTransfer::try_from(any_msg.clone()) {
Ok(msg) => {
let message = decode_message(tx_data)?;
match message {
IbcMessage::Transfer(msg) => {
let token_transfer_ctx =
TokenTransferContext::new(self.ctx.inner.clone());
send_transfer_validate(&self.ctx, &token_transfer_ctx, msg)
.map_err(Error::TokenTransfer)
}
Err(_) => {
let envelope = MsgEnvelope::try_from(any_msg)
.map_err(Error::DecodingMessage)?;
IbcMessage::ShieldedTransfer(msg) => {
let token_transfer_ctx =
TokenTransferContext::new(self.ctx.inner.clone());
send_transfer_validate(
&self.ctx,
&token_transfer_ctx,
msg.message,
)
.map_err(Error::TokenTransfer)
}
IbcMessage::Envelope(envelope) => {
validate(&self.ctx, &self.router, envelope)
.map_err(|e| Error::Context(Box::new(e)))
}
}
}

/// Handle the MASP transaction if needed
fn handle_masp_tx(&mut self, envelope: &MsgEnvelope) -> Result<(), Error> {
let shielded_transfer = match envelope {
MsgEnvelope::Packet(PacketMsg::Recv(_)) => {
fn handle_masp_tx(&mut self, message: IbcMessage) -> Result<(), Error> {
let shielded_transfer = match message {
IbcMessage::Envelope(MsgEnvelope::Packet(PacketMsg::Recv(_))) => {
let event = self
.ctx
.inner
Expand All @@ -257,6 +275,7 @@ where
None => return Ok(()),
}
}
IbcMessage::ShieldedTransfer(msg) => Some(msg.shielded_transfer),
_ => return Ok(()),
};
if let Some(shielded_transfer) = shielded_transfer {
Expand All @@ -272,6 +291,31 @@ where
}
}

enum IbcMessage {
Envelope(MsgEnvelope),
Transfer(MsgTransfer),
ShieldedTransfer(MsgShieldedTransfer),
}

fn decode_message(tx_data: &[u8]) -> Result<IbcMessage, Error> {
// ibc-rs message
if let Ok(any_msg) = Any::decode(tx_data) {
if let Ok(transfer_msg) = MsgTransfer::try_from(any_msg.clone()) {
return Ok(IbcMessage::Transfer(transfer_msg));
}
if let Ok(envelope) = MsgEnvelope::try_from(any_msg) {
return Ok(IbcMessage::Envelope(envelope));
}
}

// Message with Transfer for the shielded transfer
if let Ok(msg) = MsgShieldedTransfer::try_from_slice(tx_data) {
return Ok(IbcMessage::ShieldedTransfer(msg));
}

Err(Error::DecodingData)
}

/// Get the IbcToken from the source/destination ports and channels
pub fn received_ibc_token(
ibc_denom: &PrefixedDenom,
Expand Down
16 changes: 12 additions & 4 deletions core/src/ledger/vp_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use super::storage_api::{self, OptionExt, ResultExt, StorageRead};
use crate::proto::Tx;
use crate::types::address::Address;
use crate::types::hash::Hash;
use crate::types::ibc::{get_shielded_transfer, IbcEvent, EVENT_TYPE_PACKET};
use crate::types::ibc::{
get_shielded_transfer, IbcEvent, MsgShieldedTransfer, EVENT_TYPE_PACKET,
};
use crate::types::storage::{
BlockHash, BlockHeight, Epoch, Header, Key, TxIndex,
};
Expand Down Expand Up @@ -112,9 +114,8 @@ where
tx_data: &Tx,
) -> Result<(Transfer, Transaction), storage_api::Error> {
let signed = tx_data;
if let Ok(transfer) =
Transfer::try_from_slice(&signed.data().unwrap()[..])
{
let data = signed.data().ok_or_err_msg("No transaction data")?;
if let Ok(transfer) = Transfer::try_from_slice(&data) {
let shielded_hash = transfer
.shielded
.ok_or_err_msg("unable to find shielded hash")?;
Expand All @@ -125,6 +126,13 @@ where
return Ok((transfer, masp_tx));
}

if let Ok(message) = MsgShieldedTransfer::try_from_slice(&data) {
return Ok((
message.shielded_transfer.transfer,
message.shielded_transfer.masp_tx,
));
}

// Shielded transfer over IBC
let events = self.get_ibc_events(EVENT_TYPE_PACKET.to_string())?;
// The receiving event should be only one in the single IBC transaction
Expand Down
41 changes: 40 additions & 1 deletion core/src/types/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ use serde::{Deserialize, Serialize};
use thiserror::Error;

use super::address::HASH_LEN;
use crate::ibc::apps::transfer::types::msgs::transfer::MsgTransfer;
use crate::ibc::apps::transfer::types::{Memo, PrefixedDenom, TracePath};
use crate::ibc::core::handler::types::events::{
Error as IbcEventError, IbcEvent as RawIbcEvent,
};
use crate::ibc::primitives::proto::Protobuf;
pub use crate::ledger::ibc::storage::is_ibc_key;
use crate::tendermint::abci::Event as AbciEvent;
use crate::types::masp::PaymentAddress;
use crate::types::token::Transfer;

/// The event type defined in ibc-rs for receiving a token
pub const EVENT_TYPE_PACKET: &str = "fungible_token_packet";
Expand Down Expand Up @@ -107,11 +110,47 @@ impl std::fmt::Display for IbcEvent {
}
}

/// IBC transfer message to send from a shielded address
#[derive(Debug, Clone)]
pub struct MsgShieldedTransfer {
/// IBC transfer message
pub message: MsgTransfer,
/// MASP tx with token transfer
pub shielded_transfer: IbcShieldedTransfer,
}

impl BorshSerialize for MsgShieldedTransfer {
fn serialize<W: std::io::Write>(
&self,
writer: &mut W,
) -> std::io::Result<()> {
let encoded_msg = self.message.clone().encode_vec();
let members = (encoded_msg, self.shielded_transfer.clone());
BorshSerialize::serialize(&members, writer)
}
}

impl BorshDeserialize for MsgShieldedTransfer {
fn deserialize_reader<R: std::io::Read>(
reader: &mut R,
) -> std::io::Result<Self> {
use std::io::{Error, ErrorKind};
let (msg, shielded_transfer): (Vec<u8>, IbcShieldedTransfer) =
BorshDeserialize::deserialize_reader(reader)?;
let message = MsgTransfer::decode_vec(&msg)
.map_err(|err| Error::new(ErrorKind::InvalidData, err))?;
Ok(Self {
message,
shielded_transfer,
})
}
}

/// IBC shielded transfer
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
pub struct IbcShieldedTransfer {
/// The IBC event type
pub transfer: crate::types::token::Transfer,
pub transfer: Transfer,
/// The attributes of the IBC event
pub masp_tx: masp_primitives::transaction::Transaction,
}
Expand Down
7 changes: 4 additions & 3 deletions sdk/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ pub struct TxIbcTransfer<C: NamadaTypes = SdkTypes> {
/// Common tx arguments
pub tx: Tx<C>,
/// Transfer source address
pub source: C::Address,
pub source: C::TransferSource,
/// Transfer target address
pub receiver: String,
/// Transferred token address
Expand Down Expand Up @@ -330,7 +330,7 @@ impl<C: NamadaTypes> TxBuilder<C> for TxIbcTransfer<C> {

impl<C: NamadaTypes> TxIbcTransfer<C> {
/// Transfer source address
pub fn source(self, source: C::Address) -> Self {
pub fn source(self, source: C::TransferSource) -> Self {
Self { source, ..self }
}

Expand Down Expand Up @@ -397,7 +397,8 @@ impl TxIbcTransfer {
pub async fn build(
&self,
context: &impl Namada,
) -> crate::error::Result<(crate::proto::Tx, SigningTxData)> {
) -> crate::error::Result<(crate::proto::Tx, SigningTxData, Option<Epoch>)>
{
tx::build_ibc_transfer(context, self).await
}
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ pub trait Namada: Sized + MaybeSync + MaybeSend {
/// Make a TxIbcTransfer builder from the given minimum set of arguments
fn new_ibc_transfer(
&self,
source: Address,
source: TransferSource,
receiver: String,
token: Address,
amount: InputAmount,
Expand Down
Loading

0 comments on commit 3e63dd3

Please sign in to comment.