From e5cef4cc83c71b8f469fa961eb4138e8b3a1f687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 22 May 2024 10:03:56 +0200 Subject: [PATCH 01/26] replace the single `Transfer` struct with 4 distinct ones --- Cargo.lock | 4 + crates/core/src/token.rs | 51 ------------ crates/token/Cargo.toml | 9 ++- crates/token/src/lib.rs | 137 ++++++++++++++++++++++++++++++++- crates/tx_prelude/src/token.rs | 5 +- 5 files changed, 150 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a9d914fe1..ae2636106b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5165,11 +5165,15 @@ dependencies = [ name = "namada_token" version = "0.37.0" dependencies = [ + "borsh 1.2.1", "namada_core", "namada_events", + "namada_macros", "namada_shielded_token", "namada_storage", "namada_trans_token", + "proptest", + "serde 1.0.193", ] [[package]] diff --git a/crates/core/src/token.rs b/crates/core/src/token.rs index 616ec87221..3f0a6d4804 100644 --- a/crates/core/src/token.rs +++ b/crates/core/src/token.rs @@ -13,10 +13,8 @@ use namada_migrations::*; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::address::Address; use crate::arith::{self, checked, CheckedAdd, CheckedSub}; use crate::dec::{Dec, POS_DECIMAL_PRECISION}; -use crate::hash::Hash; use crate::ibc::apps::transfer::types::Amount as IbcAmount; use crate::storage; use crate::storage::{DbKeySeg, KeySeg}; @@ -950,34 +948,6 @@ impl From for IbcAmount { } } -/// A simple bilateral token transfer -#[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - BorshDeserializer, - BorshSchema, - Hash, - Eq, - PartialOrd, - Serialize, - Deserialize, -)] -pub struct Transfer { - /// Source address will spend the tokens - pub source: Address, - /// Target address will receive the tokens - pub target: Address, - /// Token's address - pub token: Address, - /// The amount of tokens - pub amount: DenominatedAmount, - /// Shielded transaction part - pub shielded: Option, -} - #[allow(missing_docs)] #[derive(Error, Debug)] pub enum AmountError { @@ -994,9 +964,6 @@ pub mod testing { use proptest::prelude::*; use super::*; - use crate::address::testing::{ - arb_established_address, arb_non_internal_address, - }; impl std::ops::Add for Amount { type Output = Self; @@ -1078,24 +1045,6 @@ pub mod testing { } } - prop_compose! { - /// Generate a transfer - pub fn arb_transfer()( - source in arb_non_internal_address(), - target in arb_non_internal_address(), - token in arb_established_address().prop_map(Address::Established), - amount in arb_denominated_amount(), - ) -> Transfer { - Transfer { - source, - target, - token, - amount, - shielded: None, - } - } - } - /// Generate an arbitrary token amount pub fn arb_amount() -> impl Strategy { any::().prop_map(|val| Amount::from_uint(val, 0).unwrap()) diff --git a/crates/token/Cargo.toml b/crates/token/Cargo.toml index b06ab6086d..69c663680b 100644 --- a/crates/token/Cargo.toml +++ b/crates/token/Cargo.toml @@ -15,14 +15,21 @@ version.workspace = true [features] default = [] multicore = ["namada_shielded_token/multicore"] -testing = ["namada_core/testing"] +testing = ["namada_core/testing", "proptest"] [dependencies] namada_core = { path = "../core" } namada_events = { path = "../events", default-features = false } +namada_macros = { path = "../macros" } namada_shielded_token = { path = "../shielded_token" } namada_storage = { path = "../storage" } namada_trans_token = { path = "../trans_token" } +borsh.workspace = true +proptest = { workspace = true, optional = true } +serde.workspace = true + [dev-dependencies] namada_core = { path = "../core", features = ["testing"] } + +proptest.workspace = true \ No newline at end of file diff --git a/crates/token/src/lib.rs b/crates/token/src/lib.rs index 2ce15898a5..fb3b58f357 100644 --- a/crates/token/src/lib.rs +++ b/crates/token/src/lib.rs @@ -18,8 +18,12 @@ clippy::print_stderr )] +use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use namada_core::hash::Hash; +use namada_macros::BorshDeserializer; pub use namada_shielded_token::*; pub use namada_trans_token::*; +use serde::{Deserialize, Serialize}; /// Token storage keys pub mod storage_key { @@ -28,8 +32,6 @@ pub mod storage_key { } use namada_core::address::Address; -#[cfg(any(test, feature = "testing"))] -pub use namada_core::token::testing; use namada_events::EmitEvents; use namada_storage::{Result, StorageRead, StorageWrite}; @@ -64,3 +66,134 @@ where } Ok(()) } + +/// Arguments for a transparent token transfer +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + BorshDeserializer, + BorshSchema, + Hash, + Eq, + PartialOrd, + Serialize, + Deserialize, +)] +pub struct TransparentTransfer { + /// Source address will spend the tokens + pub source: Address, + /// Target address will receive the tokens + pub target: Address, + /// Token's address + pub token: Address, + /// The amount of tokens + pub amount: DenominatedAmount, +} + +/// Arguments for a shielded token transfer +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + BorshDeserializer, + BorshSchema, + Hash, + Eq, + PartialOrd, + Serialize, + Deserialize, +)] +pub struct ShieldedTransfer { + /// Hash of tx section that contains the MASP transaction + pub section_hash: Hash, +} + +/// Arguments for a shielding transfer (from a transparent token to a shielded +/// token) +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + BorshDeserializer, + BorshSchema, + Hash, + Eq, + PartialOrd, + Serialize, + Deserialize, +)] +pub struct ShieldingTransfer { + /// Source address will spend the tokens + pub source: Address, + /// Token's address + pub token: Address, + /// The amount of tokens + pub amount: DenominatedAmount, + /// Hash of tx section that contains the MASP transaction + pub shielded_section_hash: Hash, +} + +/// Arguments for an unshielding transfer (from a shielded token to a +/// transparent token) +#[derive( + Debug, + Clone, + PartialEq, + BorshSerialize, + BorshDeserialize, + BorshDeserializer, + BorshSchema, + Hash, + Eq, + PartialOrd, + Serialize, + Deserialize, +)] +pub struct UnshieldingTransfer { + /// Target address will receive the tokens + pub target: Address, + /// Token's address + pub token: Address, + /// The amount of tokens + pub amount: DenominatedAmount, + /// Hash of tx section that contains the MASP transaction + pub shielded_section_hash: Hash, +} + +#[cfg(any(test, feature = "testing"))] +/// Testing helpers and strategies for tokens +pub mod testing { + use namada_core::address::testing::{ + arb_established_address, arb_non_internal_address, + }; + use namada_core::address::Address; + pub use namada_core::token::*; + pub use namada_trans_token::testing::*; + use proptest::prelude::*; + + use super::TransparentTransfer; + + prop_compose! { + /// Generate a transparent transfer + pub fn arb_transparent_transfer()( + source in arb_non_internal_address(), + target in arb_non_internal_address(), + token in arb_established_address().prop_map(Address::Established), + amount in arb_denominated_amount(), + ) -> TransparentTransfer { + TransparentTransfer { + source, + target, + token, + amount, + } + } + } +} diff --git a/crates/tx_prelude/src/token.rs b/crates/tx_prelude/src/token.rs index b30954aa1c..ab9ecaf571 100644 --- a/crates/tx_prelude/src/token.rs +++ b/crates/tx_prelude/src/token.rs @@ -5,13 +5,14 @@ use namada_events::{EmitEvents, EventLevel}; #[cfg(any(test, feature = "testing"))] pub use namada_token::testing; pub use namada_token::{ - storage_key, utils, Amount, DenominatedAmount, Transfer, + storage_key, utils, Amount, DenominatedAmount, ShieldedTransfer, + ShieldingTransfer, TransparentTransfer, UnshieldingTransfer, }; use namada_tx_env::TxEnv; use crate::{Ctx, TxResult}; -/// A token transfer that can be used in a transaction. +/// A transparent token transfer that can be used in a transaction. pub fn transfer( ctx: &mut Ctx, src: &Address, From 9c1f9932b76f6ebac612fa4fdbf39bb746d0fd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 23 May 2024 10:21:48 +0200 Subject: [PATCH 02/26] move most of core IBC types into IBC crate --- crates/core/src/address.rs | 2 +- crates/core/src/ibc.rs | 445 +-------------------- crates/core/src/token.rs | 2 +- crates/ibc/src/actions.rs | 10 +- crates/ibc/src/context/client.rs | 14 +- crates/ibc/src/context/common.rs | 39 +- crates/ibc/src/context/execution.rs | 26 +- crates/ibc/src/context/mod.rs | 4 +- crates/ibc/src/context/nft_transfer.rs | 15 +- crates/ibc/src/context/nft_transfer_mod.rs | 36 +- crates/ibc/src/context/router.rs | 8 +- crates/ibc/src/context/token_transfer.rs | 19 +- crates/ibc/src/context/transfer_mod.rs | 38 +- crates/ibc/src/context/validation.rs | 34 +- crates/ibc/src/event.rs | 22 +- crates/ibc/src/lib.rs | 91 +++-- crates/ibc/src/msg.rs | 193 +++++++++ crates/ibc/src/nft.rs | 193 +++++++++ crates/ibc/src/storage.rs | 10 +- 19 files changed, 583 insertions(+), 618 deletions(-) create mode 100644 crates/ibc/src/msg.rs create mode 100644 crates/ibc/src/nft.rs diff --git a/crates/core/src/address.rs b/crates/core/src/address.rs index bdda98c56e..409622d747 100644 --- a/crates/core/src/address.rs +++ b/crates/core/src/address.rs @@ -9,6 +9,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::HEXUPPER; +use ibc::primitives::Signer; use namada_macros::BorshDeserializer; #[cfg(feature = "migrations")] use namada_migrations::*; @@ -16,7 +17,6 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use crate::ethereum_events::EthAddress; -use crate::ibc::primitives::Signer; use crate::ibc::IbcTokenHash; use crate::key::PublicKeyHash; use crate::{impl_display_and_from_str_via_format, key, string_encoding}; diff --git a/crates/core/src/ibc.rs b/crates/core/src/ibc.rs index 115ef5b017..fe29f61fe7 100644 --- a/crates/core/src/ibc.rs +++ b/crates/core/src/ibc.rs @@ -9,32 +9,8 @@ use namada_macros::BorshDeserializer; #[cfg(feature = "migrations")] use namada_migrations::*; use serde::{Deserialize, Serialize}; -use thiserror::Error; -use super::address::{Address, InternalAddress, HASH_LEN}; -use crate::ibc::apps::nft_transfer::context::{NftClassContext, NftContext}; -use crate::ibc::apps::nft_transfer::types::error::NftTransferError; -use crate::ibc::apps::nft_transfer::types::msgs::transfer::MsgTransfer as IbcMsgNftTransfer; -use crate::ibc::apps::nft_transfer::types::{ - ClassData, ClassId, ClassUri, PrefixedClassId, TokenData, TokenId, - TokenUri, TracePath as NftTracePath, -}; -use crate::ibc::apps::transfer::types::msgs::transfer::MsgTransfer as IbcMsgTransfer; -use crate::ibc::apps::transfer::types::{PrefixedDenom, TracePath}; -use crate::ibc::core::channel::types::msgs::{ - MsgAcknowledgement as IbcMsgAcknowledgement, - MsgRecvPacket as IbcMsgRecvPacket, MsgTimeout as IbcMsgTimeout, -}; -use crate::ibc::core::handler::types::msgs::MsgEnvelope; -use crate::ibc::primitives::proto::Protobuf; -use crate::token::Transfer; - -/// The event type defined in ibc-rs for receiving a token -pub const EVENT_TYPE_PACKET: &str = "fungible_token_packet"; -/// The event type defined in ibc-rs for receiving an NFT -pub const EVENT_TYPE_NFT_PACKET: &str = "non_fungible_token_packet"; -/// The escrow address for IBC transfer -pub const IBC_ESCROW_ADDRESS: Address = Address::Internal(InternalAddress::Ibc); +use super::address::HASH_LEN; /// IBC token hash derived from a denomination. #[derive( @@ -71,422 +47,3 @@ impl FromStr for IbcTokenHash { Ok(IbcTokenHash(output)) } } - -/// The different variants of an Ibc message -pub enum IbcMessage { - /// Ibc Envelop - Envelope(Box), - /// Ibc transaprent transfer - Transfer(MsgTransfer), - /// NFT transfer - NftTransfer(MsgNftTransfer), - /// Receiving a packet - RecvPacket(MsgRecvPacket), - /// Acknowledgement - AckPacket(MsgAcknowledgement), - /// Timeout - Timeout(MsgTimeout), -} - -/// IBC transfer message with `Transfer` -#[derive(Debug, Clone)] -pub struct MsgTransfer { - /// IBC transfer message - pub message: IbcMsgTransfer, - /// Shieleded transfer for MASP transaction - pub transfer: Option, -} - -impl BorshSerialize for MsgTransfer { - fn serialize( - &self, - writer: &mut W, - ) -> std::io::Result<()> { - let encoded_msg = self.message.clone().encode_vec(); - let members = (encoded_msg, self.transfer.clone()); - BorshSerialize::serialize(&members, writer) - } -} - -impl BorshDeserialize for MsgTransfer { - fn deserialize_reader( - reader: &mut R, - ) -> std::io::Result { - use std::io::{Error, ErrorKind}; - let (msg, transfer): (Vec, Option) = - BorshDeserialize::deserialize_reader(reader)?; - let message = IbcMsgTransfer::decode_vec(&msg) - .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; - Ok(Self { message, transfer }) - } -} - -/// IBC NFT transfer message with `Transfer` -#[derive(Debug, Clone)] -pub struct MsgNftTransfer { - /// IBC NFT transfer message - pub message: IbcMsgNftTransfer, - /// Shieleded transfer for MASP transaction - pub transfer: Option, -} - -impl BorshSerialize for MsgNftTransfer { - fn serialize( - &self, - writer: &mut W, - ) -> std::io::Result<()> { - let encoded_msg = self.message.clone().encode_vec(); - let members = (encoded_msg, self.transfer.clone()); - BorshSerialize::serialize(&members, writer) - } -} - -impl BorshDeserialize for MsgNftTransfer { - fn deserialize_reader( - reader: &mut R, - ) -> std::io::Result { - use std::io::{Error, ErrorKind}; - let (msg, transfer): (Vec, Option) = - BorshDeserialize::deserialize_reader(reader)?; - let message = IbcMsgNftTransfer::decode_vec(&msg) - .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; - Ok(Self { message, transfer }) - } -} - -/// IBC shielded transfer -#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, BorshDeserializer)] -pub struct IbcShieldedTransfer { - /// The IBC event type - pub transfer: Transfer, - /// The attributes of the IBC event - pub masp_tx: masp_primitives::transaction::Transaction, -} - -/// IBC receiving packet message with `Transfer` -#[derive(Debug, Clone)] -pub struct MsgRecvPacket { - /// IBC receiving packet message - pub message: IbcMsgRecvPacket, - /// Shieleded transfer for MASP transaction - pub transfer: Option, -} - -impl BorshSerialize for MsgRecvPacket { - fn serialize( - &self, - writer: &mut W, - ) -> std::io::Result<()> { - let encoded_msg = self.message.clone().encode_vec(); - let members = (encoded_msg, self.transfer.clone()); - BorshSerialize::serialize(&members, writer) - } -} - -impl BorshDeserialize for MsgRecvPacket { - fn deserialize_reader( - reader: &mut R, - ) -> std::io::Result { - use std::io::{Error, ErrorKind}; - let (msg, transfer): (Vec, Option) = - BorshDeserialize::deserialize_reader(reader)?; - let message = IbcMsgRecvPacket::decode_vec(&msg) - .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; - Ok(Self { message, transfer }) - } -} - -/// IBC acknowledgement message with `Transfer` for refunding to a shielded -/// address -#[derive(Debug, Clone)] -pub struct MsgAcknowledgement { - /// IBC acknowledgement message - pub message: IbcMsgAcknowledgement, - /// Shieleded transfer for MASP transaction - pub transfer: Option, -} - -impl BorshSerialize for MsgAcknowledgement { - fn serialize( - &self, - writer: &mut W, - ) -> std::io::Result<()> { - let encoded_msg = self.message.clone().encode_vec(); - let members = (encoded_msg, self.transfer.clone()); - BorshSerialize::serialize(&members, writer) - } -} - -impl BorshDeserialize for MsgAcknowledgement { - fn deserialize_reader( - reader: &mut R, - ) -> std::io::Result { - use std::io::{Error, ErrorKind}; - let (msg, transfer): (Vec, Option) = - BorshDeserialize::deserialize_reader(reader)?; - let message = IbcMsgAcknowledgement::decode_vec(&msg) - .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; - Ok(Self { message, transfer }) - } -} - -/// IBC timeout packet message with `Transfer` for refunding to a shielded -/// address -#[derive(Debug, Clone)] -pub struct MsgTimeout { - /// IBC timeout message - pub message: IbcMsgTimeout, - /// Shieleded transfer for MASP transaction - pub transfer: Option, -} - -impl BorshSerialize for MsgTimeout { - fn serialize( - &self, - writer: &mut W, - ) -> std::io::Result<()> { - let encoded_msg = self.message.clone().encode_vec(); - let members = (encoded_msg, self.transfer.clone()); - BorshSerialize::serialize(&members, writer) - } -} - -impl BorshDeserialize for MsgTimeout { - fn deserialize_reader( - reader: &mut R, - ) -> std::io::Result { - use std::io::{Error, ErrorKind}; - let (msg, transfer): (Vec, Option) = - BorshDeserialize::deserialize_reader(reader)?; - let message = IbcMsgTimeout::decode_vec(&msg) - .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; - Ok(Self { message, transfer }) - } -} - -#[allow(missing_docs)] -#[derive(Error, Debug)] -pub enum Error { - #[error("IBC transfer memo HEX decoding error: {0}")] - DecodingHex(data_encoding::DecodeError), - #[error("IBC transfer memo decoding error: {0}")] - DecodingShieldedTransfer(std::io::Error), -} - -/// Returns the trace path and the token string if the denom is an IBC -/// denom. -pub fn is_ibc_denom(denom: impl AsRef) -> Option<(TracePath, String)> { - let prefixed_denom = PrefixedDenom::from_str(denom.as_ref()).ok()?; - let base_denom = prefixed_denom.base_denom.to_string(); - if prefixed_denom.trace_path.is_empty() || base_denom.contains('/') { - // The denom is just a token or an NFT trace - return None; - } - // The base token isn't decoded because it could be non Namada token - Some((prefixed_denom.trace_path, base_denom)) -} - -/// Returns the trace path and the token string if the trace is an NFT one -pub fn is_nft_trace( - trace: impl AsRef, -) -> Option<(NftTracePath, String, String)> { - // The trace should be {port}/{channel}/.../{class_id}/{token_id} - if let Some((class_id, token_id)) = trace.as_ref().rsplit_once('/') { - let prefixed_class_id = PrefixedClassId::from_str(class_id).ok()?; - // The base token isn't decoded because it could be non Namada token - Some(( - prefixed_class_id.trace_path, - prefixed_class_id.base_class_id.to_string(), - token_id.to_string(), - )) - } else { - None - } -} - -/// NFT class -#[derive(Clone, Debug)] -pub struct NftClass { - /// NFT class ID - pub class_id: PrefixedClassId, - /// NFT class URI - pub class_uri: Option, - /// NFT class data - pub class_data: Option, -} - -impl BorshSerialize for NftClass { - fn serialize( - &self, - writer: &mut W, - ) -> std::io::Result<()> { - BorshSerialize::serialize(&self.class_id.to_string(), writer)?; - match &self.class_uri { - Some(uri) => { - BorshSerialize::serialize(&true, writer)?; - BorshSerialize::serialize(&uri.to_string(), writer)?; - } - None => BorshSerialize::serialize(&false, writer)?, - } - match &self.class_data { - Some(data) => { - BorshSerialize::serialize(&true, writer)?; - BorshSerialize::serialize(&data.to_string(), writer) - } - None => BorshSerialize::serialize(&false, writer), - } - } -} - -impl BorshDeserialize for NftClass { - fn deserialize_reader( - reader: &mut R, - ) -> std::io::Result { - use std::io::{Error, ErrorKind}; - let class_id: String = BorshDeserialize::deserialize_reader(reader)?; - let class_id = class_id.parse().map_err(|e: NftTransferError| { - Error::new(ErrorKind::InvalidData, e.to_string()) - })?; - - let is_uri: bool = BorshDeserialize::deserialize_reader(reader)?; - let class_uri = if is_uri { - let uri_str: String = BorshDeserialize::deserialize_reader(reader)?; - Some(uri_str.parse().map_err(|e: NftTransferError| { - Error::new(ErrorKind::InvalidData, e.to_string()) - })?) - } else { - None - }; - - let is_data: bool = BorshDeserialize::deserialize_reader(reader)?; - let class_data = if is_data { - let data_str: String = - BorshDeserialize::deserialize_reader(reader)?; - Some(data_str.parse().map_err(|e: NftTransferError| { - Error::new(ErrorKind::InvalidData, e.to_string()) - })?) - } else { - None - }; - - Ok(Self { - class_id, - class_uri, - class_data, - }) - } -} - -impl NftClassContext for NftClass { - fn get_id(&self) -> &ClassId { - &self.class_id.base_class_id - } - - fn get_uri(&self) -> Option<&ClassUri> { - self.class_uri.as_ref() - } - - fn get_data(&self) -> Option<&ClassData> { - self.class_data.as_ref() - } -} - -/// NFT metadata -#[derive(Clone, Debug)] -pub struct NftMetadata { - /// NFT class ID - pub class_id: PrefixedClassId, - /// NFT ID - pub token_id: TokenId, - /// NFT URI - pub token_uri: Option, - /// NFT data - pub token_data: Option, -} - -impl BorshSerialize for NftMetadata { - fn serialize( - &self, - writer: &mut W, - ) -> std::io::Result<()> { - BorshSerialize::serialize(&self.class_id.to_string(), writer)?; - BorshSerialize::serialize(&self.token_id.to_string(), writer)?; - match &self.token_uri { - Some(uri) => { - BorshSerialize::serialize(&true, writer)?; - BorshSerialize::serialize(&uri.to_string(), writer)?; - } - None => BorshSerialize::serialize(&false, writer)?, - } - match &self.token_data { - Some(data) => { - BorshSerialize::serialize(&true, writer)?; - BorshSerialize::serialize(&data.to_string(), writer) - } - None => BorshSerialize::serialize(&false, writer), - } - } -} - -impl BorshDeserialize for NftMetadata { - fn deserialize_reader( - reader: &mut R, - ) -> std::io::Result { - use std::io::{Error, ErrorKind}; - let class_id: String = BorshDeserialize::deserialize_reader(reader)?; - let class_id = class_id.parse().map_err(|e: NftTransferError| { - Error::new(ErrorKind::InvalidData, e.to_string()) - })?; - - let token_id: String = BorshDeserialize::deserialize_reader(reader)?; - let token_id = token_id.parse().map_err(|e: NftTransferError| { - Error::new(ErrorKind::InvalidData, e.to_string()) - })?; - - let is_uri: bool = BorshDeserialize::deserialize_reader(reader)?; - let token_uri = if is_uri { - let uri_str: String = BorshDeserialize::deserialize_reader(reader)?; - Some(uri_str.parse().map_err(|e: NftTransferError| { - Error::new(ErrorKind::InvalidData, e.to_string()) - })?) - } else { - None - }; - - let is_data: bool = BorshDeserialize::deserialize_reader(reader)?; - let token_data = if is_data { - let data_str: String = - BorshDeserialize::deserialize_reader(reader)?; - Some(data_str.parse().map_err(|e: NftTransferError| { - Error::new(ErrorKind::InvalidData, e.to_string()) - })?) - } else { - None - }; - - Ok(Self { - class_id, - token_id, - token_uri, - token_data, - }) - } -} - -impl NftContext for NftMetadata { - fn get_class_id(&self) -> &ClassId { - &self.class_id.base_class_id - } - - fn get_id(&self) -> &TokenId { - &self.token_id - } - - fn get_uri(&self) -> Option<&TokenUri> { - self.token_uri.as_ref() - } - - fn get_data(&self) -> Option<&TokenData> { - self.token_data.as_ref() - } -} diff --git a/crates/core/src/token.rs b/crates/core/src/token.rs index 3f0a6d4804..69588b9518 100644 --- a/crates/core/src/token.rs +++ b/crates/core/src/token.rs @@ -7,6 +7,7 @@ use std::str::FromStr; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::BASE32HEX_NOPAD; use ethabi::ethereum_types::U256; +use ibc::apps::transfer::types::Amount as IbcAmount; use namada_macros::BorshDeserializer; #[cfg(feature = "migrations")] use namada_migrations::*; @@ -15,7 +16,6 @@ use thiserror::Error; use crate::arith::{self, checked, CheckedAdd, CheckedSub}; use crate::dec::{Dec, POS_DECIMAL_PRECISION}; -use crate::ibc::apps::transfer::types::Amount as IbcAmount; use crate::storage; use crate::storage::{DbKeySeg, KeySeg}; use crate::uint::{self, Uint, I256}; diff --git a/crates/ibc/src/actions.rs b/crates/ibc/src/actions.rs index 24a13a35bf..61e04336ce 100644 --- a/crates/ibc/src/actions.rs +++ b/crates/ibc/src/actions.rs @@ -4,13 +4,12 @@ use std::cell::RefCell; use std::collections::BTreeSet; use std::rc::Rc; +use ibc::apps::transfer::types::msgs::transfer::MsgTransfer as IbcMsgTransfer; +use ibc::apps::transfer::types::packet::PacketData; +use ibc::apps::transfer::types::PrefixedCoin; +use ibc::core::channel::types::timeout::TimeoutHeight; use namada_core::address::Address; use namada_core::borsh::BorshSerializeExt; -use namada_core::ibc::apps::transfer::types::msgs::transfer::MsgTransfer as IbcMsgTransfer; -use namada_core::ibc::apps::transfer::types::packet::PacketData; -use namada_core::ibc::apps::transfer::types::PrefixedCoin; -use namada_core::ibc::core::channel::types::timeout::TimeoutHeight; -use namada_core::ibc::MsgTransfer; use namada_core::tendermint::Time as TmTime; use namada_core::token::Amount; use namada_events::{EmitEvents, EventTypeBuilder}; @@ -26,6 +25,7 @@ use token::DenominatedAmount; use crate::event::IbcEvent; use crate::{ storage as ibc_storage, IbcActions, IbcCommonContext, IbcStorageContext, + MsgTransfer, }; /// IBC protocol context diff --git a/crates/ibc/src/context/client.rs b/crates/ibc/src/context/client.rs index 702a1b5e4b..ab34d71832 100644 --- a/crates/ibc/src/context/client.rs +++ b/crates/ibc/src/context/client.rs @@ -1,17 +1,17 @@ //! AnyClientState and AnyConsensusState for IBC context +use ibc::clients::tendermint::client_state::ClientState as TmClientState; +use ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; +use ibc::clients::tendermint::types::{ + ClientState as TmClientStateType, ConsensusState as TmConsensusStateType, +}; +use ibc::core::client::types::error::ClientError; +use ibc::primitives::proto::Any; use ibc_derive::{IbcClientState, IbcConsensusState}; #[cfg(feature = "testing")] use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; #[cfg(feature = "testing")] use ibc_testkit::testapp::ibc::clients::mock::consensus_state::MockConsensusState; -use namada_core::ibc::clients::tendermint::client_state::ClientState as TmClientState; -use namada_core::ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; -use namada_core::ibc::clients::tendermint::types::{ - ClientState as TmClientStateType, ConsensusState as TmConsensusStateType, -}; -use namada_core::ibc::core::client::types::error::ClientError; -use namada_core::ibc::primitives::proto::Any; use prost::Message; use super::common::IbcCommonContext; diff --git a/crates/ibc/src/context/common.rs b/crates/ibc/src/context/common.rs index e536bb75ee..bc0e1d92b7 100644 --- a/crates/ibc/src/context/common.rs +++ b/crates/ibc/src/context/common.rs @@ -2,30 +2,27 @@ use core::time::Duration; -use namada_core::address::Address; -use namada_core::ibc::apps::nft_transfer::types::{PrefixedClassId, TokenId}; -use namada_core::ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; -use namada_core::ibc::clients::tendermint::types::ConsensusState as TmConsensusStateType; -use namada_core::ibc::core::channel::types::channel::ChannelEnd; -use namada_core::ibc::core::channel::types::commitment::{ +use ibc::apps::nft_transfer::types::{PrefixedClassId, TokenId}; +use ibc::clients::tendermint::consensus_state::ConsensusState as TmConsensusState; +use ibc::clients::tendermint::types::ConsensusState as TmConsensusStateType; +use ibc::core::channel::types::channel::ChannelEnd; +use ibc::core::channel::types::commitment::{ AcknowledgementCommitment, PacketCommitment, }; -use namada_core::ibc::core::channel::types::error::{ - ChannelError, PacketError, -}; -use namada_core::ibc::core::channel::types::packet::Receipt; -use namada_core::ibc::core::channel::types::timeout::TimeoutHeight; -use namada_core::ibc::core::client::types::error::ClientError; -use namada_core::ibc::core::client::types::Height; -use namada_core::ibc::core::connection::types::error::ConnectionError; -use namada_core::ibc::core::connection::types::ConnectionEnd; -use namada_core::ibc::core::handler::types::error::ContextError; -use namada_core::ibc::core::host::types::identifiers::{ +use ibc::core::channel::types::error::{ChannelError, PacketError}; +use ibc::core::channel::types::packet::Receipt; +use ibc::core::channel::types::timeout::TimeoutHeight; +use ibc::core::client::types::error::ClientError; +use ibc::core::client::types::Height; +use ibc::core::connection::types::error::ConnectionError; +use ibc::core::connection::types::ConnectionEnd; +use ibc::core::handler::types::error::ContextError; +use ibc::core::host::types::identifiers::{ ChannelId, ClientId, ConnectionId, PortId, Sequence, }; -use namada_core::ibc::primitives::proto::{Any, Protobuf}; -use namada_core::ibc::primitives::Timestamp; -use namada_core::ibc::{NftClass, NftMetadata}; +use ibc::primitives::proto::{Any, Protobuf}; +use ibc::primitives::Timestamp; +use namada_core::address::Address; use namada_core::storage::{BlockHeight, Key}; use namada_core::tendermint::Time as TmTime; use namada_core::time::DurationSecs; @@ -37,7 +34,7 @@ use sha2::Digest; use super::client::{AnyClientState, AnyConsensusState}; use super::storage::IbcStorageContext; -use crate::storage; +use crate::{storage, NftClass, NftMetadata}; /// Result of IBC common function call pub type Result = std::result::Result; diff --git a/crates/ibc/src/context/execution.rs b/crates/ibc/src/context/execution.rs index 955228edfe..c081d519f7 100644 --- a/crates/ibc/src/context/execution.rs +++ b/crates/ibc/src/context/execution.rs @@ -1,25 +1,23 @@ //! ExecutionContext implementation for IBC -use namada_core::ibc::core::channel::types::channel::ChannelEnd; -use namada_core::ibc::core::channel::types::commitment::{ +use ibc::core::channel::types::channel::ChannelEnd; +use ibc::core::channel::types::commitment::{ AcknowledgementCommitment, PacketCommitment, }; -use namada_core::ibc::core::channel::types::packet::Receipt; -use namada_core::ibc::core::client::context::ClientExecutionContext; -use namada_core::ibc::core::client::types::Height; -use namada_core::ibc::core::connection::types::ConnectionEnd; -use namada_core::ibc::core::handler::types::error::ContextError; -use namada_core::ibc::core::handler::types::events::IbcEvent; -use namada_core::ibc::core::host::types::identifiers::{ - ClientId, ConnectionId, Sequence, -}; -use namada_core::ibc::core::host::types::path::{ +use ibc::core::channel::types::packet::Receipt; +use ibc::core::client::context::ClientExecutionContext; +use ibc::core::client::types::Height; +use ibc::core::connection::types::ConnectionEnd; +use ibc::core::handler::types::error::ContextError; +use ibc::core::handler::types::events::IbcEvent; +use ibc::core::host::types::identifiers::{ClientId, ConnectionId, Sequence}; +use ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, ConnectionPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; -use namada_core::ibc::core::host::ExecutionContext; -use namada_core::ibc::primitives::Timestamp; +use ibc::core::host::ExecutionContext; +use ibc::primitives::Timestamp; use super::client::AnyClientState; use super::common::IbcCommonContext; diff --git a/crates/ibc/src/context/mod.rs b/crates/ibc/src/context/mod.rs index be3190813e..7bba1137b9 100644 --- a/crates/ibc/src/context/mod.rs +++ b/crates/ibc/src/context/mod.rs @@ -16,9 +16,9 @@ use std::fmt::Debug; use std::rc::Rc; use std::time::Duration; +use ibc::core::commitment_types::specs::ProofSpecs; +use ibc::core::host::types::identifiers::ChainId as IbcChainId; use namada_core::hash::Sha256Hasher; -use namada_core::ibc::core::commitment_types::specs::ProofSpecs; -use namada_core::ibc::core::host::types::identifiers::ChainId as IbcChainId; use namada_state::merkle_tree::ics23_specs::proof_specs; /// IBC context to handle IBC-related data diff --git a/crates/ibc/src/context/nft_transfer.rs b/crates/ibc/src/context/nft_transfer.rs index ef06b05ed7..c9e5cfa204 100644 --- a/crates/ibc/src/context/nft_transfer.rs +++ b/crates/ibc/src/context/nft_transfer.rs @@ -3,22 +3,21 @@ use std::cell::RefCell; use std::rc::Rc; -use namada_core::address::Address; -use namada_core::ibc::apps::nft_transfer::context::{ +use ibc::apps::nft_transfer::context::{ NftTransferExecutionContext, NftTransferValidationContext, }; -use namada_core::ibc::apps::nft_transfer::types::error::NftTransferError; -use namada_core::ibc::apps::nft_transfer::types::{ +use ibc::apps::nft_transfer::types::error::NftTransferError; +use ibc::apps::nft_transfer::types::{ ClassData, ClassUri, Memo, PrefixedClassId, TokenData, TokenId, TokenUri, PORT_ID_STR, }; -use namada_core::ibc::core::handler::types::error::ContextError; -use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; -use namada_core::ibc::{NftClass, NftMetadata, IBC_ESCROW_ADDRESS}; +use ibc::core::handler::types::error::ContextError; +use ibc::core::host::types::identifiers::{ChannelId, PortId}; +use namada_core::address::Address; use namada_core::token::Amount; use super::common::IbcCommonContext; -use crate::storage; +use crate::{storage, NftClass, NftMetadata, IBC_ESCROW_ADDRESS}; /// NFT transfer context to handle tokens #[derive(Debug)] diff --git a/crates/ibc/src/context/nft_transfer_mod.rs b/crates/ibc/src/context/nft_transfer_mod.rs index e8af60b523..1d94056425 100644 --- a/crates/ibc/src/context/nft_transfer_mod.rs +++ b/crates/ibc/src/context/nft_transfer_mod.rs @@ -4,8 +4,8 @@ use std::cell::RefCell; use std::fmt::Debug; use std::rc::Rc; -use namada_core::ibc::apps::nft_transfer::context::NftTransferValidationContext; -use namada_core::ibc::apps::nft_transfer::module::{ +use ibc::apps::nft_transfer::context::NftTransferValidationContext; +use ibc::apps::nft_transfer::module::{ on_acknowledgement_packet_execute, on_acknowledgement_packet_validate, on_chan_close_confirm_execute, on_chan_close_confirm_validate, on_chan_close_init_execute, on_chan_close_init_validate, @@ -16,21 +16,17 @@ use namada_core::ibc::apps::nft_transfer::module::{ on_recv_packet_execute, on_timeout_packet_execute, on_timeout_packet_validate, }; -use namada_core::ibc::apps::nft_transfer::types::error::NftTransferError; -use namada_core::ibc::apps::nft_transfer::types::MODULE_ID_STR; -use namada_core::ibc::core::channel::types::acknowledgement::Acknowledgement; -use namada_core::ibc::core::channel::types::channel::{Counterparty, Order}; -use namada_core::ibc::core::channel::types::error::{ - ChannelError, PacketError, -}; -use namada_core::ibc::core::channel::types::packet::Packet; -use namada_core::ibc::core::channel::types::Version; -use namada_core::ibc::core::host::types::identifiers::{ - ChannelId, ConnectionId, PortId, -}; -use namada_core::ibc::core::router::module::Module; -use namada_core::ibc::core::router::types::module::{ModuleExtras, ModuleId}; -use namada_core::ibc::primitives::Signer; +use ibc::apps::nft_transfer::types::error::NftTransferError; +use ibc::apps::nft_transfer::types::MODULE_ID_STR; +use ibc::core::channel::types::acknowledgement::Acknowledgement; +use ibc::core::channel::types::channel::{Counterparty, Order}; +use ibc::core::channel::types::error::{ChannelError, PacketError}; +use ibc::core::channel::types::packet::Packet; +use ibc::core::channel::types::Version; +use ibc::core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; +use ibc::core::router::module::Module; +use ibc::core::router::types::module::{ModuleExtras, ModuleId}; +use ibc::primitives::Signer; use super::common::IbcCommonContext; use super::nft_transfer::NftTransferContext; @@ -332,10 +328,8 @@ fn into_packet_error(error: NftTransferError) -> PacketError { pub mod testing { use std::str::FromStr; - use namada_core::ibc::apps::nft_transfer::types::{ - ack_success_b64, PORT_ID_STR, - }; - use namada_core::ibc::core::channel::types::acknowledgement::AcknowledgementStatus; + use ibc::apps::nft_transfer::types::{ack_success_b64, PORT_ID_STR}; + use ibc::core::channel::types::acknowledgement::AcknowledgementStatus; use super::*; diff --git a/crates/ibc/src/context/router.rs b/crates/ibc/src/context/router.rs index 0833a6282a..61fc6e24d9 100644 --- a/crates/ibc/src/context/router.rs +++ b/crates/ibc/src/context/router.rs @@ -2,11 +2,11 @@ use std::rc::Rc; +use ibc::core::host::types::identifiers::PortId; +use ibc::core::router::module::Module; +use ibc::core::router::router::Router; +use ibc::core::router::types::module::ModuleId; use namada_core::collections::HashMap; -use namada_core::ibc::core::host::types::identifiers::PortId; -use namada_core::ibc::core::router::module::Module; -use namada_core::ibc::core::router::router::Router; -use namada_core::ibc::core::router::types::module::ModuleId; use super::super::ModuleWrapper; diff --git a/crates/ibc/src/context/token_transfer.rs b/crates/ibc/src/context/token_transfer.rs index 94410c5673..2036dd9c19 100644 --- a/crates/ibc/src/context/token_transfer.rs +++ b/crates/ibc/src/context/token_transfer.rs @@ -4,23 +4,20 @@ use std::cell::RefCell; use std::collections::BTreeSet; use std::rc::Rc; -use namada_core::address::{Address, InternalAddress}; -use namada_core::ibc::apps::transfer::context::{ +use ibc::apps::transfer::context::{ TokenTransferExecutionContext, TokenTransferValidationContext, }; -use namada_core::ibc::apps::transfer::types::error::TokenTransferError; -use namada_core::ibc::apps::transfer::types::{ - Memo, PrefixedCoin, PrefixedDenom, -}; -use namada_core::ibc::core::channel::types::error::ChannelError; -use namada_core::ibc::core::handler::types::error::ContextError; -use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; -use namada_core::ibc::IBC_ESCROW_ADDRESS; +use ibc::apps::transfer::types::error::TokenTransferError; +use ibc::apps::transfer::types::{Memo, PrefixedCoin, PrefixedDenom}; +use ibc::core::channel::types::error::ChannelError; +use ibc::core::handler::types::error::ContextError; +use ibc::core::host::types::identifiers::{ChannelId, PortId}; +use namada_core::address::{Address, InternalAddress}; use namada_core::uint::Uint; use namada_token::{read_denom, Amount, Denomination}; use super::common::IbcCommonContext; -use crate::storage; +use crate::{storage, IBC_ESCROW_ADDRESS}; /// Token transfer context to handle tokens #[derive(Debug)] diff --git a/crates/ibc/src/context/transfer_mod.rs b/crates/ibc/src/context/transfer_mod.rs index 4b88220213..9a1075e280 100644 --- a/crates/ibc/src/context/transfer_mod.rs +++ b/crates/ibc/src/context/transfer_mod.rs @@ -5,9 +5,8 @@ use std::collections::BTreeSet; use std::fmt::Debug; use std::rc::Rc; -use namada_core::address::Address; -use namada_core::ibc::apps::transfer::context::TokenTransferValidationContext; -use namada_core::ibc::apps::transfer::module::{ +use ibc::apps::transfer::context::TokenTransferValidationContext; +use ibc::apps::transfer::module::{ on_acknowledgement_packet_execute, on_acknowledgement_packet_validate, on_chan_close_confirm_execute, on_chan_close_confirm_validate, on_chan_close_init_execute, on_chan_close_init_validate, @@ -18,21 +17,18 @@ use namada_core::ibc::apps::transfer::module::{ on_recv_packet_execute, on_timeout_packet_execute, on_timeout_packet_validate, }; -use namada_core::ibc::apps::transfer::types::error::TokenTransferError; -use namada_core::ibc::apps::transfer::types::MODULE_ID_STR; -use namada_core::ibc::core::channel::types::acknowledgement::Acknowledgement; -use namada_core::ibc::core::channel::types::channel::{Counterparty, Order}; -use namada_core::ibc::core::channel::types::error::{ - ChannelError, PacketError, -}; -use namada_core::ibc::core::channel::types::packet::Packet; -use namada_core::ibc::core::channel::types::Version; -use namada_core::ibc::core::host::types::identifiers::{ - ChannelId, ConnectionId, PortId, -}; -use namada_core::ibc::core::router::module::Module; -use namada_core::ibc::core::router::types::module::{ModuleExtras, ModuleId}; -use namada_core::ibc::primitives::Signer; +use ibc::apps::transfer::types::error::TokenTransferError; +use ibc::apps::transfer::types::MODULE_ID_STR; +use ibc::core::channel::types::acknowledgement::Acknowledgement; +use ibc::core::channel::types::channel::{Counterparty, Order}; +use ibc::core::channel::types::error::{ChannelError, PacketError}; +use ibc::core::channel::types::packet::Packet; +use ibc::core::channel::types::Version; +use ibc::core::host::types::identifiers::{ChannelId, ConnectionId, PortId}; +use ibc::core::router::module::Module; +use ibc::core::router::types::module::{ModuleExtras, ModuleId}; +use ibc::primitives::Signer; +use namada_core::address::Address; use super::common::IbcCommonContext; use super::token_transfer::TokenTransferContext; @@ -351,10 +347,8 @@ fn into_packet_error(error: TokenTransferError) -> PacketError { pub mod testing { use std::str::FromStr; - use namada_core::ibc::apps::transfer::types::{ - ack_success_b64, PORT_ID_STR, - }; - use namada_core::ibc::core::channel::types::acknowledgement::AcknowledgementStatus; + use ibc::apps::transfer::types::{ack_success_b64, PORT_ID_STR}; + use ibc::core::channel::types::acknowledgement::AcknowledgementStatus; use super::*; diff --git a/crates/ibc/src/context/validation.rs b/crates/ibc/src/context/validation.rs index e063ccc772..5cb60fcd12 100644 --- a/crates/ibc/src/context/validation.rs +++ b/crates/ibc/src/context/validation.rs @@ -1,31 +1,31 @@ //! ValidationContext implementation for IBC -#[cfg(feature = "testing")] -use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; -use namada_core::ibc::clients::tendermint::client_state::ClientState as TmClientState; -use namada_core::ibc::core::channel::types::channel::ChannelEnd; -use namada_core::ibc::core::channel::types::commitment::{ +use ibc::clients::tendermint::client_state::ClientState as TmClientState; +use ibc::core::channel::types::channel::ChannelEnd; +use ibc::core::channel::types::commitment::{ AcknowledgementCommitment, PacketCommitment, }; -use namada_core::ibc::core::channel::types::packet::Receipt; -use namada_core::ibc::core::client::context::{ +use ibc::core::channel::types::packet::Receipt; +use ibc::core::client::context::{ ClientValidationContext, ExtClientValidationContext, }; -use namada_core::ibc::core::client::types::Height; -use namada_core::ibc::core::commitment_types::commitment::CommitmentPrefix; -use namada_core::ibc::core::commitment_types::specs::ProofSpecs; -use namada_core::ibc::core::connection::types::ConnectionEnd; -use namada_core::ibc::core::handler::types::error::ContextError; -use namada_core::ibc::core::host::types::identifiers::{ +use ibc::core::client::types::Height; +use ibc::core::commitment_types::commitment::CommitmentPrefix; +use ibc::core::commitment_types::specs::ProofSpecs; +use ibc::core::connection::types::ConnectionEnd; +use ibc::core::handler::types::error::ContextError; +use ibc::core::host::types::identifiers::{ ChainId, ClientId, ConnectionId, Sequence, }; -use namada_core::ibc::core::host::types::path::{ +use ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConsensusStatePath, CommitmentPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; -use namada_core::ibc::core::host::ValidationContext; -use namada_core::ibc::cosmos_host::ValidateSelfClientContext; -use namada_core::ibc::primitives::{Signer, Timestamp}; +use ibc::core::host::ValidationContext; +use ibc::cosmos_host::ValidateSelfClientContext; +use ibc::primitives::{Signer, Timestamp}; +#[cfg(feature = "testing")] +use ibc_testkit::testapp::ibc::clients::mock::client_state::MockClientState; use super::client::{AnyClientState, AnyConsensusState}; use super::common::IbcCommonContext; diff --git a/crates/ibc/src/event.rs b/crates/ibc/src/event.rs index 7bd613e881..173e87e6e9 100644 --- a/crates/ibc/src/event.rs +++ b/crates/ibc/src/event.rs @@ -3,20 +3,20 @@ use std::cmp::Ordering; use std::str::FromStr; -use namada_core::borsh::*; -use namada_core::collections::HashMap; -use namada_core::ibc::core::channel::types::packet::Packet; -use namada_core::ibc::core::channel::types::timeout::TimeoutHeight as IbcTimeoutHeight; -use namada_core::ibc::core::client::types::events::{ +use ibc::core::channel::types::packet::Packet; +use ibc::core::channel::types::timeout::TimeoutHeight as IbcTimeoutHeight; +use ibc::core::client::types::events::{ CLIENT_ID_ATTRIBUTE_KEY, CONSENSUS_HEIGHTS_ATTRIBUTE_KEY, }; -use namada_core::ibc::core::client::types::{Height as IbcHeight, HeightError}; -use namada_core::ibc::core::handler::types::events::IbcEvent as RawIbcEvent; -use namada_core::ibc::core::host::types::identifiers::{ +use ibc::core::client::types::{Height as IbcHeight, HeightError}; +use ibc::core::handler::types::events::IbcEvent as RawIbcEvent; +use ibc::core::host::types::identifiers::{ ChannelId as IbcChannelId, ClientId as IbcClientId, ConnectionId as IbcConnectionId, PortId, Sequence, }; -use namada_core::ibc::primitives::Timestamp; +use ibc::primitives::Timestamp; +use namada_core::borsh::*; +use namada_core::collections::HashMap; use namada_core::tendermint::abci::Event as AbciEvent; use namada_events::extend::{ event_domain_of, AttributesMap, EventAttributeEntry, ExtendAttributesMap, @@ -36,7 +36,7 @@ pub const TOKEN_EVENT_DESCRIPTOR: &str = IbcEvent::DOMAIN; pub mod types { //! IBC event types. - use namada_core::ibc::core::client::types::events::UPDATE_CLIENT_EVENT; + use ibc::core::client::types::events::UPDATE_CLIENT_EVENT; use namada_events::{event_type, EventType}; use super::IbcEvent; @@ -388,7 +388,7 @@ impl FromStr for TimeoutHeight { type Err = HeightError; fn from_str(s: &str) -> Result { - namada_core::ibc::core::client::types::Height::from_str(s).map_or_else( + ibc::core::client::types::Height::from_str(s).map_or_else( |err| match err { HeightError::ZeroHeight => { Ok(TimeoutHeight(IbcTimeoutHeight::Never)) diff --git a/crates/ibc/src/lib.rs b/crates/ibc/src/lib.rs index fc79cfd566..0d70c52419 100644 --- a/crates/ibc/src/lib.rs +++ b/crates/ibc/src/lib.rs @@ -20,6 +20,8 @@ mod actions; pub mod context; pub mod event; +mod msg; +mod nft; pub mod parameters; pub mod storage; @@ -27,6 +29,7 @@ use std::cell::RefCell; use std::collections::BTreeSet; use std::fmt::Debug; use std::rc::Rc; +use std::str::FromStr; pub use actions::transfer_over_ibc; use borsh::BorshDeserialize; @@ -39,45 +42,54 @@ pub use context::token_transfer::TokenTransferContext; pub use context::transfer_mod::{ModuleWrapper, TransferModule}; use context::IbcContext; pub use context::ValidationParams; -use namada_core::address::{Address, MASP}; -use namada_core::ibc::apps::nft_transfer::handler::{ +use ibc::apps::nft_transfer::handler::{ send_nft_transfer_execute, send_nft_transfer_validate, }; -use namada_core::ibc::apps::nft_transfer::types::error::NftTransferError; -use namada_core::ibc::apps::nft_transfer::types::packet::PacketData as NftPacketData; -use namada_core::ibc::apps::nft_transfer::types::{ +use ibc::apps::nft_transfer::types::error::NftTransferError; +use ibc::apps::nft_transfer::types::packet::PacketData as NftPacketData; +use ibc::apps::nft_transfer::types::{ is_receiver_chain_source as is_nft_receiver_chain_source, PrefixedClassId, - TokenId, TracePrefix as NftTracePrefix, + TokenId, TracePath as NftTracePath, TracePrefix as NftTracePrefix, }; -use namada_core::ibc::apps::transfer::handler::{ +use ibc::apps::transfer::handler::{ send_transfer_execute, send_transfer_validate, }; -use namada_core::ibc::apps::transfer::types::error::TokenTransferError; -use namada_core::ibc::apps::transfer::types::packet::PacketData; -use namada_core::ibc::apps::transfer::types::{ - is_receiver_chain_source, TracePrefix, +use ibc::apps::transfer::types::error::TokenTransferError; +use ibc::apps::transfer::types::packet::PacketData; +use ibc::apps::transfer::types::{ + is_receiver_chain_source, PrefixedDenom, TracePath, TracePrefix, }; -use namada_core::ibc::core::channel::types::acknowledgement::{ +use ibc::core::channel::types::acknowledgement::{ Acknowledgement, AcknowledgementStatus, }; -use namada_core::ibc::core::channel::types::msgs::{ +use ibc::core::channel::types::msgs::{ MsgRecvPacket as IbcMsgRecvPacket, PacketMsg, }; -use namada_core::ibc::core::entrypoint::{execute, validate}; -use namada_core::ibc::core::handler::types::error::ContextError; -use namada_core::ibc::core::handler::types::events::Error as RawIbcEventError; -use namada_core::ibc::core::handler::types::msgs::MsgEnvelope; -use namada_core::ibc::core::host::types::error::IdentifierError; -use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; -use namada_core::ibc::core::router::types::error::RouterError; -use namada_core::ibc::primitives::proto::Any; -pub use namada_core::ibc::*; +use ibc::core::entrypoint::{execute, validate}; +use ibc::core::handler::types::error::ContextError; +use ibc::core::handler::types::events::Error as RawIbcEventError; +use ibc::core::handler::types::msgs::MsgEnvelope; +use ibc::core::host::types::error::IdentifierError; +use ibc::core::host::types::identifiers::{ChannelId, PortId}; +use ibc::core::router::types::error::RouterError; +use ibc::primitives::proto::Any; +pub use ibc::*; +pub use msg::*; +use namada_core::address::{self, Address, MASP}; use namada_core::masp::PaymentAddress; use namada_events::extend::{ReadFromEventAttributes, Success as SuccessAttr}; -use namada_token::Transfer; +use namada_token::UnshieldingTransfer; +pub use nft::*; use prost::Message; use thiserror::Error; +/// The event type defined in ibc-rs for receiving a token +pub const EVENT_TYPE_PACKET: &str = "fungible_token_packet"; +/// The event type defined in ibc-rs for receiving an NFT +pub const EVENT_TYPE_NFT_PACKET: &str = "non_fungible_token_packet"; +/// The escrow address for IBC transfer +pub const IBC_ESCROW_ADDRESS: Address = address::IBC; + #[allow(missing_docs)] #[derive(Error, Debug)] pub enum Error { @@ -142,7 +154,7 @@ where pub fn execute( &mut self, tx_data: &[u8], - ) -> Result, Error> { + ) -> Result, Error> { let message = decode_message(tx_data)?; match &message { IbcMessage::Transfer(msg) => { @@ -550,6 +562,37 @@ pub fn received_ibc_token( } } +/// Returns the trace path and the token string if the denom is an IBC +/// denom. +pub fn is_ibc_denom(denom: impl AsRef) -> Option<(TracePath, String)> { + let prefixed_denom = PrefixedDenom::from_str(denom.as_ref()).ok()?; + let base_denom = prefixed_denom.base_denom.to_string(); + if prefixed_denom.trace_path.is_empty() || base_denom.contains('/') { + // The denom is just a token or an NFT trace + return None; + } + // The base token isn't decoded because it could be non Namada token + Some((prefixed_denom.trace_path, base_denom)) +} + +/// Returns the trace path and the token string if the trace is an NFT one +pub fn is_nft_trace( + trace: impl AsRef, +) -> Option<(NftTracePath, String, String)> { + // The trace should be {port}/{channel}/.../{class_id}/{token_id} + if let Some((class_id, token_id)) = trace.as_ref().rsplit_once('/') { + let prefixed_class_id = PrefixedClassId::from_str(class_id).ok()?; + // The base token isn't decoded because it could be non Namada token + Some(( + prefixed_class_id.trace_path, + prefixed_class_id.base_class_id.to_string(), + token_id.to_string(), + )) + } else { + None + } +} + #[cfg(any(test, feature = "testing"))] /// Testing helpers ans strategies for IBC pub mod testing { diff --git a/crates/ibc/src/msg.rs b/crates/ibc/src/msg.rs new file mode 100644 index 0000000000..7b607617f1 --- /dev/null +++ b/crates/ibc/src/msg.rs @@ -0,0 +1,193 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use ibc::apps::nft_transfer::types::msgs::transfer::MsgTransfer as IbcMsgNftTransfer; +use ibc::apps::transfer::types::msgs::transfer::MsgTransfer as IbcMsgTransfer; +use ibc::core::channel::types::msgs::{ + MsgAcknowledgement as IbcMsgAcknowledgement, + MsgRecvPacket as IbcMsgRecvPacket, MsgTimeout as IbcMsgTimeout, +}; +use ibc::core::handler::types::msgs::MsgEnvelope; +use ibc::primitives::proto::Protobuf; +use namada_token::UnshieldingTransfer; + +/// The different variants of an Ibc message +pub enum IbcMessage { + /// Ibc Envelop + Envelope(Box), + /// Ibc transaprent transfer + Transfer(MsgTransfer), + /// NFT transfer + NftTransfer(MsgNftTransfer), + /// Receiving a packet + RecvPacket(MsgRecvPacket), + /// Acknowledgement + AckPacket(MsgAcknowledgement), + /// Timeout + Timeout(MsgTimeout), +} + +/// IBC transfer message with `Transfer` +#[derive(Debug, Clone)] +pub struct MsgTransfer { + /// IBC transfer message + pub message: IbcMsgTransfer, + /// Shieleded transfer for MASP transaction + pub transfer: Option, +} + +impl BorshSerialize for MsgTransfer { + fn serialize( + &self, + writer: &mut W, + ) -> std::io::Result<()> { + let encoded_msg = self.message.clone().encode_vec(); + let members = (encoded_msg, self.transfer.clone()); + BorshSerialize::serialize(&members, writer) + } +} + +impl BorshDeserialize for MsgTransfer { + fn deserialize_reader( + reader: &mut R, + ) -> std::io::Result { + use std::io::{Error, ErrorKind}; + let (msg, transfer): (Vec, Option) = + BorshDeserialize::deserialize_reader(reader)?; + let message = IbcMsgTransfer::decode_vec(&msg) + .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; + Ok(Self { message, transfer }) + } +} + +/// IBC NFT transfer message with `Transfer` +#[derive(Debug, Clone)] +pub struct MsgNftTransfer { + /// IBC NFT transfer message + pub message: IbcMsgNftTransfer, + /// Shieleded transfer for MASP transaction + pub transfer: Option, +} + +impl BorshSerialize for MsgNftTransfer { + fn serialize( + &self, + writer: &mut W, + ) -> std::io::Result<()> { + let encoded_msg = self.message.clone().encode_vec(); + let members = (encoded_msg, self.transfer.clone()); + BorshSerialize::serialize(&members, writer) + } +} + +impl BorshDeserialize for MsgNftTransfer { + fn deserialize_reader( + reader: &mut R, + ) -> std::io::Result { + use std::io::{Error, ErrorKind}; + let (msg, transfer): (Vec, Option) = + BorshDeserialize::deserialize_reader(reader)?; + let message = IbcMsgNftTransfer::decode_vec(&msg) + .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; + Ok(Self { message, transfer }) + } +} + +/// IBC receiving packet message with `Transfer` +#[derive(Debug, Clone)] +pub struct MsgRecvPacket { + /// IBC receiving packet message + pub message: IbcMsgRecvPacket, + /// Shieleded transfer for MASP transaction + pub transfer: Option, +} + +impl BorshSerialize for MsgRecvPacket { + fn serialize( + &self, + writer: &mut W, + ) -> std::io::Result<()> { + let encoded_msg = self.message.clone().encode_vec(); + let members = (encoded_msg, self.transfer.clone()); + BorshSerialize::serialize(&members, writer) + } +} + +impl BorshDeserialize for MsgRecvPacket { + fn deserialize_reader( + reader: &mut R, + ) -> std::io::Result { + use std::io::{Error, ErrorKind}; + let (msg, transfer): (Vec, Option) = + BorshDeserialize::deserialize_reader(reader)?; + let message = IbcMsgRecvPacket::decode_vec(&msg) + .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; + Ok(Self { message, transfer }) + } +} + +/// IBC acknowledgement message with `Transfer` for refunding to a shielded +/// address +#[derive(Debug, Clone)] +pub struct MsgAcknowledgement { + /// IBC acknowledgement message + pub message: IbcMsgAcknowledgement, + /// Shieleded transfer for MASP transaction + pub transfer: Option, +} + +impl BorshSerialize for MsgAcknowledgement { + fn serialize( + &self, + writer: &mut W, + ) -> std::io::Result<()> { + let encoded_msg = self.message.clone().encode_vec(); + let members = (encoded_msg, self.transfer.clone()); + BorshSerialize::serialize(&members, writer) + } +} + +impl BorshDeserialize for MsgAcknowledgement { + fn deserialize_reader( + reader: &mut R, + ) -> std::io::Result { + use std::io::{Error, ErrorKind}; + let (msg, transfer): (Vec, Option) = + BorshDeserialize::deserialize_reader(reader)?; + let message = IbcMsgAcknowledgement::decode_vec(&msg) + .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; + Ok(Self { message, transfer }) + } +} + +/// IBC timeout packet message with `Transfer` for refunding to a shielded +/// address +#[derive(Debug, Clone)] +pub struct MsgTimeout { + /// IBC timeout message + pub message: IbcMsgTimeout, + /// Shieleded transfer for MASP transaction + pub transfer: Option, +} + +impl BorshSerialize for MsgTimeout { + fn serialize( + &self, + writer: &mut W, + ) -> std::io::Result<()> { + let encoded_msg = self.message.clone().encode_vec(); + let members = (encoded_msg, self.transfer.clone()); + BorshSerialize::serialize(&members, writer) + } +} + +impl BorshDeserialize for MsgTimeout { + fn deserialize_reader( + reader: &mut R, + ) -> std::io::Result { + use std::io::{Error, ErrorKind}; + let (msg, transfer): (Vec, Option) = + BorshDeserialize::deserialize_reader(reader)?; + let message = IbcMsgTimeout::decode_vec(&msg) + .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; + Ok(Self { message, transfer }) + } +} diff --git a/crates/ibc/src/nft.rs b/crates/ibc/src/nft.rs new file mode 100644 index 0000000000..a1f05502a9 --- /dev/null +++ b/crates/ibc/src/nft.rs @@ -0,0 +1,193 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use ibc::apps::nft_transfer::context::{NftClassContext, NftContext}; +use ibc::apps::nft_transfer::types::error::NftTransferError; +use ibc::apps::nft_transfer::types::{ + ClassData, ClassId, ClassUri, PrefixedClassId, TokenData, TokenId, TokenUri, +}; + +/// NFT class +#[derive(Clone, Debug)] +pub struct NftClass { + /// NFT class ID + pub class_id: PrefixedClassId, + /// NFT class URI + pub class_uri: Option, + /// NFT class data + pub class_data: Option, +} + +impl BorshSerialize for NftClass { + fn serialize( + &self, + writer: &mut W, + ) -> std::io::Result<()> { + BorshSerialize::serialize(&self.class_id.to_string(), writer)?; + match &self.class_uri { + Some(uri) => { + BorshSerialize::serialize(&true, writer)?; + BorshSerialize::serialize(&uri.to_string(), writer)?; + } + None => BorshSerialize::serialize(&false, writer)?, + } + match &self.class_data { + Some(data) => { + BorshSerialize::serialize(&true, writer)?; + BorshSerialize::serialize(&data.to_string(), writer) + } + None => BorshSerialize::serialize(&false, writer), + } + } +} + +impl BorshDeserialize for NftClass { + fn deserialize_reader( + reader: &mut R, + ) -> std::io::Result { + use std::io::{Error, ErrorKind}; + let class_id: String = BorshDeserialize::deserialize_reader(reader)?; + let class_id = class_id.parse().map_err(|e: NftTransferError| { + Error::new(ErrorKind::InvalidData, e.to_string()) + })?; + + let is_uri: bool = BorshDeserialize::deserialize_reader(reader)?; + let class_uri = if is_uri { + let uri_str: String = BorshDeserialize::deserialize_reader(reader)?; + Some(uri_str.parse().map_err(|e: NftTransferError| { + Error::new(ErrorKind::InvalidData, e.to_string()) + })?) + } else { + None + }; + + let is_data: bool = BorshDeserialize::deserialize_reader(reader)?; + let class_data = if is_data { + let data_str: String = + BorshDeserialize::deserialize_reader(reader)?; + Some(data_str.parse().map_err(|e: NftTransferError| { + Error::new(ErrorKind::InvalidData, e.to_string()) + })?) + } else { + None + }; + + Ok(Self { + class_id, + class_uri, + class_data, + }) + } +} + +impl NftClassContext for NftClass { + fn get_id(&self) -> &ClassId { + &self.class_id.base_class_id + } + + fn get_uri(&self) -> Option<&ClassUri> { + self.class_uri.as_ref() + } + + fn get_data(&self) -> Option<&ClassData> { + self.class_data.as_ref() + } +} + +/// NFT metadata +#[derive(Clone, Debug)] +pub struct NftMetadata { + /// NFT class ID + pub class_id: PrefixedClassId, + /// NFT ID + pub token_id: TokenId, + /// NFT URI + pub token_uri: Option, + /// NFT data + pub token_data: Option, +} + +impl BorshSerialize for NftMetadata { + fn serialize( + &self, + writer: &mut W, + ) -> std::io::Result<()> { + BorshSerialize::serialize(&self.class_id.to_string(), writer)?; + BorshSerialize::serialize(&self.token_id.to_string(), writer)?; + match &self.token_uri { + Some(uri) => { + BorshSerialize::serialize(&true, writer)?; + BorshSerialize::serialize(&uri.to_string(), writer)?; + } + None => BorshSerialize::serialize(&false, writer)?, + } + match &self.token_data { + Some(data) => { + BorshSerialize::serialize(&true, writer)?; + BorshSerialize::serialize(&data.to_string(), writer) + } + None => BorshSerialize::serialize(&false, writer), + } + } +} + +impl BorshDeserialize for NftMetadata { + fn deserialize_reader( + reader: &mut R, + ) -> std::io::Result { + use std::io::{Error, ErrorKind}; + let class_id: String = BorshDeserialize::deserialize_reader(reader)?; + let class_id = class_id.parse().map_err(|e: NftTransferError| { + Error::new(ErrorKind::InvalidData, e.to_string()) + })?; + + let token_id: String = BorshDeserialize::deserialize_reader(reader)?; + let token_id = token_id.parse().map_err(|e: NftTransferError| { + Error::new(ErrorKind::InvalidData, e.to_string()) + })?; + + let is_uri: bool = BorshDeserialize::deserialize_reader(reader)?; + let token_uri = if is_uri { + let uri_str: String = BorshDeserialize::deserialize_reader(reader)?; + Some(uri_str.parse().map_err(|e: NftTransferError| { + Error::new(ErrorKind::InvalidData, e.to_string()) + })?) + } else { + None + }; + + let is_data: bool = BorshDeserialize::deserialize_reader(reader)?; + let token_data = if is_data { + let data_str: String = + BorshDeserialize::deserialize_reader(reader)?; + Some(data_str.parse().map_err(|e: NftTransferError| { + Error::new(ErrorKind::InvalidData, e.to_string()) + })?) + } else { + None + }; + + Ok(Self { + class_id, + token_id, + token_uri, + token_data, + }) + } +} + +impl NftContext for NftMetadata { + fn get_class_id(&self) -> &ClassId { + &self.class_id.base_class_id + } + + fn get_id(&self) -> &TokenId { + &self.token_id + } + + fn get_uri(&self) -> Option<&TokenUri> { + self.token_uri.as_ref() + } + + fn get_data(&self) -> Option<&TokenData> { + self.token_data.as_ref() + } +} diff --git a/crates/ibc/src/storage.rs b/crates/ibc/src/storage.rs index c9fa19839a..b187b2e41c 100644 --- a/crates/ibc/src/storage.rs +++ b/crates/ibc/src/storage.rs @@ -2,17 +2,17 @@ use std::str::FromStr; -use namada_core::address::{Address, InternalAddress, HASH_LEN, SHA_HASH_LEN}; -use namada_core::ibc::apps::nft_transfer::types::{PrefixedClassId, TokenId}; -use namada_core::ibc::core::client::types::Height; -use namada_core::ibc::core::host::types::identifiers::{ +use ibc::apps::nft_transfer::types::{PrefixedClassId, TokenId}; +use ibc::core::client::types::Height; +use ibc::core::host::types::identifiers::{ ChannelId, ClientId, ConnectionId, PortId, Sequence, }; -use namada_core::ibc::core::host::types::path::{ +use ibc::core::host::types::path::{ AckPath, ChannelEndPath, ClientConnectionPath, ClientConsensusStatePath, ClientStatePath, CommitmentPath, ConnectionPath, Path, PortPath, ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; +use namada_core::address::{Address, InternalAddress, HASH_LEN, SHA_HASH_LEN}; use namada_core::ibc::IbcTokenHash; use namada_core::storage::{DbKeySeg, Key, KeySeg}; use namada_core::token::Amount; From ca84daaa37a9f42c1b51a51f721d917bd1389b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 28 May 2024 14:06:32 +0200 Subject: [PATCH 03/26] wasm: rename `tx_transfer` to `tx_transparent_transfer` --- wasm/Cargo.toml | 2 +- wasm/{tx_transfer => tx_transparent_transfer}/Cargo.toml | 0 wasm/{tx_transfer => tx_transparent_transfer}/src/lib.rs | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) rename wasm/{tx_transfer => tx_transparent_transfer}/Cargo.toml (100%) rename wasm/{tx_transfer => tx_transparent_transfer}/src/lib.rs (87%) diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 890a3b6c61..7412b35aae 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -16,7 +16,7 @@ members = [ "tx_reactivate_validator", "tx_redelegate", "tx_resign_steward", - "tx_transfer", + "tx_transparent_transfer", "tx_unbond", "tx_update_account", "tx_reveal_pk", diff --git a/wasm/tx_transfer/Cargo.toml b/wasm/tx_transparent_transfer/Cargo.toml similarity index 100% rename from wasm/tx_transfer/Cargo.toml rename to wasm/tx_transparent_transfer/Cargo.toml diff --git a/wasm/tx_transfer/src/lib.rs b/wasm/tx_transparent_transfer/src/lib.rs similarity index 87% rename from wasm/tx_transfer/src/lib.rs rename to wasm/tx_transparent_transfer/src/lib.rs index 62e7adebac..7cbfaed821 100644 --- a/wasm/tx_transfer/src/lib.rs +++ b/wasm/tx_transparent_transfer/src/lib.rs @@ -1,5 +1,5 @@ -//! A tx for token transfer. -//! This tx uses `token::Transfer` wrapped inside `SignedTxData` +//! A tx for transparent token transfer. +//! This tx uses `token::TransparentTransfer` wrapped inside `SignedTxData` //! as its input as declared in `namada` crate. use namada_tx_prelude::action::{Action, MaspAction, Write}; @@ -8,7 +8,7 @@ use namada_tx_prelude::*; #[transaction] fn apply_tx(ctx: &mut Ctx, tx_data: BatchedTx) -> TxResult { let data = ctx.get_tx_data(&tx_data)?; - let transfer = token::Transfer::try_from_slice(&data[..]) + let transfer = token::TransparentTransfer::try_from_slice(&data[..]) .wrap_err("Failed to decode token::Transfer tx data")?; debug_log!("apply_tx called with transfer: {:#?}", transfer); From 006d4697bf52462f92ad36059d33cffa88ee2e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 28 May 2024 14:08:45 +0200 Subject: [PATCH 04/26] wasm/tx_transparent_transfer: remove the shielded section --- wasm/tx_transparent_transfer/Cargo.toml | 2 +- wasm/tx_transparent_transfer/src/lib.rs | 25 ++----------------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/wasm/tx_transparent_transfer/Cargo.toml b/wasm/tx_transparent_transfer/Cargo.toml index 8d664f48f3..b1b03e76b1 100644 --- a/wasm/tx_transparent_transfer/Cargo.toml +++ b/wasm/tx_transparent_transfer/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tx_transfer" +name = "tx_transparent_transfer" description = "WASM transaction to transfer tokens" authors.workspace = true edition.workspace = true diff --git a/wasm/tx_transparent_transfer/src/lib.rs b/wasm/tx_transparent_transfer/src/lib.rs index 7cbfaed821..76aacc3891 100644 --- a/wasm/tx_transparent_transfer/src/lib.rs +++ b/wasm/tx_transparent_transfer/src/lib.rs @@ -2,14 +2,13 @@ //! This tx uses `token::TransparentTransfer` wrapped inside `SignedTxData` //! as its input as declared in `namada` crate. -use namada_tx_prelude::action::{Action, MaspAction, Write}; use namada_tx_prelude::*; #[transaction] fn apply_tx(ctx: &mut Ctx, tx_data: BatchedTx) -> TxResult { let data = ctx.get_tx_data(&tx_data)?; let transfer = token::TransparentTransfer::try_from_slice(&data[..]) - .wrap_err("Failed to decode token::Transfer tx data")?; + .wrap_err("Failed to decode token::TransparentTransfer tx data")?; debug_log!("apply_tx called with transfer: {:#?}", transfer); token::transfer( @@ -19,25 +18,5 @@ fn apply_tx(ctx: &mut Ctx, tx_data: BatchedTx) -> TxResult { &transfer.token, transfer.amount.amount(), ) - .wrap_err("Token transfer failed")?; - - if let Some(masp_section_ref) = transfer.shielded { - let shielded = tx_data - .tx - .get_section(&masp_section_ref) - .and_then(|x| x.as_ref().masp_tx()) - .ok_or_err_msg( - "Unable to find required shielded section in tx data", - ) - .map_err(|err| { - ctx.set_commitment_sentinel(); - err - })?; - token::utils::handle_masp_tx(ctx, &shielded) - .wrap_err("Encountered error while handling MASP transaction")?; - update_masp_note_commitment_tree(&shielded) - .wrap_err("Failed to update the MASP commitment tree")?; - ctx.push_action(Action::Masp(MaspAction { masp_section_ref }))?; - } - Ok(()) + .wrap_err("Token transfer failed") } From efc30ac9968d161ecc4387fedbcdf4fedc0443a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 28 May 2024 14:22:44 +0200 Subject: [PATCH 05/26] wasm: add shielded, shielding and unshielding transfer --- crates/tx_prelude/src/lib.rs | 2 +- wasm/Cargo.lock | 33 ++++++++++++++++++++- wasm/Cargo.toml | 7 +++-- wasm/tx_shielded_transfer/Cargo.toml | 17 +++++++++++ wasm/tx_shielded_transfer/src/lib.rs | 29 ++++++++++++++++++ wasm/tx_shielding_transfer/Cargo.toml | 17 +++++++++++ wasm/tx_shielding_transfer/src/lib.rs | 39 +++++++++++++++++++++++++ wasm/tx_unshielding_transfer/Cargo.toml | 17 +++++++++++ wasm/tx_unshielding_transfer/src/lib.rs | 39 +++++++++++++++++++++++++ 9 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 wasm/tx_shielded_transfer/Cargo.toml create mode 100644 wasm/tx_shielded_transfer/src/lib.rs create mode 100644 wasm/tx_shielding_transfer/Cargo.toml create mode 100644 wasm/tx_shielding_transfer/src/lib.rs create mode 100644 wasm/tx_unshielding_transfer/Cargo.toml create mode 100644 wasm/tx_unshielding_transfer/src/lib.rs diff --git a/crates/tx_prelude/src/lib.rs b/crates/tx_prelude/src/lib.rs index eb0f6a3be9..3b737c8157 100644 --- a/crates/tx_prelude/src/lib.rs +++ b/crates/tx_prelude/src/lib.rs @@ -37,7 +37,7 @@ use namada_core::storage::TxIndex; pub use namada_core::storage::{ self, BlockHash, BlockHeight, Epoch, Header, BLOCK_HASH_LENGTH, }; -pub use namada_core::{encode, eth_bridge_pool, *}; +pub use namada_core::{address, encode, eth_bridge_pool, *}; use namada_events::{EmitEvents, Event, EventToEmit, EventType}; pub use namada_governance::storage as gov_storage; pub use namada_macros::transaction; diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 2a00208e8a..21c7a95a9e 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -4090,11 +4090,15 @@ dependencies = [ name = "namada_token" version = "0.37.0" dependencies = [ + "borsh 1.4.0", "namada_core", "namada_events", + "namada_macros", "namada_shielded_token", "namada_storage", "namada_trans_token", + "proptest", + "serde", ] [[package]] @@ -6750,7 +6754,25 @@ dependencies = [ ] [[package]] -name = "tx_transfer" +name = "tx_shielded_transfer" +version = "0.37.0" +dependencies = [ + "getrandom 0.2.11", + "namada_tx_prelude", + "wee_alloc", +] + +[[package]] +name = "tx_shielding_transfer" +version = "0.37.0" +dependencies = [ + "getrandom 0.2.11", + "namada_tx_prelude", + "wee_alloc", +] + +[[package]] +name = "tx_transparent_transfer" version = "0.37.0" dependencies = [ "getrandom 0.2.11", @@ -6784,6 +6806,15 @@ dependencies = [ "wee_alloc", ] +[[package]] +name = "tx_unshielding_transfer" +version = "0.37.0" +dependencies = [ + "getrandom 0.2.11", + "namada_tx_prelude", + "wee_alloc", +] + [[package]] name = "tx_update_account" version = "0.37.0" diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 7412b35aae..f389a579be 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -16,12 +16,15 @@ members = [ "tx_reactivate_validator", "tx_redelegate", "tx_resign_steward", + "tx_reveal_pk", + "tx_shielded_transfer", + "tx_shielding_transfer", "tx_transparent_transfer", "tx_unbond", + "tx_unjail_validator", + "tx_unshielding_transfer", "tx_update_account", - "tx_reveal_pk", "tx_update_steward_commission", - "tx_unjail_validator", "tx_vote_proposal", "tx_withdraw", "vp_implicit", diff --git a/wasm/tx_shielded_transfer/Cargo.toml b/wasm/tx_shielded_transfer/Cargo.toml new file mode 100644 index 0000000000..e0fd515ff3 --- /dev/null +++ b/wasm/tx_shielded_transfer/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tx_shielded_transfer" +description = "WASM transaction to transfer tokens" +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +namada_tx_prelude.workspace = true +wee_alloc.workspace = true +getrandom.workspace = true + +[lib] +crate-type = ["cdylib"] diff --git a/wasm/tx_shielded_transfer/src/lib.rs b/wasm/tx_shielded_transfer/src/lib.rs new file mode 100644 index 0000000000..cc9e70a638 --- /dev/null +++ b/wasm/tx_shielded_transfer/src/lib.rs @@ -0,0 +1,29 @@ +//! A tx for shielded token transfer. + +use namada_tx_prelude::action::{Action, MaspAction, Write}; +use namada_tx_prelude::*; + +#[transaction] +fn apply_tx(ctx: &mut Ctx, tx_data: BatchedTx) -> TxResult { + let data = ctx.get_tx_data(&tx_data)?; + let transfer = token::ShieldedTransfer::try_from_slice(&data[..]) + .wrap_err("Failed to decode token::ShieldedTransfer tx data")?; + debug_log!("apply_tx called with transfer: {:#?}", transfer); + + let masp_section_ref = transfer.section_hash; + let shielded = tx_data + .tx + .get_section(&masp_section_ref) + .and_then(|x| x.as_ref().masp_tx()) + .ok_or_err_msg("Unable to find required shielded section in tx data") + .map_err(|err| { + ctx.set_commitment_sentinel(); + err + })?; + token::utils::handle_masp_tx(ctx, &shielded) + .wrap_err("Encountered error while handling MASP transaction")?; + update_masp_note_commitment_tree(&shielded) + .wrap_err("Failed to update the MASP commitment tree")?; + ctx.push_action(Action::Masp(MaspAction { masp_section_ref }))?; + Ok(()) +} diff --git a/wasm/tx_shielding_transfer/Cargo.toml b/wasm/tx_shielding_transfer/Cargo.toml new file mode 100644 index 0000000000..c7e0475434 --- /dev/null +++ b/wasm/tx_shielding_transfer/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tx_shielding_transfer" +description = "WASM transaction to transfer tokens" +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +namada_tx_prelude.workspace = true +wee_alloc.workspace = true +getrandom.workspace = true + +[lib] +crate-type = ["cdylib"] diff --git a/wasm/tx_shielding_transfer/src/lib.rs b/wasm/tx_shielding_transfer/src/lib.rs new file mode 100644 index 0000000000..389942686b --- /dev/null +++ b/wasm/tx_shielding_transfer/src/lib.rs @@ -0,0 +1,39 @@ +//! A tx for shielding token transfer. + +use namada_tx_prelude::action::{Action, MaspAction, Write}; +use namada_tx_prelude::*; + +#[transaction] +fn apply_tx(ctx: &mut Ctx, tx_data: BatchedTx) -> TxResult { + let data = ctx.get_tx_data(&tx_data)?; + let transfer = token::ShieldingTransfer::try_from_slice(&data[..]) + .wrap_err("Failed to decode token::ShieldingTransfer tx data")?; + debug_log!("apply_tx called with transfer: {:#?}", transfer); + + token::transfer( + ctx, + &transfer.source, + &address::MASP, + &transfer.token, + transfer.amount.amount(), + ) + .wrap_err("Token transfer failed")?; + + let masp_section_ref = transfer.shielded_section_hash; + let shielded = tx_data + .tx + .get_section(&masp_section_ref) + .and_then(|x| x.as_ref().masp_tx()) + .ok_or_err_msg("Unable to find required shielded section in tx data") + .map_err(|err| { + ctx.set_commitment_sentinel(); + err + })?; + token::utils::handle_masp_tx(ctx, &shielded) + .wrap_err("Encountered error while handling MASP transaction")?; + update_masp_note_commitment_tree(&shielded) + .wrap_err("Failed to update the MASP commitment tree")?; + ctx.push_action(Action::Masp(MaspAction { masp_section_ref }))?; + + Ok(()) +} diff --git a/wasm/tx_unshielding_transfer/Cargo.toml b/wasm/tx_unshielding_transfer/Cargo.toml new file mode 100644 index 0000000000..5f42ead57c --- /dev/null +++ b/wasm/tx_unshielding_transfer/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tx_unshielding_transfer" +description = "WASM transaction to transfer tokens" +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +namada_tx_prelude.workspace = true +wee_alloc.workspace = true +getrandom.workspace = true + +[lib] +crate-type = ["cdylib"] diff --git a/wasm/tx_unshielding_transfer/src/lib.rs b/wasm/tx_unshielding_transfer/src/lib.rs new file mode 100644 index 0000000000..79bdac0757 --- /dev/null +++ b/wasm/tx_unshielding_transfer/src/lib.rs @@ -0,0 +1,39 @@ +//! A tx for unshielding token transfer. + +use namada_tx_prelude::action::{Action, MaspAction, Write}; +use namada_tx_prelude::*; + +#[transaction] +fn apply_tx(ctx: &mut Ctx, tx_data: BatchedTx) -> TxResult { + let data = ctx.get_tx_data(&tx_data)?; + let transfer = token::UnshieldingTransfer::try_from_slice(&data[..]) + .wrap_err("Failed to decode token::UnshieldingTransfer tx data")?; + debug_log!("apply_tx called with transfer: {:#?}", transfer); + + token::transfer( + ctx, + &address::MASP, + &transfer.target, + &transfer.token, + transfer.amount.amount(), + ) + .wrap_err("Token transfer failed")?; + + let masp_section_ref = transfer.shielded_section_hash; + let shielded = tx_data + .tx + .get_section(&masp_section_ref) + .and_then(|x| x.as_ref().masp_tx()) + .ok_or_err_msg("Unable to find required shielded section in tx data") + .map_err(|err| { + ctx.set_commitment_sentinel(); + err + })?; + token::utils::handle_masp_tx(ctx, &shielded) + .wrap_err("Encountered error while handling MASP transaction")?; + update_masp_note_commitment_tree(&shielded) + .wrap_err("Failed to update the MASP commitment tree")?; + ctx.push_action(Action::Masp(MaspAction { masp_section_ref }))?; + + Ok(()) +} From 91a258801e898d9bd3dcd5adedb682ec05904365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:22:11 +0200 Subject: [PATCH 06/26] sdk: update for split-up transfer tx --- crates/sdk/Cargo.toml | 2 + crates/sdk/src/args.rs | 113 +++++++++-- crates/sdk/src/lib.rs | 174 +++++++++++----- crates/sdk/src/signing.rs | 268 ++++++++++++++++++------ crates/sdk/src/tx.rs | 383 +++++++++++++++++++++++++++-------- crates/sdk/src/wallet/mod.rs | 2 +- 6 files changed, 731 insertions(+), 211 deletions(-) diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml index d681124f8e..1ad3c4523e 100644 --- a/crates/sdk/Cargo.toml +++ b/crates/sdk/Cargo.toml @@ -46,6 +46,7 @@ testing = [ "namada_ibc/testing", "namada_proof_of_stake/testing", "namada_storage/testing", + "namada_token/testing", "namada_tx/testing", "async-client", "proptest", @@ -160,6 +161,7 @@ namada_proof_of_stake = { path = "../proof_of_stake", default-features = false, namada_state = { path = "../state", features = ["testing"] } namada_storage = { path = "../storage", features = ["testing"] } namada_test_utils = { path = "../test_utils" } +namada_token = { path = "../token", features = ["testing"] } namada_tx = { path = "../tx", features = ["testing"]} namada_vote_ext = {path = "../vote_ext"} diff --git a/crates/sdk/src/args.rs b/crates/sdk/src/args.rs index b8cbe7ad06..b839c71903 100644 --- a/crates/sdk/src/args.rs +++ b/crates/sdk/src/args.rs @@ -59,10 +59,12 @@ pub trait NamadaTypes: Clone + std::fmt::Debug { + From; /// Represents the address of an Ethereum endpoint type EthereumAddress: Clone + std::fmt::Debug; - /// Represents a viewing key + /// Represents a shielded viewing key type ViewingKey: Clone + std::fmt::Debug; - /// Represents a spending key + /// Represents a shielded spending key type SpendingKey: Clone + std::fmt::Debug; + /// Represents a shielded payment address + type PaymentAddress: Clone + std::fmt::Debug; /// Represents the owner of a balance type BalanceOwner: Clone + std::fmt::Debug; /// Represents a public key @@ -101,6 +103,7 @@ impl NamadaTypes for SdkTypes { type Data = Vec; type EthereumAddress = (); type Keypair = namada_core::key::common::SecretKey; + type PaymentAddress = namada_core::masp::PaymentAddress; type PublicKey = namada_core::key::common::PublicKey; type SpendingKey = namada_core::masp::ExtendedSpendingKey; type TendermintAddress = tendermint_rpc::Url; @@ -226,15 +229,15 @@ impl From for InputAmount { } } -/// Transfer transaction arguments +/// Transparent transfer transaction arguments #[derive(Clone, Debug)] -pub struct TxTransfer { +pub struct TxTransparentTransfer { /// Common tx arguments pub tx: Tx, /// Transfer source address - pub source: C::TransferSource, + pub source: C::Address, /// Transfer target address - pub target: C::TransferTarget, + pub target: C::Address, /// Transferred token address pub token: C::Address, /// Transferred token amount @@ -243,26 +246,26 @@ pub struct TxTransfer { pub tx_code_path: PathBuf, } -impl TxBuilder for TxTransfer { +impl TxBuilder for TxTransparentTransfer { fn tx(self, func: F) -> Self where F: FnOnce(Tx) -> Tx, { - TxTransfer { + TxTransparentTransfer { tx: func(self.tx), ..self } } } -impl TxTransfer { +impl TxTransparentTransfer { /// Transfer source address - pub fn source(self, source: C::TransferSource) -> Self { + pub fn source(self, source: C::Address) -> Self { Self { source, ..self } } /// Transfer target address - pub fn receiver(self, target: C::TransferTarget) -> Self { + pub fn receiver(self, target: C::Address) -> Self { Self { target, ..self } } @@ -285,14 +288,94 @@ impl TxTransfer { } } -impl TxTransfer { +impl TxTransparentTransfer { /// Build a transaction from this builder pub async fn build( &mut self, context: &impl Namada, - ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Option)> - { - tx::build_transfer(context, self).await + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { + tx::build_transparent_transfer(context, self).await + } +} + +/// Shielded transfer transaction arguments +#[derive(Clone, Debug)] +pub struct TxShieldedTransfer { + /// Common tx arguments + pub tx: Tx, + /// Transfer source spending key + pub source: C::SpendingKey, + /// Transfer target address + pub target: C::PaymentAddress, + /// Transferred token address + pub token: C::Address, + /// Transferred token amount + pub amount: InputAmount, + /// Path to the TX WASM code file + pub tx_code_path: PathBuf, +} + +impl TxShieldedTransfer { + /// Build a transaction from this builder + pub async fn build( + &mut self, + context: &impl Namada, + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Epoch)> { + tx::build_shielded_transfer(context, self).await + } +} + +/// Shielding transfer transaction arguments +#[derive(Clone, Debug)] +pub struct TxShieldingTransfer { + /// Common tx arguments + pub tx: Tx, + /// Transfer source address + pub source: C::Address, + /// Transfer target address + pub target: C::PaymentAddress, + /// Transferred token address + pub token: C::Address, + /// Transferred token amount + pub amount: InputAmount, + /// Path to the TX WASM code file + pub tx_code_path: PathBuf, +} + +impl TxShieldingTransfer { + /// Build a transaction from this builder + pub async fn build( + &mut self, + context: &impl Namada, + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Epoch)> { + tx::build_shielding_transfer(context, self).await + } +} + +/// Unshielding transfer transaction arguments +#[derive(Clone, Debug)] +pub struct TxUnshieldingTransfer { + /// Common tx arguments + pub tx: Tx, + /// Transfer source spending key + pub source: C::SpendingKey, + /// Transfer target address + pub target: C::Address, + /// Transferred token address + pub token: C::Address, + /// Transferred token amount + pub amount: InputAmount, + /// Path to the TX WASM code file + pub tx_code_path: PathBuf, +} + +impl TxUnshieldingTransfer { + /// Build a transaction from this builder + pub async fn build( + &mut self, + context: &impl Namada, + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Epoch)> { + tx::build_unshielding_transfer(context, self).await } } diff --git a/crates/sdk/src/lib.rs b/crates/sdk/src/lib.rs index f1d6d672ee..c8bbe5cdf0 100644 --- a/crates/sdk/src/lib.rs +++ b/crates/sdk/src/lib.rs @@ -21,7 +21,7 @@ pub use { bip39, masp_primitives, masp_proofs, namada_account as account, namada_gas as gas, namada_governance as governance, namada_proof_of_stake as proof_of_stake, namada_state as state, - namada_storage as storage, zeroize, + namada_storage as storage, namada_token as token, zeroize, }; pub mod eth_bridge; @@ -51,36 +51,35 @@ use std::path::PathBuf; use std::str::FromStr; use args::{InputAmount, SdkTypes}; +use io::Io; +use masp::{ShieldedContext, ShieldedUtils}; use namada_core::address::Address; use namada_core::collections::HashSet; use namada_core::dec::Dec; use namada_core::ethereum_events::EthAddress; use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; use namada_core::key::*; -use namada_core::masp::{TransferSource, TransferTarget}; +use namada_core::masp::{ExtendedSpendingKey, PaymentAddress, TransferSource}; use namada_tx::data::wrapper::GasLimit; use namada_tx::Tx; +use rpc::{denominate_amount, format_denominated_amount, query_native_token}; +use signing::SigningTxData; +use token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; - -use crate::io::Io; -use crate::masp::{ShieldedContext, ShieldedUtils}; -use crate::rpc::{ - denominate_amount, format_denominated_amount, query_native_token, -}; -use crate::signing::SigningTxData; -use crate::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; -use crate::tx::{ +use tx::{ ProcessTxResponse, TX_BECOME_VALIDATOR_WASM, TX_BOND_WASM, TX_BRIDGE_POOL_WASM, TX_CHANGE_COMMISSION_WASM, TX_CHANGE_CONSENSUS_KEY_WASM, TX_CHANGE_METADATA_WASM, TX_CLAIM_REWARDS_WASM, TX_DEACTIVATE_VALIDATOR_WASM, TX_IBC_WASM, TX_INIT_ACCOUNT_WASM, TX_INIT_PROPOSAL, TX_REACTIVATE_VALIDATOR_WASM, - TX_REDELEGATE_WASM, TX_RESIGN_STEWARD, TX_REVEAL_PK, TX_TRANSFER_WASM, - TX_UNBOND_WASM, TX_UNJAIL_VALIDATOR_WASM, TX_UPDATE_ACCOUNT_WASM, + TX_REDELEGATE_WASM, TX_RESIGN_STEWARD, TX_REVEAL_PK, + TX_SHIELDED_TRANSFER_WASM, TX_SHIELDING_TRANSFER_WASM, + TX_TRANSPARENT_TRANSFER_WASM, TX_UNBOND_WASM, TX_UNJAIL_VALIDATOR_WASM, + TX_UNSHIELDING_TRANSFER_WASM, TX_UPDATE_ACCOUNT_WASM, TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL, TX_WITHDRAW_WASM, VP_USER_WASM, }; -use crate::wallet::{Wallet, WalletIo, WalletStorage}; +use wallet::{Wallet, WalletIo, WalletStorage}; /// Default gas-limit pub const DEFAULT_GAS_LIMIT: u64 = 25_000; @@ -172,20 +171,78 @@ pub trait Namada: Sized + MaybeSync + MaybeSend { } } - /// Make a TxTransfer builder from the given minimum set of arguments - fn new_transfer( + /// Make a TxTransparentTransfer builder from the given minimum set of + /// arguments + fn new_transparent_transfer( &self, - source: TransferSource, - target: TransferTarget, + source: Address, + target: Address, + token: Address, + amount: InputAmount, + ) -> args::TxTransparentTransfer { + args::TxTransparentTransfer { + source, + target, + token, + amount, + tx_code_path: PathBuf::from(TX_TRANSPARENT_TRANSFER_WASM), + tx: self.tx_builder(), + } + } + + /// Make a TxShieldedTransfer builder from the given minimum set of + /// arguments + fn new_shielded_transfer( + &self, + source: ExtendedSpendingKey, + target: PaymentAddress, token: Address, amount: InputAmount, - ) -> args::TxTransfer { - args::TxTransfer { + ) -> args::TxShieldedTransfer { + args::TxShieldedTransfer { source, target, token, amount, - tx_code_path: PathBuf::from(TX_TRANSFER_WASM), + tx_code_path: PathBuf::from(TX_SHIELDED_TRANSFER_WASM), + tx: self.tx_builder(), + } + } + + /// Make a TxShieldingTransfer builder from the given minimum set of + /// arguments + fn new_shielding_transfer( + &self, + source: Address, + target: PaymentAddress, + token: Address, + amount: InputAmount, + ) -> args::TxShieldingTransfer { + args::TxShieldingTransfer { + source, + target, + token, + amount, + tx_code_path: PathBuf::from(TX_SHIELDING_TRANSFER_WASM), + tx: self.tx_builder(), + } + } + + /// Make a TxUnshieldingTransfer builder from the given minimum set of + /// arguments + fn new_unshielding_transfer( + &self, + source: ExtendedSpendingKey, + target: Address, + token: Address, + amount: InputAmount, + ) -> args::TxUnshieldingTransfer { + args::TxUnshieldingTransfer { + source, + target, + token, + amount, + tx_code_path: PathBuf::from(TX_UNSHIELDING_TRANSFER_WASM), tx: self.tx_builder(), } } @@ -806,17 +863,21 @@ pub mod testing { use namada_core::address::testing::{ arb_established_address, arb_non_internal_address, }; - use namada_core::address::MASP; use namada_core::eth_bridge_pool::PendingTransfer; use namada_core::hash::testing::arb_hash; use namada_core::key::testing::arb_common_keypair; - use namada_core::token::testing::{arb_denominated_amount, arb_transfer}; - use namada_core::token::Transfer; use namada_governance::storage::proposal::testing::{ arb_init_proposal, arb_vote_proposal, }; use namada_governance::{InitProposalData, VoteProposalData}; use namada_ibc::testing::arb_ibc_any; + use namada_token::testing::{ + arb_denominated_amount, arb_transparent_transfer, + }; + use namada_token::{ + ShieldedTransfer, ShieldingTransfer, TransparentTransfer, + UnshieldingTransfer, + }; use namada_tx::data::pgf::UpdateStewardCommission; use namada_tx::data::pos::{ BecomeValidator, Bond, CommissionChange, ConsensusKeyChange, @@ -869,7 +930,10 @@ pub mod testing { UpdateAccount(UpdateAccount), VoteProposal(VoteProposalData), Withdraw(Withdraw), - Transfer(Transfer), + TransparentTransfer(TransparentTransfer), + ShieldedTransfer(ShieldedTransfer), + ShieldingTransfer(ShieldingTransfer), + UnshieldingTransfer(UnshieldingTransfer), Bond(Bond), Redelegation(Redelegation), UpdateStewardCommission(UpdateStewardCommission), @@ -1025,17 +1089,17 @@ pub mod testing { prop_compose! { /// Generate an arbitrary transfer transaction - pub fn arb_transfer_tx()( + pub fn arb_transparent_transfer_tx()( mut header in arb_header(), wrapper in arb_wrapper_tx(), - transfer in arb_transfer(), + transfer in arb_transparent_transfer(), code_hash in arb_hash(), ) -> (Tx, TxData) { header.tx_type = TxType::Wrapper(Box::new(wrapper)); let mut tx = Tx { header, sections: vec![] }; tx.add_data(transfer.clone()); - tx.add_code_from_hash(code_hash, Some(TX_TRANSFER_WASM.to_owned())); - (tx, TxData::Transfer(transfer)) + tx.add_code_from_hash(code_hash, Some(TX_TRANSPARENT_TRANSFER_WASM.to_owned())); + (tx, TxData::TransparentTransfer(transfer)) } } @@ -1057,56 +1121,60 @@ pub mod testing { Shielded, // Shielding transaction Shielding, - // Deshielding transaction - Deshielding, + // Unshielding transaction + Unshielding, } prop_compose! { /// Generate an arbitrary transfer transaction - pub fn arb_masp_transfer_tx()(transfer in arb_transfer())( + pub fn arb_masp_transfer_tx()(transfer in arb_transparent_transfer())( mut header in arb_header(), wrapper in arb_wrapper_tx(), code_hash in arb_hash(), (masp_tx_type, (shielded_transfer, asset_types)) in prop_oneof![ (Just(MaspTxType::Shielded), arb_shielded_transfer(0..MAX_ASSETS)), (Just(MaspTxType::Shielding), arb_shielding_transfer(encode_address(&transfer.source), 1)), - (Just(MaspTxType::Deshielding), arb_deshielding_transfer(encode_address(&transfer.target), 1)), + (Just(MaspTxType::Unshielding), arb_deshielding_transfer(encode_address(&transfer.target), 1)), ], - mut transfer in Just(transfer), + transfer in Just(transfer), ) -> (Tx, TxData) { header.tx_type = TxType::Wrapper(Box::new(wrapper)); let mut tx = Tx { header, sections: vec![] }; - match masp_tx_type { + let shielded_section_hash = tx.add_masp_tx_section(shielded_transfer.masp_tx).1; + let tx_data = match masp_tx_type { MaspTxType::Shielded => { - transfer.source = MASP; - transfer.target = MASP; - transfer.amount = token::Amount::zero().into(); + tx.add_code_from_hash(code_hash, Some(TX_SHIELDED_TRANSFER_WASM.to_owned())); + let data = ShieldedTransfer { section_hash: shielded_section_hash }; + tx.add_data(data.clone()); + TxData::ShieldedTransfer(data) }, MaspTxType::Shielding => { - transfer.target = MASP; // Set the transparent amount and token let (decoded, value) = asset_types.iter().next().unwrap(); - transfer.amount = DenominatedAmount::new( + let token = decoded.token.clone(); + let amount = DenominatedAmount::new( token::Amount::from_masp_denominated(*value, decoded.position), decoded.denom, ); - transfer.token = decoded.token.clone(); + tx.add_code_from_hash(code_hash, Some(TX_SHIELDING_TRANSFER_WASM.to_owned())); + let data = ShieldingTransfer {source: transfer.source, token, amount, shielded_section_hash }; + tx.add_data(data.clone()); + TxData::ShieldingTransfer(data) }, - MaspTxType::Deshielding => { - transfer.source = MASP; + MaspTxType::Unshielding => { // Set the transparent amount and token let (decoded, value) = asset_types.iter().next().unwrap(); - transfer.amount = DenominatedAmount::new( + let token = decoded.token.clone(); + let amount = DenominatedAmount::new( token::Amount::from_masp_denominated(*value, decoded.position), decoded.denom, ); - transfer.token = decoded.token.clone(); + tx.add_code_from_hash(code_hash, Some(TX_UNSHIELDING_TRANSFER_WASM.to_owned())); + let data = UnshieldingTransfer {target: transfer.target, token, amount, shielded_section_hash }; + tx.add_data(data.clone()); + TxData::UnshieldingTransfer(data) }, - } - let masp_tx_hash = tx.add_masp_tx_section(shielded_transfer.masp_tx).1; - transfer.shielded = Some(masp_tx_hash); - tx.add_data(transfer.clone()); - tx.add_code_from_hash(code_hash, Some(TX_TRANSFER_WASM.to_owned())); + }; tx.add_masp_builder(MaspBuilder { asset_types: asset_types.into_keys().collect(), // Store how the Info objects map to Descriptors/Outputs @@ -1114,9 +1182,9 @@ pub mod testing { // Store the data that was used to construct the Transaction builder: shielded_transfer.builder, // Link the Builder to the Transaction by hash code - target: masp_tx_hash, + target: shielded_section_hash, }); - (tx, TxData::Transfer(transfer)) + (tx, tx_data) } } @@ -1495,7 +1563,7 @@ pub mod testing { /// Generate an arbitrary tx pub fn arb_tx() -> impl Strategy { prop_oneof![ - arb_transfer_tx(), + arb_transparent_transfer_tx(), arb_masp_transfer_tx(), arb_bond_tx(), arb_unbond_tx(), diff --git a/crates/sdk/src/signing.rs b/crates/sdk/src/signing.rs index f6c5e4f840..a6e1ef5f44 100644 --- a/crates/sdk/src/signing.rs +++ b/crates/sdk/src/signing.rs @@ -17,15 +17,13 @@ use namada_core::collections::{HashMap, HashSet}; use namada_core::key::*; use namada_core::masp::{AssetData, ExtendedViewingKey, PaymentAddress}; use namada_core::sign::SignatureIndex; -use namada_core::token; -use namada_core::token::Transfer; -// use namada_core::storage::Key; use namada_core::token::{Amount, DenominatedAmount}; use namada_governance::storage::proposal::{ InitProposalData, ProposalType, VoteProposalData, }; use namada_governance::storage::vote::ProposalVote; use namada_parameters::storage as parameter_storage; +use namada_token as token; use namada_token::storage_key::balance_key; use namada_tx::data::pgf::UpdateStewardCommission; use namada_tx::data::pos::BecomeValidator; @@ -50,10 +48,11 @@ use crate::tx::{ TX_CHANGE_METADATA_WASM, TX_CLAIM_REWARDS_WASM, TX_DEACTIVATE_VALIDATOR_WASM, TX_IBC_WASM, TX_INIT_ACCOUNT_WASM, TX_INIT_PROPOSAL, TX_REACTIVATE_VALIDATOR_WASM, TX_REDELEGATE_WASM, - TX_RESIGN_STEWARD, TX_REVEAL_PK, TX_TRANSFER_WASM, TX_UNBOND_WASM, - TX_UNJAIL_VALIDATOR_WASM, TX_UPDATE_ACCOUNT_WASM, - TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL, TX_WITHDRAW_WASM, - VP_USER_WASM, + TX_RESIGN_STEWARD, TX_REVEAL_PK, TX_SHIELDED_TRANSFER_WASM, + TX_SHIELDING_TRANSFER_WASM, TX_TRANSPARENT_TRANSFER_WASM, TX_UNBOND_WASM, + TX_UNJAIL_VALIDATOR_WASM, TX_UNSHIELDING_TRANSFER_WASM, + TX_UPDATE_ACCOUNT_WASM, TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL, + TX_WITHDRAW_WASM, VP_USER_WASM, }; pub use crate::wallet::store::AddressVpType; use crate::wallet::{Wallet, WalletIo}; @@ -703,25 +702,61 @@ fn format_outputs(output: &mut Vec) { } } +enum TokenTransfer<'a> { + Transparent(&'a token::TransparentTransfer), + Shielded, + Shielding(&'a token::ShieldingTransfer), + Unshielding(&'a token::UnshieldingTransfer), +} + +impl TokenTransfer<'_> { + fn source(&self) -> Option<&Address> { + match self { + TokenTransfer::Transparent(transfer) => Some(&transfer.source), + TokenTransfer::Shielded => None, + TokenTransfer::Shielding(transfer) => Some(&transfer.source), + TokenTransfer::Unshielding(_) => None, + } + } + + fn target(&self) -> Option<&Address> { + match self { + TokenTransfer::Transparent(transfer) => Some(&transfer.target), + TokenTransfer::Shielded => None, + TokenTransfer::Shielding(_) => None, + TokenTransfer::Unshielding(transfer) => Some(&transfer.target), + } + } + + fn token_and_amount(&self) -> Option<(&Address, DenominatedAmount)> { + match self { + TokenTransfer::Transparent(transfer) => { + Some((&transfer.token, transfer.amount)) + } + TokenTransfer::Shielded => None, + TokenTransfer::Shielding(transfer) => { + Some((&transfer.token, transfer.amount)) + } + TokenTransfer::Unshielding(transfer) => { + Some((&transfer.token, transfer.amount)) + } + } + } +} + /// Adds a Ledger output for the sender and destination for transparent and MASP /// transactions -pub async fn make_ledger_masp_endpoints( +async fn make_ledger_token_transfer_endpoints( tokens: &HashMap, output: &mut Vec, - transfer: &Transfer, + transfer: TokenTransfer<'_>, builder: Option<&MaspBuilder>, assets: &HashMap, ) { - if transfer.source != MASP { - output.push(format!("Sender : {}", transfer.source)); - if transfer.target == MASP { - make_ledger_amount_addr( - tokens, - output, - transfer.amount, - &transfer.token, - "Sending ", - ); + if let Some(source) = transfer.source() { + output.push(format!("Sender : {}", source)); + if let Some((token, amount)) = transfer.token_and_amount() { + make_ledger_amount_addr(tokens, output, amount, token, "Sending "); } } else if let Some(builder) = builder { for sapling_input in builder.builder.sapling_inputs() { @@ -738,14 +773,14 @@ pub async fn make_ledger_masp_endpoints( .await; } } - if transfer.target != MASP { - output.push(format!("Destination : {}", transfer.target)); - if transfer.source == MASP { + if let Some(target) = transfer.target() { + output.push(format!("Destination : {}", target)); + if let Some((token, amount)) = transfer.token_and_amount() { make_ledger_amount_addr( tokens, output, - transfer.amount, - &transfer.token, + amount, + token, "Receiving ", ); } @@ -764,15 +799,6 @@ pub async fn make_ledger_masp_endpoints( .await; } } - if transfer.source != MASP && transfer.target != MASP { - make_ledger_amount_addr( - tokens, - output, - transfer.amount, - &transfer.token, - "", - ); - } } /// Convert decimal numbers into the format used by Ledger. Specifically remove @@ -1248,8 +1274,37 @@ pub async fn to_ledger_vector( HEXLOWER.encode(&extra_code_hash.0) )]); } - } else if code_sec.tag == Some(TX_TRANSFER_WASM.to_string()) { - let transfer = Transfer::try_from_slice( + } else if code_sec.tag == Some(TX_TRANSPARENT_TRANSFER_WASM.to_string()) + { + let transfer = token::TransparentTransfer::try_from_slice( + &tx.data(cmt) + .ok_or_else(|| Error::Other("Invalid Data".to_string()))?, + ) + .map_err(|err| { + Error::from(EncodingError::Conversion(err.to_string())) + })?; + + tv.name = "Transfer_0".to_string(); + + tv.output.push("Type : TransparentTransfer".to_string()); + make_ledger_token_transfer_endpoints( + &tokens, + &mut tv.output, + TokenTransfer::Transparent(&transfer), + None, + &HashMap::default(), + ) + .await; + make_ledger_token_transfer_endpoints( + &tokens, + &mut tv.output_expert, + TokenTransfer::Transparent(&transfer), + None, + &HashMap::default(), + ) + .await; + } else if code_sec.tag == Some(TX_SHIELDED_TRANSFER_WASM.to_string()) { + let transfer = token::ShieldedTransfer::try_from_slice( &tx.data(cmt) .ok_or_else(|| Error::Other("Invalid Data".to_string()))?, ) @@ -1258,43 +1313,134 @@ pub async fn to_ledger_vector( })?; // To facilitate lookups of MASP AssetTypes let mut asset_types = HashMap::new(); - let builder = if let Some(shielded_hash) = transfer.shielded { - tx.sections.iter().find_map(|x| match x { - Section::MaspBuilder(builder) - if builder.target == shielded_hash => - { - for decoded in &builder.asset_types { - match decoded.encode() { - Err(_) => None, - Ok(asset) => { - asset_types.insert(asset, decoded.clone()); - Some(builder) - } - }?; - } - Some(builder) + let builder = tx.sections.iter().find_map(|x| match x { + Section::MaspBuilder(builder) + if builder.target == transfer.section_hash => + { + for decoded in &builder.asset_types { + match decoded.encode() { + Err(_) => None, + Ok(asset) => { + asset_types.insert(asset, decoded.clone()); + Some(builder) + } + }?; } - _ => None, - }) - } else { - None - }; + Some(builder) + } + _ => None, + }); - tv.name = "Transfer_0".to_string(); + tv.name = "ShieldedTransfer_0".to_string(); + + tv.output.push("Type : ShieldedTransfer".to_string()); + make_ledger_token_transfer_endpoints( + &tokens, + &mut tv.output, + TokenTransfer::Shielded, + builder, + &asset_types, + ) + .await; + make_ledger_token_transfer_endpoints( + &tokens, + &mut tv.output_expert, + TokenTransfer::Shielded, + builder, + &asset_types, + ) + .await; + } else if code_sec.tag == Some(TX_SHIELDING_TRANSFER_WASM.to_string()) { + let transfer = token::ShieldingTransfer::try_from_slice( + &tx.data(cmt) + .ok_or_else(|| Error::Other("Invalid Data".to_string()))?, + ) + .map_err(|err| { + Error::from(EncodingError::Conversion(err.to_string())) + })?; + // To facilitate lookups of MASP AssetTypes + let mut asset_types = HashMap::new(); + let builder = tx.sections.iter().find_map(|x| match x { + Section::MaspBuilder(builder) + if builder.target == transfer.shielded_section_hash => + { + for decoded in &builder.asset_types { + match decoded.encode() { + Err(_) => None, + Ok(asset) => { + asset_types.insert(asset, decoded.clone()); + Some(builder) + } + }?; + } + Some(builder) + } + _ => None, + }); + + tv.name = "ShieldingTransfer_0".to_string(); + + tv.output.push("Type : ShieldingTransfer".to_string()); + make_ledger_token_transfer_endpoints( + &tokens, + &mut tv.output, + TokenTransfer::Shielding(&transfer), + builder, + &asset_types, + ) + .await; + make_ledger_token_transfer_endpoints( + &tokens, + &mut tv.output_expert, + TokenTransfer::Shielding(&transfer), + builder, + &asset_types, + ) + .await; + } else if code_sec.tag == Some(TX_UNSHIELDING_TRANSFER_WASM.to_string()) + { + let transfer = token::UnshieldingTransfer::try_from_slice( + &tx.data(cmt) + .ok_or_else(|| Error::Other("Invalid Data".to_string()))?, + ) + .map_err(|err| { + Error::from(EncodingError::Conversion(err.to_string())) + })?; + // To facilitate lookups of MASP AssetTypes + let mut asset_types = HashMap::new(); + let builder = tx.sections.iter().find_map(|x| match x { + Section::MaspBuilder(builder) + if builder.target == transfer.shielded_section_hash => + { + for decoded in &builder.asset_types { + match decoded.encode() { + Err(_) => None, + Ok(asset) => { + asset_types.insert(asset, decoded.clone()); + Some(builder) + } + }?; + } + Some(builder) + } + _ => None, + }); + + tv.name = "UnshieldingTransfer_0".to_string(); - tv.output.push("Type : Transfer".to_string()); - make_ledger_masp_endpoints( + tv.output.push("Type : UnshieldingTransfer".to_string()); + make_ledger_token_transfer_endpoints( &tokens, &mut tv.output, - &transfer, + TokenTransfer::Unshielding(&transfer), builder, &asset_types, ) .await; - make_ledger_masp_endpoints( + make_ledger_token_transfer_endpoints( &tokens, &mut tv.output_expert, - &transfer, + TokenTransfer::Unshielding(&transfer), builder, &asset_types, ) diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index 5dc90cec70..436fdfeb46 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -35,14 +35,13 @@ use namada_core::ibc::core::channel::types::timeout::TimeoutHeight; use namada_core::ibc::core::client::types::Height as IbcHeight; use namada_core::ibc::core::host::types::identifiers::{ChannelId, PortId}; use namada_core::ibc::primitives::Timestamp as IbcTimestamp; -use namada_core::ibc::{is_nft_trace, MsgNftTransfer, MsgTransfer}; use namada_core::key::{self, *}; use namada_core::masp::{ AssetData, PaymentAddress, TransferSource, TransferTarget, }; +use namada_core::storage; use namada_core::storage::Epoch; use namada_core::time::DateTimeUtc; -use namada_core::{storage, token}; use namada_governance::cli::onchain::{ DefaultProposal, OnChainProposal, PgfFundingProposal, PgfStewardProposal, }; @@ -52,10 +51,12 @@ use namada_governance::storage::proposal::{ }; use namada_governance::storage::vote::ProposalVote; use namada_ibc::storage::{channel_key, ibc_token}; +use namada_ibc::{is_nft_trace, MsgNftTransfer, MsgTransfer}; use namada_proof_of_stake::parameters::{ PosParams, MAX_VALIDATOR_METADATA_LEN, }; use namada_proof_of_stake::types::{CommissionPair, ValidatorState}; +use namada_token as token; use namada_token::storage_key::balance_key; use namada_token::DenominatedAmount; use namada_tx::data::pgf::UpdateStewardCommission; @@ -101,8 +102,14 @@ pub const TX_VOTE_PROPOSAL: &str = "tx_vote_proposal.wasm"; pub const TX_REVEAL_PK: &str = "tx_reveal_pk.wasm"; /// Update validity predicate WASM path pub const TX_UPDATE_ACCOUNT_WASM: &str = "tx_update_account.wasm"; -/// Transfer transaction WASM path -pub const TX_TRANSFER_WASM: &str = "tx_transfer.wasm"; +/// Transparent transfer transaction WASM path +pub const TX_TRANSPARENT_TRANSFER_WASM: &str = "tx_transparent_transfer.wasm"; +/// Shielded transfer transaction WASM path +pub const TX_SHIELDED_TRANSFER_WASM: &str = "tx_shielded_transfer.wasm"; +/// Shielding transfer transaction WASM path +pub const TX_SHIELDING_TRANSFER_WASM: &str = "tx_shielding_transfer.wasm"; +/// Unshielding transfer transaction WASM path +pub const TX_UNSHIELDING_TRANSFER_WASM: &str = "tx_unshielding_transfer.wasm"; /// IBC transaction WASM path pub const TX_IBC_WASM: &str = "tx_ibc.wasm"; /// User validity predicate WASM path @@ -2560,14 +2567,13 @@ pub async fn build_ibc_transfer( let transfer = shielded_parts.map(|(shielded_transfer, asset_types)| { let masp_tx_hash = tx.add_masp_tx_section(shielded_transfer.masp_tx.clone()).1; - let transfer = token::Transfer { - source: source.clone(), + let transfer = token::UnshieldingTransfer { // The token will be escrowed to IBC address target: Address::Internal(InternalAddress::Ibc), token: args.token.clone(), amount: validated_amount, // Link the Transfer to the MASP Transaction by hash code - shielded: Some(masp_tx_hash), + shielded_section_hash: masp_tx_hash, }; tx.add_masp_builder(MaspBuilder { asset_types, @@ -2820,62 +2826,54 @@ pub fn build_batch( Ok((batched_tx, signing_data)) } -/// Submit an ordinary transfer -pub async fn build_transfer( +/// Build a transparent transfer +pub async fn build_transparent_transfer( context: &N, - args: &mut args::TxTransfer, -) -> Result<(Tx, SigningTxData, Option)> { - let default_signer = Some(args.source.effective_address()); + args: &mut args::TxTransparentTransfer, +) -> Result<(Tx, SigningTxData)> { + let source = &args.source; + let target = &args.target; + + let default_signer = Some(source.clone()); let signing_data = signing::aux_signing_data( context, &args.tx, - Some(args.source.effective_address()), + Some(source.clone()), default_signer, ) .await?; + // Transparent fee payment let (fee_amount, updated_balance) = - if let TransferSource::ExtendedSpendingKey(_) = args.source { - // MASP fee payment - (validate_fee(context, &args.tx).await?, None) - } else { - // Transparent fee payment - validate_transparent_fee(context, &args.tx, &signing_data.fee_payer) - .await - .map(|(fee_amount, updated_balance)| { - (fee_amount, Some(updated_balance)) - })? - }; - - // TODO(namada#2596): need multiple source/targets here for masp fee payment - // targets or leave the fees on the MASP balance - let source = args.source.effective_address(); - let target = args.target.effective_address(); + validate_transparent_fee(context, &args.tx, &signing_data.fee_payer) + .await + .map(|(fee_amount, updated_balance)| { + (fee_amount, Some(updated_balance)) + })?; // Check that the source address exists on chain source_exists_or_err(source.clone(), args.tx.force, context).await?; // Check that the target address exists on chain target_exists_or_err(target.clone(), args.tx.force, context).await?; - // validate the amount given + // Validate the amount given let validated_amount = validate_amount(context, args.amount, &args.token, args.tx.force) .await?; - // If source is transparent check the balance (MASP balance is checked when - // constructing the shielded part) + // Check the balance of the source if let Some(updated_balance) = updated_balance { - let check_balance = if updated_balance.source == source + let check_balance = if &updated_balance.source == source && updated_balance.token == args.token { CheckBalance::Balance(updated_balance.post_balance) } else { - CheckBalance::Query(balance_key(&args.token, &source)) + CheckBalance::Query(balance_key(&args.token, source)) }; check_balance_too_low_err( &args.token, - &source, + source, validated_amount.amount(), check_balance, args.tx.force, @@ -2884,60 +2882,274 @@ pub async fn build_transfer( .await?; } - let masp_addr = MASP; + // Construct the corresponding transparent Transfer object + let transfer = token::TransparentTransfer { + source: source.clone(), + target: target.clone(), + token: args.token.clone(), + amount: validated_amount, + }; + + let tx = build_pow_flag( + context, + &args.tx, + args.tx_code_path.clone(), + transfer, + do_nothing, + fee_amount, + &signing_data.fee_payer, + ) + .await?; + Ok((tx, signing_data)) +} + +/// Build a shielded transfer +pub async fn build_shielded_transfer( + context: &N, + args: &mut args::TxShieldedTransfer, +) -> Result<(Tx, SigningTxData, Epoch)> { + let default_signer = Some(MASP); + let signing_data = signing::aux_signing_data( + context, + &args.tx, + Some(MASP), + default_signer, + ) + .await?; + + // Shielded fee payment + let fee_amount = validate_fee(context, &args.tx).await?; + + // Validate the amount given + let validated_amount = + validate_amount(context, args.amount, &args.token, args.tx.force) + .await?; + + // TODO(namada#2597): this function should also take another arg as the fees + // token and amount + let shielded_parts = construct_shielded_parts( + context, + &TransferSource::ExtendedSpendingKey(args.source), + &TransferTarget::PaymentAddress(args.target), + &args.token, + validated_amount, + !(args.tx.dry_run || args.tx.dry_run_wrapper), + ) + .await? + .expect("Shielded transfer must have shielded parts"); + let shielded_tx_epoch = shielded_parts.0.epoch; + + let add_shielded_parts = + |tx: &mut Tx, data: &mut token::ShieldedTransfer| { + // Add the MASP Transaction and its Builder to facilitate validation + let ( + ShieldedTransfer { + builder, + masp_tx, + metadata, + epoch: _, + }, + asset_types, + ) = shielded_parts; + // Add a MASP Transaction section to the Tx and get the tx hash + let section_hash = tx.add_masp_tx_section(masp_tx).1; + + tx.add_masp_builder(MaspBuilder { + asset_types, + // Store how the Info objects map to Descriptors/Outputs + metadata, + // Store the data that was used to construct the Transaction + builder, + // Link the Builder to the Transaction by hash code + target: section_hash, + }); + + data.section_hash = section_hash; + tracing::debug!("Transfer data {data:?}"); + Ok(()) + }; + + // Construct the tx data with a placeholder shielded section hash + let data = token::ShieldedTransfer { + section_hash: Hash::zero(), + }; + let tx = build_pow_flag( + context, + &args.tx, + args.tx_code_path.clone(), + data, + add_shielded_parts, + fee_amount, + &signing_data.fee_payer, + ) + .await?; + Ok((tx, signing_data, shielded_tx_epoch)) +} - // If the transaction is shielded, redact the amount and token - // types by setting the transparent value to 0 and token type to a constant. - // This has no side-effect because transaction is to self. - let (transparent_amount, transparent_token) = - if source == masp_addr && target == masp_addr { - // TODO(namada#1677): Refactor me, we shouldn't rely on any specific - // token here. - (token::Amount::zero().into(), context.native_token()) +/// Build a shielding transfer +pub async fn build_shielding_transfer( + context: &N, + args: &mut args::TxShieldingTransfer, +) -> Result<(Tx, SigningTxData, Epoch)> { + let source = &args.source; + let default_signer = Some(source.clone()); + let signing_data = signing::aux_signing_data( + context, + &args.tx, + Some(source.clone()), + default_signer, + ) + .await?; + + // Transparent fee payment + let (fee_amount, updated_balance) = + validate_transparent_fee(context, &args.tx, &signing_data.fee_payer) + .await + .map(|(fee_amount, updated_balance)| { + (fee_amount, Some(updated_balance)) + })?; + + // Validate the amount given + let validated_amount = + validate_amount(context, args.amount, &args.token, args.tx.force) + .await?; + + // Check the balance of the source + if let Some(updated_balance) = updated_balance { + let check_balance = if &updated_balance.source == source + && updated_balance.token == args.token + { + CheckBalance::Balance(updated_balance.post_balance) } else { - (validated_amount, args.token.clone()) + CheckBalance::Query(balance_key(&args.token, source)) }; + check_balance_too_low_err( + &args.token, + source, + validated_amount.amount(), + check_balance, + args.tx.force, + context, + ) + .await?; + } + // TODO(namada#2597): this function should also take another arg as the fees // token and amount let shielded_parts = construct_shielded_parts( context, - &args.source, - &args.target, + &TransferSource::Address(source.clone()), + &TransferTarget::PaymentAddress(args.target), &args.token, validated_amount, !(args.tx.dry_run || args.tx.dry_run_wrapper), ) - .await?; - let shielded_tx_epoch = shielded_parts.as_ref().map(|trans| trans.0.epoch); + .await? + .expect("Shielding transfer must have shielded parts"); + let shielded_tx_epoch = shielded_parts.0.epoch; + + let add_shielded_parts = + |tx: &mut Tx, data: &mut token::ShieldingTransfer| { + // Add the MASP Transaction and its Builder to facilitate validation + let ( + ShieldedTransfer { + builder, + masp_tx, + metadata, + epoch: _, + }, + asset_types, + ) = shielded_parts; + // Add a MASP Transaction section to the Tx and get the tx hash + let shielded_section_hash = tx.add_masp_tx_section(masp_tx).1; - // Construct the corresponding transparent Transfer object - let transfer = token::Transfer { + tx.add_masp_builder(MaspBuilder { + asset_types, + // Store how the Info objects map to Descriptors/Outputs + metadata, + // Store the data that was used to construct the Transaction + builder, + // Link the Builder to the Transaction by hash code + target: shielded_section_hash, + }); + + data.shielded_section_hash = shielded_section_hash; + tracing::debug!("Transfer data {data:?}"); + Ok(()) + }; + + // Construct the tx data with a placeholder shielded section hash + let data = token::ShieldingTransfer { source: source.clone(), - target: target.clone(), - token: transparent_token.clone(), - amount: transparent_amount, - // Link the Transfer to the MASP Transaction by hash code - shielded: None, + token: args.token.clone(), + amount: validated_amount, + shielded_section_hash: Hash::zero(), }; - let add_shielded = |tx: &mut Tx, transfer: &mut token::Transfer| { - // Add the MASP Transaction and its Builder to facilitate validation - if let Some(( - ShieldedTransfer { - builder, - masp_tx, - metadata, - epoch: _, - }, - asset_types, - )) = shielded_parts - { - // Add a MASP Transaction section to the Tx and get the tx hash - let masp_tx_hash = tx.add_masp_tx_section(masp_tx).1; - transfer.shielded = Some(masp_tx_hash); + let tx = build_pow_flag( + context, + &args.tx, + args.tx_code_path.clone(), + data, + add_shielded_parts, + fee_amount, + &signing_data.fee_payer, + ) + .await?; + Ok((tx, signing_data, shielded_tx_epoch)) +} - tracing::debug!("Transfer data {:?}", transfer); +/// Build an unshielding transfer +pub async fn build_unshielding_transfer( + context: &N, + args: &mut args::TxUnshieldingTransfer, +) -> Result<(Tx, SigningTxData, Epoch)> { + let default_signer = Some(MASP); + let signing_data = signing::aux_signing_data( + context, + &args.tx, + Some(MASP), + default_signer, + ) + .await?; + + // Shielded fee payment + let fee_amount = validate_fee(context, &args.tx).await?; + + // Validate the amount given + let validated_amount = + validate_amount(context, args.amount, &args.token, args.tx.force) + .await?; + + // TODO(namada#2597): this function should also take another arg as the fees + // token and amount + let shielded_parts = construct_shielded_parts( + context, + &TransferSource::ExtendedSpendingKey(args.source), + &TransferTarget::Address(args.target.clone()), + &args.token, + validated_amount, + !(args.tx.dry_run || args.tx.dry_run_wrapper), + ) + .await? + .expect("Shielding transfer must have shielded parts"); + let shielded_tx_epoch = shielded_parts.0.epoch; + + let add_shielded_parts = + |tx: &mut Tx, data: &mut token::UnshieldingTransfer| { + // Add the MASP Transaction and its Builder to facilitate validation + let ( + ShieldedTransfer { + builder, + masp_tx, + metadata, + epoch: _, + }, + asset_types, + ) = shielded_parts; + // Add a MASP Transaction section to the Tx and get the tx hash + let shielded_section_hash = tx.add_masp_tx_section(masp_tx).1; tx.add_masp_builder(MaspBuilder { asset_types, @@ -2946,17 +3158,27 @@ pub async fn build_transfer( // Store the data that was used to construct the Transaction builder, // Link the Builder to the Transaction by hash code - target: masp_tx_hash, + target: shielded_section_hash, }); + + data.shielded_section_hash = shielded_section_hash; + tracing::debug!("Transfer data {data:?}"); + Ok(()) }; - Ok(()) + + // Construct the tx data with a placeholder shielded section hash + let data = token::UnshieldingTransfer { + target: args.target.clone(), + token: args.token.clone(), + amount: validated_amount, + shielded_section_hash: Hash::zero(), }; let tx = build_pow_flag( context, &args.tx, args.tx_code_path.clone(), - transfer, - add_shielded, + data, + add_shielded_parts, fee_amount, &signing_data.fee_payer, ) @@ -3265,10 +3487,10 @@ pub async fn build_custom( } /// Generate IBC shielded transfer -pub async fn gen_ibc_shielded_transfer( +pub async fn gen_ibc_shielding_transfer( context: &N, args: args::GenIbcShieldedTransfer, -) -> Result> { +) -> Result> { let source = Address::Internal(InternalAddress::Ibc); let (src_port_id, src_channel_id) = get_ibc_src_port_channel(context, &args.port_id, &args.channel_id) @@ -3323,12 +3545,11 @@ pub async fn gen_ibc_shielded_transfer( if let Some(shielded_transfer) = shielded_transfer { let masp_tx_hash = Section::MaspTx(shielded_transfer.masp_tx.clone()).get_hash(); - let transfer = token::Transfer { + let transfer = token::ShieldingTransfer { source: source.clone(), - target: MASP, token: token.clone(), amount: validated_amount, - shielded: Some(masp_tx_hash), + shielded_section_hash: masp_tx_hash, }; Ok(Some((transfer, shielded_transfer.masp_tx))) } else { diff --git a/crates/sdk/src/wallet/mod.rs b/crates/sdk/src/wallet/mod.rs index 01db64feb8..a664c0fbe4 100644 --- a/crates/sdk/src/wallet/mod.rs +++ b/crates/sdk/src/wallet/mod.rs @@ -16,11 +16,11 @@ use bip39::{Language, Mnemonic, MnemonicType, Seed}; use borsh::{BorshDeserialize, BorshSerialize}; use namada_core::address::Address; use namada_core::collections::{HashMap, HashSet}; -use namada_core::ibc::is_ibc_denom; use namada_core::key::*; use namada_core::masp::{ ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress, }; +use namada_ibc::is_ibc_denom; pub use pre_genesis::gen_key_to_store; use rand::CryptoRng; use rand_core::RngCore; From 45031d39fb3bb33f0c8b70423f3dde72c673d4fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:22:31 +0200 Subject: [PATCH 07/26] namada: update ibc path imports --- crates/namada/src/ledger/native_vp/ibc/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/namada/src/ledger/native_vp/ibc/mod.rs b/crates/namada/src/ledger/native_vp/ibc/mod.rs index a0e1f41bbd..485b4ea81c 100644 --- a/crates/namada/src/ledger/native_vp/ibc/mod.rs +++ b/crates/namada/src/ledger/native_vp/ibc/mod.rs @@ -419,7 +419,6 @@ mod tests { use crate::core::address::testing::{ established_address_1, established_address_2, nam, }; - use crate::core::ibc::{MsgNftTransfer, MsgTransfer}; use crate::core::storage::Epoch; use crate::ibc::apps::nft_transfer::types::events::{ RecvEvent as NftRecvEvent, TokenTraceEvent, @@ -499,7 +498,7 @@ mod tests { next_sequence_recv_key, next_sequence_send_key, nft_class_key, nft_metadata_key, receipt_key, }; - use crate::ibc::{NftClass, NftMetadata}; + use crate::ibc::{MsgNftTransfer, MsgTransfer, NftClass, NftMetadata}; use crate::key::testing::keypair_1; use crate::ledger::gas::VpGasMeter; use crate::ledger::parameters::storage::{ From b9f0a985371e8460d38042b22f45cece7a556d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:22:41 +0200 Subject: [PATCH 08/26] namada/protocol: rm dead code --- crates/namada/src/ledger/protocol/mod.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/crates/namada/src/ledger/protocol/mod.rs b/crates/namada/src/ledger/protocol/mod.rs index 4b74de75ca..37a27760a4 100644 --- a/crates/namada/src/ledger/protocol/mod.rs +++ b/crates/namada/src/ledger/protocol/mod.rs @@ -7,13 +7,11 @@ use borsh_ext::BorshSerializeExt; use eyre::{eyre, WrapErr}; use namada_core::booleans::BoolResultUnitExt; use namada_core::hash::Hash; -use namada_core::storage::Key; use namada_events::extend::{ ComposeEvent, Height as HeightAttr, TxHash as TxHashAttr, }; use namada_events::EventLevel; use namada_gas::TxGasMeter; -use namada_sdk::tx::TX_TRANSFER_WASM; use namada_state::StorageWrite; use namada_token::event::{TokenEvent, TokenOperation, UserAccount}; use namada_tx::data::protocol::ProtocolTxType; @@ -363,22 +361,6 @@ where Ok(extended_tx_result) } -/// Load the wasm hash for a transfer from storage. -/// -/// # Panics -/// If the transaction hash is not found in storage -pub fn get_transfer_hash_from_storage(storage: &S) -> Hash -where - S: StorageRead, -{ - let transfer_code_name_key = - Key::wasm_code_name(TX_TRANSFER_WASM.to_string()); - storage - .read(&transfer_code_name_key) - .expect("Could not read the storage") - .expect("Expected tx transfer hash in storage") -} - /// Performs the required operation on a wrapper transaction: /// - replay protection /// - fee payment From 898b502f6996d406476f46f110f9c4f562f2b508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:23:13 +0200 Subject: [PATCH 09/26] light_sdk: update for split-up transfer tx --- crates/light_sdk/src/transaction/transfer.rs | 79 +++++++++++++++++--- 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/crates/light_sdk/src/transaction/transfer.rs b/crates/light_sdk/src/transaction/transfer.rs index 3275447ac7..84475660ea 100644 --- a/crates/light_sdk/src/transaction/transfer.rs +++ b/crates/light_sdk/src/transaction/transfer.rs @@ -1,43 +1,98 @@ -use borsh_ext::BorshSerializeExt; use namada_sdk::address::Address; use namada_sdk::hash::Hash; use namada_sdk::key::common; use namada_sdk::token::DenominatedAmount; use namada_sdk::tx::data::GasLimit; -use namada_sdk::tx::{Authorization, Tx, TxError}; +use namada_sdk::tx::{ + Authorization, Tx, TxError, TX_SHIELDED_TRANSFER_WASM, + TX_SHIELDING_TRANSFER_WASM, TX_TRANSPARENT_TRANSFER_WASM, + TX_UNSHIELDING_TRANSFER_WASM, +}; use super::{attach_fee, attach_fee_signature, GlobalArgs}; use crate::transaction; -const TX_TRANSFER_WASM: &str = "tx_transfer.wasm"; - /// A transfer transaction #[derive(Debug, Clone)] pub struct Transfer(Tx); impl Transfer { - /// Build a raw Transfer transaction from the given parameters - pub fn new( + /// Build a transparent transfer transaction from the given parameters + pub fn transparent( source: Address, target: Address, token: Address, amount: DenominatedAmount, - // TODO(namada#2596): handle masp here - shielded: Option, args: GlobalArgs, ) -> Self { - let init_proposal = namada_sdk::token::Transfer { + let data = namada_sdk::token::TransparentTransfer { + source, + target, + token, + amount, + }; + + Self(transaction::build_tx( + args, + data, + TX_TRANSPARENT_TRANSFER_WASM.to_string(), + )) + } + + /// Build a shielded transfer transaction from the given parameters + pub fn shielded(shielded_section_hash: Hash, args: GlobalArgs) -> Self { + let data = namada_sdk::token::ShieldedTransfer { + section_hash: shielded_section_hash, + }; + + Self(transaction::build_tx( + args, + data, + TX_SHIELDED_TRANSFER_WASM.to_string(), + )) + } + + /// Build a shielding transfer transaction from the given parameters + pub fn shielding( + source: Address, + token: Address, + amount: DenominatedAmount, + shielded_section_hash: Hash, + args: GlobalArgs, + ) -> Self { + let data = namada_sdk::token::ShieldingTransfer { source, + token, + amount, + shielded_section_hash, + }; + + Self(transaction::build_tx( + args, + data, + TX_SHIELDING_TRANSFER_WASM.to_string(), + )) + } + + /// Build an unshielding transfer transaction from the given parameters + pub fn unshielding( + target: Address, + token: Address, + amount: DenominatedAmount, + shielded_section_hash: Hash, + args: GlobalArgs, + ) -> Self { + let data = namada_sdk::token::UnshieldingTransfer { target, token, amount, - shielded, + shielded_section_hash, }; Self(transaction::build_tx( args, - init_proposal.serialize_to_vec(), - TX_TRANSFER_WASM.to_string(), + data, + TX_UNSHIELDING_TRANSFER_WASM.to_string(), )) } From d3a95c976f2c71a5b137bb069419eed20b373f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:23:30 +0200 Subject: [PATCH 10/26] apps_lib: update for split-up transfer tx --- crates/apps_lib/src/cli.rs | 352 ++++++++++++++++++++++++++--- crates/apps_lib/src/cli/client.rs | 40 +++- crates/apps_lib/src/cli/context.rs | 2 +- crates/apps_lib/src/client/tx.rs | 119 ++++++++-- 4 files changed, 462 insertions(+), 51 deletions(-) diff --git a/crates/apps_lib/src/cli.rs b/crates/apps_lib/src/cli.rs index bef722d042..d0e6b71b8c 100644 --- a/crates/apps_lib/src/cli.rs +++ b/crates/apps_lib/src/cli.rs @@ -57,7 +57,10 @@ pub mod cmds { // Inlined commands from the client. TxCustom(TxCustom), - TxTransfer(TxTransfer), + TxTransparentTransfer(TxTransparentTransfer), + TxShieldedTransfer(TxShieldedTransfer), + TxShieldingTransfer(TxShieldingTransfer), + TxUnshieldingTransfer(TxUnshieldingTransfer), TxIbcTransfer(TxIbcTransfer), TxUpdateAccount(TxUpdateAccount), TxInitProposal(TxInitProposal), @@ -74,7 +77,10 @@ pub mod cmds { .subcommand(EthBridgePool::def()) .subcommand(Ledger::def()) .subcommand(TxCustom::def()) - .subcommand(TxTransfer::def()) + .subcommand(TxTransparentTransfer::def()) + .subcommand(TxShieldedTransfer::def()) + .subcommand(TxShieldingTransfer::def()) + .subcommand(TxUnshieldingTransfer::def()) .subcommand(TxIbcTransfer::def()) .subcommand(TxUpdateAccount::def()) .subcommand(TxInitProposal::def()) @@ -91,7 +97,14 @@ pub mod cmds { let wallet = SubCmd::parse(matches).map(Self::Wallet); let ledger = SubCmd::parse(matches).map(Self::Ledger); let tx_custom = SubCmd::parse(matches).map(Self::TxCustom); - let tx_transfer = SubCmd::parse(matches).map(Self::TxTransfer); + let tx_transparent_transfer = + SubCmd::parse(matches).map(Self::TxTransparentTransfer); + let tx_shielded_transfer = + SubCmd::parse(matches).map(Self::TxShieldedTransfer); + let tx_shielding_transfer = + SubCmd::parse(matches).map(Self::TxShieldingTransfer); + let tx_unshielding_transfer = + SubCmd::parse(matches).map(Self::TxUnshieldingTransfer); let tx_ibc_transfer = SubCmd::parse(matches).map(Self::TxIbcTransfer); let tx_update_account = @@ -107,7 +120,10 @@ pub mod cmds { .or(wallet) .or(ledger) .or(tx_custom) - .or(tx_transfer) + .or(tx_transparent_transfer) + .or(tx_shielded_transfer) + .or(tx_shielding_transfer) + .or(tx_unshielding_transfer) .or(tx_ibc_transfer) .or(tx_update_account) .or(tx_init_proposal) @@ -218,7 +234,10 @@ pub mod cmds { app // Simple transactions .subcommand(TxCustom::def().display_order(1)) - .subcommand(TxTransfer::def().display_order(1)) + .subcommand(TxTransparentTransfer::def().display_order(1)) + .subcommand(TxShieldedTransfer::def().display_order(1)) + .subcommand(TxShieldingTransfer::def().display_order(1)) + .subcommand(TxUnshieldingTransfer::def().display_order(1)) .subcommand(TxIbcTransfer::def().display_order(1)) .subcommand(TxUpdateAccount::def().display_order(1)) .subcommand(TxInitAccount::def().display_order(1)) @@ -280,7 +299,14 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { use NamadaClientWithContext::*; let tx_custom = Self::parse_with_ctx(matches, TxCustom); - let tx_transfer = Self::parse_with_ctx(matches, TxTransfer); + let tx_transparent_transfer = + Self::parse_with_ctx(matches, TxTransparentTransfer); + let tx_shielded_transfer = + Self::parse_with_ctx(matches, TxShieldedTransfer); + let tx_shielding_transfer = + Self::parse_with_ctx(matches, TxShieldingTransfer); + let tx_unshielding_transfer = + Self::parse_with_ctx(matches, TxUnshieldingTransfer); let tx_ibc_transfer = Self::parse_with_ctx(matches, TxIbcTransfer); let tx_update_account = Self::parse_with_ctx(matches, TxUpdateAccount); @@ -356,7 +382,10 @@ pub mod cmds { let shielded_sync = Self::parse_with_ctx(matches, ShieldedSync); let utils = SubCmd::parse(matches).map(Self::WithoutContext); tx_custom - .or(tx_transfer) + .or(tx_transparent_transfer) + .or(tx_shielded_transfer) + .or(tx_shielding_transfer) + .or(tx_unshielding_transfer) .or(tx_ibc_transfer) .or(tx_update_account) .or(tx_init_account) @@ -443,7 +472,10 @@ pub mod cmds { pub enum NamadaClientWithContext { // Ledger cmds TxCustom(TxCustom), - TxTransfer(TxTransfer), + TxTransparentTransfer(TxTransparentTransfer), + TxShieldedTransfer(TxShieldedTransfer), + TxShieldingTransfer(TxShieldingTransfer), + TxUnshieldingTransfer(TxUnshieldingTransfer), TxIbcTransfer(TxIbcTransfer), QueryResult(QueryResult), TxUpdateAccount(TxUpdateAccount), @@ -1246,21 +1278,90 @@ pub mod cmds { } #[derive(Clone, Debug)] - pub struct TxTransfer(pub args::TxTransfer); + pub struct TxTransparentTransfer( + pub args::TxTransparentTransfer, + ); + + impl SubCmd for TxTransparentTransfer { + const CMD: &'static str = "transparent-transfer"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + TxTransparentTransfer(args::TxTransparentTransfer::parse( + matches, + )) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about(wrap!("Send a transparent transfer transaction.")) + .add_args::>() + } + } + + #[derive(Clone, Debug)] + pub struct TxShieldedTransfer( + pub args::TxShieldedTransfer, + ); - impl SubCmd for TxTransfer { + impl SubCmd for TxShieldedTransfer { const CMD: &'static str = "transfer"; fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .map(|matches| TxTransfer(args::TxTransfer::parse(matches))) + matches.subcommand_matches(Self::CMD).map(|matches| { + TxShieldedTransfer(args::TxShieldedTransfer::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about(wrap!("Send a shielded transfer transaction (from a shielded address to a shielded address).")) + .add_args::>() + } + } + + #[derive(Clone, Debug)] + pub struct TxShieldingTransfer( + pub args::TxShieldingTransfer, + ); + + impl SubCmd for TxShieldingTransfer { + const CMD: &'static str = "shield"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + TxShieldingTransfer(args::TxShieldingTransfer::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about(wrap!("Send a shielding transfer transaction (from a transparent address to a shielded address).")) + .add_args::>() + } + } + + #[derive(Clone, Debug)] + pub struct TxUnshieldingTransfer( + pub args::TxUnshieldingTransfer, + ); + + impl SubCmd for TxUnshieldingTransfer { + const CMD: &'static str = "unshield"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + TxUnshieldingTransfer(args::TxUnshieldingTransfer::parse( + matches, + )) + }) } fn def() -> App { App::new(Self::CMD) - .about(wrap!("Send a signed transfer transaction.")) - .add_args::>() + .about(wrap!("Send an unshielding transfer transaction (from a shielded address to a transparent address).")) + .add_args::>() } } @@ -2993,10 +3094,11 @@ pub mod args { TX_CHANGE_METADATA_WASM, TX_CLAIM_REWARDS_WASM, TX_DEACTIVATE_VALIDATOR_WASM, TX_IBC_WASM, TX_INIT_ACCOUNT_WASM, TX_INIT_PROPOSAL, TX_REACTIVATE_VALIDATOR_WASM, TX_REDELEGATE_WASM, - TX_RESIGN_STEWARD, TX_REVEAL_PK, TX_TRANSFER_WASM, TX_UNBOND_WASM, - TX_UNJAIL_VALIDATOR_WASM, TX_UPDATE_ACCOUNT_WASM, - TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL, TX_WITHDRAW_WASM, - VP_USER_WASM, + TX_RESIGN_STEWARD, TX_REVEAL_PK, TX_SHIELDED_TRANSFER_WASM, + TX_SHIELDING_TRANSFER_WASM, TX_TRANSPARENT_TRANSFER_WASM, + TX_UNBOND_WASM, TX_UNJAIL_VALIDATOR_WASM, TX_UNSHIELDING_TRANSFER_WASM, + TX_UPDATE_ACCOUNT_WASM, TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL, + TX_WITHDRAW_WASM, VP_USER_WASM, }; use namada_sdk::DEFAULT_GAS_LIMIT; @@ -3177,6 +3279,7 @@ pub mod args { pub const OWNER: Arg = arg("owner"); pub const OWNER_OPT: ArgOpt = OWNER.opt(); pub const PATH: Arg = arg("path"); + pub const PAYMENT_ADDRESS_TARGET: Arg = arg("target"); pub const PORT_ID: ArgDefault = arg_default( "port-id", DefaultFn(|| PortId::from_str("transfer").unwrap()), @@ -3227,12 +3330,14 @@ pub mod args { pub const SIGNATURES: ArgMulti = arg_multi("signatures"); pub const SOURCE: Arg = arg("source"); pub const SOURCE_OPT: ArgOpt = SOURCE.opt(); + pub const SOURCE_VALIDATOR: Arg = arg("source-validator"); + pub const SPENDING_KEY_SOURCE: Arg = arg("source"); pub const SPENDING_KEYS: ArgMulti = arg_multi("spending-keys"); pub const STEWARD: Arg = arg("steward"); - pub const SOURCE_VALIDATOR: Arg = arg("source-validator"); pub const STORAGE_KEY: Arg = arg("storage-key"); pub const SUSPEND_ACTION: ArgFlag = flag("suspend"); + pub const TARGET: Arg = arg("target"); pub const TEMPLATES_PATH: Arg = arg("templates-path"); pub const TIMEOUT_HEIGHT: ArgOpt = arg_opt("timeout-height"); pub const TIMEOUT_SEC_OFFSET: ArgOpt = arg_opt("timeout-sec-offset"); @@ -4175,19 +4280,21 @@ pub mod args { } } - impl CliToSdk> for TxTransfer { + impl CliToSdk> + for TxTransparentTransfer + { type Error = std::io::Error; fn to_sdk( self, ctx: &mut Context, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { let tx = self.tx.to_sdk(ctx)?; let chain_ctx = ctx.borrow_mut_chain_or_exit(); - Ok(TxTransfer:: { + Ok(TxTransparentTransfer:: { tx, - source: chain_ctx.get_cached(&self.source), + source: chain_ctx.get(&self.source), target: chain_ctx.get(&self.target), token: chain_ctx.get(&self.token), amount: self.amount, @@ -4196,14 +4303,14 @@ pub mod args { } } - impl Args for TxTransfer { + impl Args for TxTransparentTransfer { fn parse(matches: &ArgMatches) -> Self { let tx = Tx::parse(matches); - let source = TRANSFER_SOURCE.parse(matches); - let target = TRANSFER_TARGET.parse(matches); + let source = SOURCE.parse(matches); + let target = TARGET.parse(matches); let token = TOKEN.parse(matches); let amount = InputAmount::Unvalidated(AMOUNT.parse(matches)); - let tx_code_path = PathBuf::from(TX_TRANSFER_WASM); + let tx_code_path = PathBuf::from(TX_TRANSPARENT_TRANSFER_WASM); Self { tx, source, @@ -4216,15 +4323,193 @@ pub mod args { fn def(app: App) -> App { app.add_args::>() - .arg(TRANSFER_SOURCE.def().help(wrap!( + .arg(SOURCE.def().help(wrap!( "The source account address. The source's key may be used \ to produce the signature." ))) - .arg(TRANSFER_TARGET.def().help(wrap!( - "The target account address. The target's key may be used \ - to produce the signature." + .arg(TARGET.def().help(wrap!("The target account address."))) + .arg(TOKEN.def().help(wrap!("The token address."))) + .arg( + AMOUNT + .def() + .help(wrap!("The amount to transfer in decimal.")), + ) + } + } + + impl CliToSdk> for TxShieldedTransfer { + type Error = std::io::Error; + + fn to_sdk( + self, + ctx: &mut Context, + ) -> Result, Self::Error> { + let tx = self.tx.to_sdk(ctx)?; + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + + Ok(TxShieldedTransfer:: { + tx, + source: chain_ctx.get_cached(&self.source), + target: chain_ctx.get(&self.target), + token: chain_ctx.get(&self.token), + amount: self.amount, + tx_code_path: self.tx_code_path.to_path_buf(), + }) + } + } + + impl Args for TxShieldedTransfer { + fn parse(matches: &ArgMatches) -> Self { + let tx = Tx::parse(matches); + let source = SPENDING_KEY_SOURCE.parse(matches); + let target = PAYMENT_ADDRESS_TARGET.parse(matches); + let token = TOKEN.parse(matches); + let amount = InputAmount::Unvalidated(AMOUNT.parse(matches)); + let tx_code_path = PathBuf::from(TX_SHIELDED_TRANSFER_WASM); + Self { + tx, + source, + target, + token, + amount, + tx_code_path, + } + } + + fn def(app: App) -> App { + app.add_args::>() + .arg( + SPENDING_KEY_SOURCE + .def() + .help(wrap!("The source shielded spending key.")), + ) + .arg( + PAYMENT_ADDRESS_TARGET + .def() + .help(wrap!("The shielded target account address.")), + ) + .arg(TOKEN.def().help(wrap!("The token address."))) + .arg( + AMOUNT + .def() + .help(wrap!("The amount to transfer in decimal.")), + ) + } + } + + impl CliToSdk> for TxShieldingTransfer { + type Error = std::io::Error; + + fn to_sdk( + self, + ctx: &mut Context, + ) -> Result, Self::Error> { + let tx = self.tx.to_sdk(ctx)?; + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + + Ok(TxShieldingTransfer:: { + tx, + source: chain_ctx.get(&self.source), + target: chain_ctx.get(&self.target), + token: chain_ctx.get(&self.token), + amount: self.amount, + tx_code_path: self.tx_code_path.to_path_buf(), + }) + } + } + + impl Args for TxShieldingTransfer { + fn parse(matches: &ArgMatches) -> Self { + let tx = Tx::parse(matches); + let source = SOURCE.parse(matches); + let target = PAYMENT_ADDRESS_TARGET.parse(matches); + let token = TOKEN.parse(matches); + let amount = InputAmount::Unvalidated(AMOUNT.parse(matches)); + let tx_code_path = PathBuf::from(TX_SHIELDING_TRANSFER_WASM); + Self { + tx, + source, + target, + token, + amount, + tx_code_path, + } + } + + fn def(app: App) -> App { + app.add_args::>() + .arg(SOURCE.def().help(wrap!( + "The transparent source account address. The source's key \ + will be used to produce the signature." ))) - .arg(TOKEN.def().help(wrap!("The transfer token."))) + .arg( + PAYMENT_ADDRESS_TARGET + .def() + .help(wrap!("The target shielded account address.")), + ) + .arg(TOKEN.def().help(wrap!("The token address."))) + .arg( + AMOUNT + .def() + .help(wrap!("The amount to transfer in decimal.")), + ) + } + } + + impl CliToSdk> + for TxUnshieldingTransfer + { + type Error = std::io::Error; + + fn to_sdk( + self, + ctx: &mut Context, + ) -> Result, Self::Error> { + let tx = self.tx.to_sdk(ctx)?; + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + + Ok(TxUnshieldingTransfer:: { + tx, + source: chain_ctx.get_cached(&self.source), + target: chain_ctx.get(&self.target), + token: chain_ctx.get(&self.token), + amount: self.amount, + tx_code_path: self.tx_code_path.to_path_buf(), + }) + } + } + + impl Args for TxUnshieldingTransfer { + fn parse(matches: &ArgMatches) -> Self { + let tx = Tx::parse(matches); + let source = SPENDING_KEY_SOURCE.parse(matches); + let target = TARGET.parse(matches); + let token = TOKEN.parse(matches); + let amount = InputAmount::Unvalidated(AMOUNT.parse(matches)); + let tx_code_path = PathBuf::from(TX_UNSHIELDING_TRANSFER_WASM); + Self { + tx, + source, + target, + token, + amount, + tx_code_path, + } + } + + fn def(app: App) -> App { + app.add_args::>() + .arg( + SPENDING_KEY_SOURCE + .def() + .help(wrap!("The source shielded spending key.")), + ) + .arg( + TARGET + .def() + .help(wrap!("The transparent target account address.")), + ) + .arg(TOKEN.def().help(wrap!("The token address."))) .arg( AMOUNT .def() @@ -6426,6 +6711,7 @@ pub mod args { type Data = PathBuf; type EthereumAddress = String; type Keypair = WalletKeypair; + type PaymentAddress = WalletPaymentAddr; type PublicKey = WalletPublicKey; type SpendingKey = WalletSpendingKey; type TendermintAddress = tendermint_rpc::Url; diff --git a/crates/apps_lib/src/cli/client.rs b/crates/apps_lib/src/cli/client.rs index 03f69c3f3e..fcb43df9de 100644 --- a/crates/apps_lib/src/cli/client.rs +++ b/crates/apps_lib/src/cli/client.rs @@ -52,7 +52,7 @@ impl CliApi { ) } } - Sub::TxTransfer(TxTransfer(args)) => { + Sub::TxTransparentTransfer(TxTransparentTransfer(args)) => { let chain_ctx = ctx.borrow_mut_chain_or_exit(); let ledger_address = chain_ctx.get(&args.tx.ledger_address); @@ -62,7 +62,43 @@ impl CliApi { client.wait_until_node_is_synced(&io).await?; let args = args.to_sdk(&mut ctx)?; let namada = ctx.to_sdk(client, io); - tx::submit_transfer(&namada, args).await?; + tx::submit_transparent_transfer(&namada, args).await?; + } + Sub::TxShieldedTransfer(TxShieldedTransfer(args)) => { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let ledger_address = + chain_ctx.get(&args.tx.ledger_address); + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&ledger_address) + }); + client.wait_until_node_is_synced(&io).await?; + let args = args.to_sdk(&mut ctx)?; + let namada = ctx.to_sdk(client, io); + tx::submit_shielded_transfer(&namada, args).await?; + } + Sub::TxShieldingTransfer(TxShieldingTransfer(args)) => { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let ledger_address = + chain_ctx.get(&args.tx.ledger_address); + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&ledger_address) + }); + client.wait_until_node_is_synced(&io).await?; + let args = args.to_sdk(&mut ctx)?; + let namada = ctx.to_sdk(client, io); + tx::submit_shielding_transfer(&namada, args).await?; + } + Sub::TxUnshieldingTransfer(TxUnshieldingTransfer(args)) => { + let chain_ctx = ctx.borrow_mut_chain_or_exit(); + let ledger_address = + chain_ctx.get(&args.tx.ledger_address); + let client = client.unwrap_or_else(|| { + C::from_tendermint_address(&ledger_address) + }); + client.wait_until_node_is_synced(&io).await?; + let args = args.to_sdk(&mut ctx)?; + let namada = ctx.to_sdk(client, io); + tx::submit_unshielding_transfer(&namada, args).await?; } Sub::TxIbcTransfer(TxIbcTransfer(args)) => { let chain_ctx = ctx.borrow_mut_chain_or_exit(); diff --git a/crates/apps_lib/src/cli/context.rs b/crates/apps_lib/src/cli/context.rs index 2fad9721c5..2045b890e2 100644 --- a/crates/apps_lib/src/cli/context.rs +++ b/crates/apps_lib/src/cli/context.rs @@ -9,9 +9,9 @@ use color_eyre::eyre::Result; use namada::core::address::{Address, InternalAddress}; use namada::core::chain::ChainId; use namada::core::ethereum_events::EthAddress; -use namada::core::ibc::{is_ibc_denom, is_nft_trace}; use namada::core::key::*; use namada::core::masp::*; +use namada::ibc::{is_ibc_denom, is_nft_trace}; use namada::io::Io; use namada::ledger::ibc::storage::ibc_token; use namada_sdk::masp::fs::FsShieldedUtils; diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index f7a2a182c6..44857b43e8 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -739,18 +739,30 @@ pub async fn submit_init_validator( .await } -pub async fn submit_transfer( +pub async fn submit_transparent_transfer( namada: &impl Namada, - args: args::TxTransfer, + args: args::TxTransparentTransfer, ) -> Result<(), error::Error> { - for _ in 0..2 { - submit_reveal_aux( - namada, - args.tx.clone(), - &args.source.effective_address(), - ) - .await?; + submit_reveal_aux(namada, args.tx.clone(), &args.source).await?; + + let (mut tx, signing_data) = args.clone().build(namada).await?; + + if args.tx.dump_tx { + tx::dump_tx(namada.io(), &args.tx, tx); + } else { + sign(namada, &mut tx, &args.tx, signing_data).await?; + namada.submit(tx, &args.tx).await?; + } + Ok(()) +} + +pub async fn submit_shielded_transfer( + namada: &impl Namada, + args: args::TxShieldedTransfer, +) -> Result<(), error::Error> { + // Repeat once if the tx fails on a crossover of an epoch + for _ in 0..2 { let (mut tx, signing_data, tx_epoch) = args.clone().build(namada).await?; @@ -760,22 +772,59 @@ pub async fn submit_transfer( } else { sign(namada, &mut tx, &args.tx, signing_data).await?; let cmt_hash = tx.first_commitments().unwrap().get_hash(); - let result = namada.submit(tx, &args.tx).await?; + match result { + ProcessTxResponse::Applied(resp) if + // If a transaction is rejected by a VP + matches!(resp.batch_result().get(&cmt_hash), Some(InnerTxResult::VpsRejected(_))) => + { + let submission_epoch = rpc::query_and_print_epoch(namada).await; + // And its submission epoch doesn't match construction epoch + if tx_epoch != submission_epoch { + // Then we probably straddled an epoch boundary. Let's retry... + edisplay_line!(namada.io(), + "Shielded transaction rejected and this may be due to the \ + epoch changing. Attempting to resubmit transaction.", + ); + continue; + } + }, + // Otherwise either the transaction was successful or it will not + // benefit from resubmission + _ => break, + } + } + } + Ok(()) +} + +pub async fn submit_shielding_transfer( + namada: &impl Namada, + args: args::TxShieldingTransfer, +) -> Result<(), error::Error> { + // Repeat once if the tx fails on a crossover of an epoch + for _ in 0..2 { + let (mut tx, signing_data, tx_epoch) = + args.clone().build(namada).await?; + if args.tx.dump_tx { + tx::dump_tx(namada.io(), &args.tx, tx); + break; + } else { + sign(namada, &mut tx, &args.tx, signing_data).await?; + let cmt_hash = tx.first_commitments().unwrap().get_hash(); + let result = namada.submit(tx, &args.tx).await?; match result { ProcessTxResponse::Applied(resp) if - // If a transaction is shielded - tx_epoch.is_some() && - // And it is rejected by a VP + // If a transaction is rejected by a VP matches!(resp.batch_result().get(&cmt_hash), Some(InnerTxResult::VpsRejected(_))) => { let submission_epoch = rpc::query_and_print_epoch(namada).await; // And its submission epoch doesn't match construction epoch - if tx_epoch.unwrap() != submission_epoch { + if tx_epoch != submission_epoch { // Then we probably straddled an epoch boundary. Let's retry... edisplay_line!(namada.io(), - "MASP transaction rejected and this may be due to the \ + "Shielding transaction rejected and this may be due to the \ epoch changing. Attempting to resubmit transaction.", ); continue; @@ -787,7 +836,47 @@ pub async fn submit_transfer( } } } + Ok(()) +} +pub async fn submit_unshielding_transfer( + namada: &impl Namada, + args: args::TxUnshieldingTransfer, +) -> Result<(), error::Error> { + // Repeat once if the tx fails on a crossover of an epoch + for _ in 0..2 { + let (mut tx, signing_data, tx_epoch) = + args.clone().build(namada).await?; + + if args.tx.dump_tx { + tx::dump_tx(namada.io(), &args.tx, tx); + break; + } else { + sign(namada, &mut tx, &args.tx, signing_data).await?; + let cmt_hash = tx.first_commitments().unwrap().get_hash(); + let result = namada.submit(tx, &args.tx).await?; + match result { + ProcessTxResponse::Applied(resp) if + // If a transaction is rejected by a VP + matches!(resp.batch_result().get(&cmt_hash), Some(InnerTxResult::VpsRejected(_))) => + { + let submission_epoch = rpc::query_and_print_epoch(namada).await; + // And its submission epoch doesn't match construction epoch + if tx_epoch != submission_epoch { + // Then we probably straddled an epoch boundary. Let's retry... + edisplay_line!(namada.io(), + "Unshielding transaction rejected and this may be due to the \ + epoch changing. Attempting to resubmit transaction.", + ); + continue; + } + }, + // Otherwise either the transaction was successful or it will not + // benefit from resubmission + _ => break, + } + } + } Ok(()) } From 2a6a7eb6c614a7558695e607b432999d17d90661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 15:10:37 +0200 Subject: [PATCH 11/26] apps: update for split-up transfer tx --- crates/apps/src/bin/namada/cli.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/apps/src/bin/namada/cli.rs b/crates/apps/src/bin/namada/cli.rs index 9b97f2e761..ac56cd02d3 100644 --- a/crates/apps/src/bin/namada/cli.rs +++ b/crates/apps/src/bin/namada/cli.rs @@ -45,7 +45,10 @@ fn handle_command(cmd: cli::cmds::Namada, raw_sub_cmd: String) -> Result<()> { } cli::cmds::Namada::Client(_) | cli::cmds::Namada::TxCustom(_) - | cli::cmds::Namada::TxTransfer(_) + | cli::cmds::Namada::TxTransparentTransfer(_) + | cli::cmds::Namada::TxShieldedTransfer(_) + | cli::cmds::Namada::TxShieldingTransfer(_) + | cli::cmds::Namada::TxUnshieldingTransfer(_) | cli::cmds::Namada::TxIbcTransfer(_) | cli::cmds::Namada::TxUpdateAccount(_) | cli::cmds::Namada::TxRevealPk(_) From 00071efd59c7af1ff1ddc750bc250c8e26a5991c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:23:51 +0200 Subject: [PATCH 12/26] node/bench_utils: update for split-up transfer tx --- crates/node/src/bench_utils.rs | 99 +++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/crates/node/src/bench_utils.rs b/crates/node/src/bench_utils.rs index 4231f65d01..f871f8c4ea 100644 --- a/crates/node/src/bench_utils.rs +++ b/crates/node/src/bench_utils.rs @@ -26,7 +26,6 @@ use namada::core::masp::{ }; use namada::core::storage::{BlockHeight, Epoch, Key, KeySeg, TxIndex}; use namada::core::time::DateTimeUtc; -use namada::core::token::{Amount, DenominatedAmount, Transfer}; use namada::events::extend::{ComposeEvent, MaspTxBatchRefs, MaspTxBlockIndex}; use namada::events::Event; use namada::governance::storage::proposal::ProposalType; @@ -76,6 +75,10 @@ use namada::ledger::queries::{ }; use namada::masp::MaspTxRefs; use namada::state::StorageRead; +use namada::token::{ + Amount, DenominatedAmount, ShieldedTransfer, ShieldingTransfer, + UnshieldingTransfer, +}; use namada::tx::data::pos::Bond; use namada::tx::data::{ BatchResults, BatchedTxResult, Fee, TxResult, VpsResult, @@ -91,7 +94,7 @@ use namada_apps_lib::cli::context::FromContext; use namada_apps_lib::cli::Context; use namada_apps_lib::wallet::{defaults, CliWalletUtils}; use namada_sdk::masp::{ - self, ContextSyncStatus, ShieldedContext, ShieldedTransfer, ShieldedUtils, + self, ContextSyncStatus, ShieldedContext, ShieldedUtils, }; pub use namada_sdk::tx::{ TX_BECOME_VALIDATOR_WASM, TX_BOND_WASM, TX_BRIDGE_POOL_WASM, @@ -101,10 +104,11 @@ pub use namada_sdk::tx::{ TX_CLAIM_REWARDS_WASM, TX_DEACTIVATE_VALIDATOR_WASM, TX_IBC_WASM, TX_INIT_ACCOUNT_WASM, TX_INIT_PROPOSAL as TX_INIT_PROPOSAL_WASM, TX_REACTIVATE_VALIDATOR_WASM, TX_REDELEGATE_WASM, TX_RESIGN_STEWARD, - TX_REVEAL_PK as TX_REVEAL_PK_WASM, TX_TRANSFER_WASM, TX_UNBOND_WASM, - TX_UNJAIL_VALIDATOR_WASM, TX_UPDATE_ACCOUNT_WASM, - TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL as TX_VOTE_PROPOSAL_WASM, - TX_WITHDRAW_WASM, VP_USER_WASM, + TX_REVEAL_PK as TX_REVEAL_PK_WASM, TX_SHIELDED_TRANSFER_WASM, + TX_SHIELDING_TRANSFER_WASM, TX_TRANSPARENT_TRANSFER_WASM, TX_UNBOND_WASM, + TX_UNJAIL_VALIDATOR_WASM, TX_UNSHIELDING_TRANSFER_WASM, + TX_UPDATE_ACCOUNT_WASM, TX_UPDATE_STEWARD_COMMISSION, + TX_VOTE_PROPOSAL as TX_VOTE_PROPOSAL_WASM, TX_WITHDRAW_WASM, VP_USER_WASM, }; use namada_sdk::wallet::Wallet; use namada_sdk::{Namada, NamadaImpl}; @@ -1084,43 +1088,61 @@ impl BenchShieldedCtx { ) .unwrap() .map( - |ShieldedTransfer { + |masp::ShieldedTransfer { builder: _, masp_tx, metadata: _, epoch: _, }| masp_tx, - ); + ) + .expect("MASP must have shielded part"); let mut hasher = Sha256::new(); - let shielded_section_hash = shielded.clone().map(|transaction| { - namada::core::hash::Hash( - Section::MaspTx(transaction) - .hash(&mut hasher) - .finalize_reset() - .into(), + let shielded_section_hash = namada::core::hash::Hash( + Section::MaspTx(shielded.clone()) + .hash(&mut hasher) + .finalize_reset() + .into(), + ); + let tx = if source.effective_address() == MASP + && target.effective_address() == MASP + { + namada.client().generate_tx( + TX_SHIELDED_TRANSFER_WASM, + ShieldedTransfer { + section_hash: shielded_section_hash, + }, + Some(shielded), + None, + vec![&defaults::albert_keypair()], ) - }); - - let tx = namada.client().generate_tx( - TX_TRANSFER_WASM, - Transfer { - source: source.effective_address(), - target: target.effective_address(), - token: address::testing::nam(), - amount: if source.effective_address().eq(&MASP) - && target.effective_address().eq(&MASP) - { - DenominatedAmount::native(0.into()) - } else { - DenominatedAmount::native(amount) + } else if target.effective_address() == MASP { + namada.client().generate_tx( + TX_SHIELDING_TRANSFER_WASM, + ShieldingTransfer { + source: source.effective_address(), + token: address::testing::nam(), + amount: DenominatedAmount::native(amount), + shielded_section_hash, }, - shielded: shielded_section_hash, - }, - shielded, - None, - vec![&defaults::albert_keypair()], - ); + Some(shielded), + None, + vec![&defaults::albert_keypair()], + ) + } else { + namada.client().generate_tx( + TX_UNSHIELDING_TRANSFER_WASM, + UnshieldingTransfer { + target: target.effective_address(), + token: address::testing::nam(), + amount: DenominatedAmount::native(amount), + shielded_section_hash, + }, + Some(shielded), + None, + vec![&defaults::albert_keypair()], + ) + }; let NamadaImpl { client, wallet, @@ -1179,12 +1201,13 @@ impl BenchShieldedCtx { timeout_timestamp_on_b: timeout_timestamp, }; - let transfer = - Transfer::deserialize(&mut tx.tx.data(&tx.cmt).unwrap().as_slice()) - .unwrap(); + let transfer = UnshieldingTransfer::deserialize( + &mut tx.tx.data(&tx.cmt).unwrap().as_slice(), + ) + .unwrap(); let masp_tx = tx .tx - .get_section(&transfer.shielded.unwrap()) + .get_section(&transfer.shielded_section_hash) .unwrap() .masp_tx() .unwrap(); From f30ef117b651e283203614199689a5611c20ec11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:24:01 +0200 Subject: [PATCH 13/26] tests: update for split-up transfer tx --- crates/tests/src/e2e/ibc_tests.rs | 2 +- crates/tests/src/e2e/ledger_tests.rs | 12 +++--- crates/tests/src/integration/ledger_tests.rs | 23 +++++------ crates/tests/src/integration/masp.rs | 43 +++++++++++--------- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/crates/tests/src/e2e/ibc_tests.rs b/crates/tests/src/e2e/ibc_tests.rs index 02236e6f0b..e5e5810e4c 100644 --- a/crates/tests/src/e2e/ibc_tests.rs +++ b/crates/tests/src/e2e/ibc_tests.rs @@ -1665,7 +1665,7 @@ fn transfer_on_chain( std::env::set_var(ENV_VAR_CHAIN_ID, test.net.chain_id.to_string()); let rpc = get_actor_rpc(test, Who::Validator(0)); let tx_args = [ - "transfer", + "transparent-transfer", "--source", sender.as_ref(), "--target", diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index e0a69364e9..792d700029 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -168,7 +168,7 @@ fn test_node_connectivity_and_consensus() -> Result<()> { // 3. Submit a valid token transfer tx let tx_args = [ - "transfer", + "transparent-transfer", "--source", BERTHA, "--target", @@ -852,7 +852,7 @@ fn pos_init_validator() -> Result<()> { // 3. Submit a delegation to the new validator First, transfer some tokens // to the validator's key for fees: let tx_args = vec![ - "transfer", + "transparent-transfer", "--source", BERTHA, "--target", @@ -892,7 +892,7 @@ fn pos_init_validator() -> Result<()> { // 4. Transfer some NAM to the new validator let validator_stake_str = &validator_stake.to_string_native(); let tx_args = vec![ - "transfer", + "transparent-transfer", "--source", BERTHA, "--target", @@ -994,7 +994,7 @@ fn ledger_many_txs_in_a_block() -> Result<()> { // A token transfer tx args let tx_args = Arc::new(vec![ - "transfer", + "transparent-transfer", "--source", BERTHA, "--target", @@ -1223,7 +1223,7 @@ fn double_signing_gets_slashed() -> Result<()> { // 5. Submit a valid token transfer tx to validator 0 let validator_one_rpc = get_actor_rpc(&test, Who::Validator(0)); let tx_args = [ - "transfer", + "transparent-transfer", "--source", BERTHA, "--target", @@ -2264,7 +2264,7 @@ fn rollback() -> Result<()> { // send a few transactions let txs_args = vec![vec![ - "transfer", + "transparent-transfer", "--source", BERTHA, "--target", diff --git a/crates/tests/src/integration/ledger_tests.rs b/crates/tests/src/integration/ledger_tests.rs index 40816bdf67..907de76de1 100644 --- a/crates/tests/src/integration/ledger_tests.rs +++ b/crates/tests/src/integration/ledger_tests.rs @@ -13,7 +13,7 @@ use namada_core::storage::Epoch; use namada_core::token::NATIVE_MAX_DECIMAL_PLACES; use namada_node::shell::testing::client::run; use namada_node::shell::testing::utils::{Bin, CapturedOutput}; -use namada_sdk::tx::{TX_TRANSFER_WASM, VP_USER_WASM}; +use namada_sdk::tx::{TX_TRANSPARENT_TRANSFER_WASM, VP_USER_WASM}; use namada_test_utils::TestWasms; use test_log::test; @@ -53,7 +53,7 @@ fn ledger_txs_and_queries() -> Result<()> { let validator_one_rpc = "http://127.0.0.1:26567"; let (node, _services) = setup::setup()?; - let transfer = token::Transfer { + let transfer = token::TransparentTransfer { source: defaults::bertha_address(), target: defaults::albert_address(), token: node.native_token(), @@ -61,7 +61,6 @@ fn ledger_txs_and_queries() -> Result<()> { token::Amount::native_whole(10), token::NATIVE_MAX_DECIMAL_PLACES.into(), ), - shielded: None, } .serialize_to_vec(); let tx_data_path = node.test_dir.path().join("tx.data"); @@ -74,7 +73,7 @@ fn ledger_txs_and_queries() -> Result<()> { let txs_args = vec![ // 2. Submit a token transfer tx (from an established account) vec![ - "transfer", + "transparent-transfer", "--source", BERTHA, "--target", @@ -90,7 +89,7 @@ fn ledger_txs_and_queries() -> Result<()> { ], // Submit a token transfer tx (from an ed25519 implicit account) vec![ - "transfer", + "transparent-transfer", "--source", DAEWON, "--target", @@ -106,7 +105,7 @@ fn ledger_txs_and_queries() -> Result<()> { ], // Submit a token transfer tx (from a secp256k1 implicit account) vec![ - "transfer", + "transparent-transfer", "--source", ESTER, "--target", @@ -135,7 +134,7 @@ fn ledger_txs_and_queries() -> Result<()> { vec![ "tx", "--code-path", - TX_TRANSFER_WASM, + TX_TRANSPARENT_TRANSFER_WASM, "--data-path", &tx_data_path, "--owner", @@ -352,7 +351,7 @@ fn invalid_transactions() -> Result<()> { // 2. Submit an invalid transaction (trying to transfer tokens should fail // in the user's VP due to the wrong signer) let tx_args = vec![ - "transfer", + "transparent-transfer", "--source", BERTHA, "--target", @@ -381,7 +380,7 @@ fn invalid_transactions() -> Result<()> { let daewon_lower = DAEWON.to_lowercase(); let tx_args = vec![ - "transfer", + "transparent-transfer", "--source", DAEWON, "--signing-keys", @@ -970,7 +969,7 @@ fn proposal_submission() -> Result<()> { // vp and verify that the transaction succeeds, i.e. the non allowlisted // vp can still run let transfer = vec![ - "transfer", + "transparent-transfer", "--source", BERTHA, "--target", @@ -1381,7 +1380,7 @@ fn implicit_account_reveal_pk() -> Result<()> { // A token transfer tx Box::new(|source| { [ - "transfer", + "transparent-transfer", "--source", source, "--target", @@ -1438,7 +1437,7 @@ fn implicit_account_reveal_pk() -> Result<()> { let tx_args = tx_args(&key_alias); // 2b. Send some funds to the implicit account. let credit_args = vec![ - "transfer", + "transparent-transfer", "--source", BERTHA, "--target", diff --git a/crates/tests/src/integration/masp.rs b/crates/tests/src/integration/masp.rs index 4912e3624a..ab534b0114 100644 --- a/crates/tests/src/integration/masp.rs +++ b/crates/tests/src/integration/masp.rs @@ -43,7 +43,7 @@ fn masp_incentives() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "shield", "--source", ALBERT, "--target", @@ -259,7 +259,7 @@ fn masp_incentives() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "shield", "--source", ALBERT, "--target", @@ -397,7 +397,7 @@ fn masp_incentives() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "unshield", "--source", B_SPENDING_KEY, "--target", @@ -507,7 +507,7 @@ fn masp_incentives() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "unshield", "--source", A_SPENDING_KEY, "--target", @@ -677,7 +677,7 @@ fn masp_incentives() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "unshield", "--source", B_SPENDING_KEY, "--target", @@ -708,7 +708,7 @@ fn masp_incentives() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "unshield", "--source", A_SPENDING_KEY, "--target", @@ -824,7 +824,7 @@ fn spend_unconverted_asset_type() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "shield", "--source", ALBERT, "--target", @@ -845,7 +845,7 @@ fn spend_unconverted_asset_type() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "shield", "--source", ALBERT, "--target", @@ -993,7 +993,7 @@ fn masp_txs_and_queries() -> Result<()> { // 1. Attempt to spend 15 BTC at SK(A) to Bertha ( vec![ - "transfer", + "unshield", "--source", A_SPENDING_KEY, "--target", @@ -1012,7 +1012,7 @@ fn masp_txs_and_queries() -> Result<()> { // 2. Send 20 BTC from Albert to PA(A) ( vec![ - "transfer", + "shield", "--source", ALBERT, "--target", @@ -1163,7 +1163,7 @@ fn masp_txs_and_queries() -> Result<()> { // 11. Send 20 BTC from SK(B) to Bertha ( vec![ - "transfer", + "unshield", "--source", B_SPENDING_KEY, "--target", @@ -1184,7 +1184,10 @@ fn masp_txs_and_queries() -> Result<()> { for (tx_args, tx_result) in &txs_args { node.assert_success(); // there is no need to dry run balance queries - let dry_run_args = if tx_args[0] == "transfer" { + let dry_run_args = if tx_args[0] == "transfer" + || tx_args[0] == "shield" + || tx_args[0] == "unshield" + { // We ensure transfers don't cross epoch boundaries. node.next_epoch(); vec![true, false] @@ -1198,7 +1201,7 @@ fn masp_txs_and_queries() -> Result<()> { Bin::Client, vec!["shielded-sync", "--node", validator_one_rpc], )?; - let tx_args = if dry_run && tx_args[0] == "transfer" { + let tx_args = if dry_run { [tx_args.clone(), vec!["--dry-run"]].concat() } else { tx_args.clone() @@ -1314,7 +1317,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "shield", "--source", ALBERT_KEY, "--target", @@ -1333,7 +1336,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "shield", "--source", ALBERT_KEY, "--target", @@ -1352,7 +1355,7 @@ fn multiple_unfetched_txs_same_block() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "shield", "--source", ALBERT_KEY, "--target", @@ -1548,7 +1551,7 @@ fn cross_epoch_unshield() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "shield", "--source", ALBERT, "--target", @@ -1583,7 +1586,7 @@ fn cross_epoch_unshield() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "unshield", "--source", A_SPENDING_KEY, "--target", @@ -1689,7 +1692,7 @@ fn dynamic_assets() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "shield", "--source", ALBERT, "--target", @@ -1823,7 +1826,7 @@ fn dynamic_assets() -> Result<()> { &node, Bin::Client, vec![ - "transfer", + "shield", "--source", ALBERT, "--target", From d58184825b292bc889b4a9d107a9117da17690bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:24:11 +0200 Subject: [PATCH 14/26] encoding_spec: update for split-up transfer tx --- crates/encoding_spec/src/main.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/crates/encoding_spec/src/main.rs b/crates/encoding_spec/src/main.rs index ea5858ae3f..efb5c0c79c 100644 --- a/crates/encoding_spec/src/main.rs +++ b/crates/encoding_spec/src/main.rs @@ -23,14 +23,13 @@ use borsh::{schema, schema_container_of}; use itertools::Itertools; use lazy_static::lazy_static; use madato::types::TableRow; -use namada::account; use namada::core::address::Address; use namada::core::collections::HashSet; use namada::core::key::ed25519::{PublicKey, Signature}; use namada::core::storage::{self, Epoch}; -use namada::core::token; use namada::ledger::parameters::Parameters; use namada::tx::data::{pos, TxType, WrapperTx}; +use namada::{account, token}; /// This generator will write output into this `docs` file. const OUTPUT_PATH: &str = @@ -82,7 +81,14 @@ fn main() -> Result<(), Box> { let signature_schema = schema_container_of::(); let init_account_schema = schema_container_of::(); let init_validator_schema = schema_container_of::(); - let token_transfer_schema = schema_container_of::(); + let transparent_transfer_schema = + schema_container_of::(); + let shielded_transfer_schema = + schema_container_of::(); + let shielding_transfer_schema = + schema_container_of::(); + let unshielding_transfer_schema = + schema_container_of::(); let update_account = schema_container_of::(); let pos_bond_schema = schema_container_of::(); let pos_withdraw_schema = schema_container_of::(); @@ -109,7 +115,10 @@ fn main() -> Result<(), Box> { definitions.extend(btree(&signature_schema)); definitions.extend(btree(&init_account_schema)); definitions.extend(btree(&init_validator_schema)); - definitions.extend(btree(&token_transfer_schema)); + definitions.extend(btree(&transparent_transfer_schema)); + definitions.extend(btree(&shielded_transfer_schema)); + definitions.extend(btree(&shielding_transfer_schema)); + definitions.extend(btree(&unshielding_transfer_schema)); definitions.extend(btree(&update_account)); definitions.extend(btree(&pos_bond_schema)); definitions.extend(btree(&pos_withdraw_schema)); @@ -183,10 +192,10 @@ fn main() -> Result<(), Box> { tables.push(init_validator_table); let token_transfer_definition = definitions - .remove(token_transfer_schema.declaration()) + .remove(transparent_transfer_schema.declaration()) .unwrap(); let token_transfer_table = definition_to_table( - token_transfer_schema.declaration(), + transparent_transfer_schema.declaration(), token_transfer_definition, ).with_rust_doc_link("https://dev.namada.net/master/rustdoc/namada/types/token/struct.Transfer.html"); tables.push(token_transfer_table); From 3e609151141f795aafffb9883971ec028a7f3017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:27:05 +0200 Subject: [PATCH 15/26] benches: update for split-up transfer tx --- crates/benches/host_env.rs | 16 ++++++++-------- crates/benches/native_vps.rs | 10 +++++----- crates/benches/process_wrapper.rs | 9 ++++----- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/crates/benches/host_env.rs b/crates/benches/host_env.rs index 8b13760964..1eee561163 100644 --- a/crates/benches/host_env.rs +++ b/crates/benches/host_env.rs @@ -3,29 +3,29 @@ use namada::core::account::AccountPublicKeysMap; use namada::core::address; use namada::core::collections::{HashMap, HashSet}; use namada::ledger::storage::DB; -use namada::token::{Amount, Transfer}; +use namada::token::{Amount, TransparentTransfer}; use namada::tx::Authorization; use namada::vm::wasm::TxCache; use namada_apps_lib::wallet::defaults; use namada_apps_lib::wasm_loader; use namada_node::bench_utils::{ - BenchShell, TX_INIT_PROPOSAL_WASM, TX_REVEAL_PK_WASM, TX_TRANSFER_WASM, - TX_UPDATE_ACCOUNT_WASM, VP_USER_WASM, WASM_DIR, + BenchShell, TX_INIT_PROPOSAL_WASM, TX_REVEAL_PK_WASM, + TX_TRANSPARENT_TRANSFER_WASM, TX_UPDATE_ACCOUNT_WASM, VP_USER_WASM, + WASM_DIR, }; // Benchmarks the validation of a single signature on a single `Section` of a // transaction fn tx_section_signature_validation(c: &mut Criterion) { let shell = BenchShell::default(); - let transfer_data = Transfer { + let transfer_data = TransparentTransfer { source: defaults::albert_address(), target: defaults::bertha_address(), token: address::testing::nam(), amount: Amount::native_whole(500).native_denominated(), - shielded: None, }; let tx = shell.generate_tx( - TX_TRANSFER_WASM, + TX_TRANSPARENT_TRANSFER_WASM, transfer_data, None, None, @@ -62,7 +62,7 @@ fn compile_wasm(c: &mut Criterion) { let mut txs: HashMap<&str, Vec> = HashMap::default(); for tx in [ - TX_TRANSFER_WASM, + TX_TRANSPARENT_TRANSFER_WASM, TX_INIT_PROPOSAL_WASM, TX_REVEAL_PK_WASM, TX_UPDATE_ACCOUNT_WASM, @@ -111,7 +111,7 @@ fn untrusted_wasm_validation(c: &mut Criterion) { let mut txs: HashMap<&str, Vec> = HashMap::default(); for tx in [ - TX_TRANSFER_WASM, + TX_TRANSPARENT_TRANSFER_WASM, TX_INIT_PROPOSAL_WASM, TX_REVEAL_PK_WASM, TX_UPDATE_ACCOUNT_WASM, diff --git a/crates/benches/native_vps.rs b/crates/benches/native_vps.rs index 0ad8a637b5..7371b3afbc 100644 --- a/crates/benches/native_vps.rs +++ b/crates/benches/native_vps.rs @@ -54,14 +54,15 @@ use namada::sdk::masp_primitives::merkle_tree::CommitmentTree; use namada::sdk::masp_primitives::transaction::Transaction; use namada::sdk::masp_proofs::sapling::SaplingVerificationContext; use namada::state::{Epoch, StorageRead, StorageWrite, TxIndex}; -use namada::token::{Amount, Transfer}; +use namada::token::{Amount, TransparentTransfer}; use namada::tx::{BatchedTx, Code, Section, Tx}; use namada_apps_lib::wallet::defaults; use namada_node::bench_utils::{ generate_foreign_key_tx, BenchShell, BenchShieldedCtx, ALBERT_PAYMENT_ADDRESS, ALBERT_SPENDING_KEY, BERTHA_PAYMENT_ADDRESS, TX_BRIDGE_POOL_WASM, TX_IBC_WASM, TX_INIT_PROPOSAL_WASM, TX_RESIGN_STEWARD, - TX_TRANSFER_WASM, TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL_WASM, + TX_TRANSPARENT_TRANSFER_WASM, TX_UPDATE_STEWARD_COMMISSION, + TX_VOTE_PROPOSAL_WASM, }; fn governance(c: &mut Criterion) { @@ -472,13 +473,12 @@ fn vp_multitoken(c: &mut Criterion) { generate_foreign_key_tx(&defaults::albert_keypair()); let transfer = shell.generate_tx( - TX_TRANSFER_WASM, - Transfer { + TX_TRANSPARENT_TRANSFER_WASM, + TransparentTransfer { source: defaults::albert_address(), target: defaults::bertha_address(), token: address::testing::nam(), amount: Amount::native_whole(1000).native_denominated(), - shielded: None, }, None, None, diff --git a/crates/benches/process_wrapper.rs b/crates/benches/process_wrapper.rs index 26ad6d2e74..bb842a9034 100644 --- a/crates/benches/process_wrapper.rs +++ b/crates/benches/process_wrapper.rs @@ -3,11 +3,11 @@ use namada::core::address; use namada::core::key::RefTo; use namada::core::storage::BlockHeight; use namada::core::time::DateTimeUtc; -use namada::token::{Amount, DenominatedAmount, Transfer}; +use namada::token::{Amount, DenominatedAmount, TransparentTransfer}; use namada::tx::data::{Fee, WrapperTx}; use namada::tx::Authorization; use namada_apps_lib::wallet::defaults; -use namada_node::bench_utils::{BenchShell, TX_TRANSFER_WASM}; +use namada_node::bench_utils::{BenchShell, TX_TRANSPARENT_TRANSFER_WASM}; use namada_node::shell::process_proposal::ValidationMeta; fn process_tx(c: &mut Criterion) { @@ -18,13 +18,12 @@ fn process_tx(c: &mut Criterion) { BlockHeight(2); let mut batched_tx = shell.generate_tx( - TX_TRANSFER_WASM, - Transfer { + TX_TRANSPARENT_TRANSFER_WASM, + TransparentTransfer { source: defaults::albert_address(), target: defaults::bertha_address(), token: address::testing::nam(), amount: Amount::native_whole(1).native_denominated(), - shielded: None, }, None, None, From d3cf14b0ad65bd618574ccecbfcee10190bc132f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:37:33 +0200 Subject: [PATCH 16/26] wasm/tx_ibc: update the shielded transfer --- wasm/tx_ibc/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/tx_ibc/src/lib.rs b/wasm/tx_ibc/src/lib.rs index a91ef16f91..e131a90c9a 100644 --- a/wasm/tx_ibc/src/lib.rs +++ b/wasm/tx_ibc/src/lib.rs @@ -13,7 +13,7 @@ fn apply_tx(ctx: &mut Ctx, tx_data: BatchedTx) -> TxResult { ibc::ibc_actions(ctx).execute(&data).into_storage_result()?; if let Some(masp_section_ref) = - transfer.and_then(|transfer| transfer.shielded) + transfer.map(|transfer| transfer.shielded_section_hash) { let shielded = tx_data .tx From e8d810c522dc2b86252a71c0e3adb23a3330e342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Wed, 29 May 2024 12:37:50 +0200 Subject: [PATCH 17/26] wasm_for_tests: update Cargo.lock --- wasm_for_tests/Cargo.lock | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wasm_for_tests/Cargo.lock b/wasm_for_tests/Cargo.lock index 59aa30e730..7c3bc193fb 100644 --- a/wasm_for_tests/Cargo.lock +++ b/wasm_for_tests/Cargo.lock @@ -4039,11 +4039,15 @@ dependencies = [ name = "namada_token" version = "0.37.0" dependencies = [ + "borsh 1.2.1", "namada_core", "namada_events", + "namada_macros", "namada_shielded_token", "namada_storage", "namada_trans_token", + "proptest", + "serde", ] [[package]] From a6fc9a22f1f52522e2f2cb035474d08d1c80978a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 30 May 2024 09:35:05 +0200 Subject: [PATCH 18/26] sdk/tx: fix the display of insufficient funds for non-native token --- crates/sdk/src/tx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index 436fdfeb46..7dddb254f2 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -3215,7 +3215,7 @@ async fn construct_shielded_parts( Err(Build(builder::Error::InsufficientFunds(_))) => { return Err(TxSubmitError::NegativeBalanceAfterTransfer( Box::new(source.effective_address()), - amount.amount().to_string_native(), + amount.to_string(), Box::new(token.clone()), ) .into()); From aa45a7813f41faf7bfc0d3e1a48ac038dacba921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 30 May 2024 10:04:34 +0200 Subject: [PATCH 19/26] changelog: add #3297 --- .../unreleased/improvements/3297-split-transfer-tx.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changelog/unreleased/improvements/3297-split-transfer-tx.md diff --git a/.changelog/unreleased/improvements/3297-split-transfer-tx.md b/.changelog/unreleased/improvements/3297-split-transfer-tx.md new file mode 100644 index 0000000000..a4a8b2fa4c --- /dev/null +++ b/.changelog/unreleased/improvements/3297-split-transfer-tx.md @@ -0,0 +1,6 @@ +- The transfer command has been split into: + - `transfer` (shielded transfer) + - `transparent-transfer` + - `shield` (from transparent to shielded) + - `unshield` (from shielded to transparent) + ([\#3297](https://github.com/anoma/namada/pull/3297)) \ No newline at end of file From 0276f8778ddb53f5db805c4444e54bb92e12028c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 30 May 2024 10:35:20 +0200 Subject: [PATCH 20/26] fixup! tests: update for split-up transfer tx --- crates/tests/src/e2e/ibc_tests.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/crates/tests/src/e2e/ibc_tests.rs b/crates/tests/src/e2e/ibc_tests.rs index e5e5810e4c..f7d8678876 100644 --- a/crates/tests/src/e2e/ibc_tests.rs +++ b/crates/tests/src/e2e/ibc_tests.rs @@ -151,7 +151,15 @@ fn run_ledger_ibc() -> Result<()> { // Transfer 50000 received over IBC on Chain B let token = format!("{port_id_b}/{channel_id_b}/nam"); - transfer_on_chain(&test_b, BERTHA, ALBERT, token, 50000, BERTHA_KEY)?; + transfer_on_chain( + &test_b, + "transparent-transfer", + BERTHA, + ALBERT, + token, + 50000, + BERTHA_KEY, + )?; check_balances_after_non_ibc(&port_id_b, &channel_id_b, &test_b)?; // Transfer 50000 back from the origin-specific account on Chain B to Chain @@ -234,7 +242,15 @@ fn run_ledger_ibc_with_hermes() -> Result<()> { // Transfer 50000 received over IBC on Chain B let token = format!("{port_id_b}/{channel_id_b}/nam"); - transfer_on_chain(&test_b, BERTHA, ALBERT, token, 50000, BERTHA_KEY)?; + transfer_on_chain( + &test_b, + "transparent-transfer", + BERTHA, + ALBERT, + token, + 50000, + BERTHA_KEY, + )?; check_balances_after_non_ibc(&port_id_b, &channel_id_b, &test_b)?; // Transfer 50000 back from the origin-specific account on Chain B to Chain @@ -263,6 +279,7 @@ fn run_ledger_ibc_with_hermes() -> Result<()> { // Send a token to the shielded address on Chain A transfer_on_chain( &test_a, + "shield", ALBERT, AA_PAYMENT_ADDRESS, BTC, @@ -466,6 +483,7 @@ fn ibc_namada_gaia() -> Result<()> { // Shielded transfer on Namada transfer_on_chain( &test, + "transfer", A_SPENDING_KEY, AB_PAYMENT_ADDRESS, &ibc_denom, @@ -536,6 +554,7 @@ fn pgf_over_ibc_with_hermes() -> Result<()> { // Transfer to PGF account transfer_on_chain( &test_a, + "transparent-transfer", ALBERT, PGF_ADDRESS.to_string(), NAM, @@ -1656,6 +1675,7 @@ fn try_invalid_transfers( fn transfer_on_chain( test: &Test, + kind: impl AsRef, sender: impl AsRef, receiver: impl AsRef, token: impl AsRef, @@ -1665,7 +1685,7 @@ fn transfer_on_chain( std::env::set_var(ENV_VAR_CHAIN_ID, test.net.chain_id.to_string()); let rpc = get_actor_rpc(test, Who::Validator(0)); let tx_args = [ - "transparent-transfer", + kind.as_ref(), "--source", sender.as_ref(), "--target", From d9c37666803c49b17109f1aa080a538aa3d7d9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 30 May 2024 13:56:53 +0200 Subject: [PATCH 21/26] fixup! sdk: update for split-up transfer tx --- crates/sdk/src/tx.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index 7dddb254f2..a2554b220c 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -3035,8 +3035,6 @@ pub async fn build_shielding_transfer( .await?; } - // TODO(namada#2597): this function should also take another arg as the fees - // token and amount let shielded_parts = construct_shielded_parts( context, &TransferSource::Address(source.clone()), From a0e2e6c09e4d7a2ee6b3114b6ba6560204d722a6 Mon Sep 17 00:00:00 2001 From: yito88 Date: Thu, 30 May 2024 22:58:18 +0200 Subject: [PATCH 22/26] ShieldingTransfer for IBC messages --- crates/ibc/src/lib.rs | 4 ++-- crates/ibc/src/msg.rs | 22 +++++++++++----------- crates/node/src/bench_utils.rs | 2 +- crates/sdk/src/tx.rs | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/ibc/src/lib.rs b/crates/ibc/src/lib.rs index 0d70c52419..021a165cf6 100644 --- a/crates/ibc/src/lib.rs +++ b/crates/ibc/src/lib.rs @@ -78,7 +78,7 @@ pub use msg::*; use namada_core::address::{self, Address, MASP}; use namada_core::masp::PaymentAddress; use namada_events::extend::{ReadFromEventAttributes, Success as SuccessAttr}; -use namada_token::UnshieldingTransfer; +use namada_token::ShieldingTransfer; pub use nft::*; use prost::Message; use thiserror::Error; @@ -154,7 +154,7 @@ where pub fn execute( &mut self, tx_data: &[u8], - ) -> Result, Error> { + ) -> Result, Error> { let message = decode_message(tx_data)?; match &message { IbcMessage::Transfer(msg) => { diff --git a/crates/ibc/src/msg.rs b/crates/ibc/src/msg.rs index 7b607617f1..7502cf1e31 100644 --- a/crates/ibc/src/msg.rs +++ b/crates/ibc/src/msg.rs @@ -7,7 +7,7 @@ use ibc::core::channel::types::msgs::{ }; use ibc::core::handler::types::msgs::MsgEnvelope; use ibc::primitives::proto::Protobuf; -use namada_token::UnshieldingTransfer; +use namada_token::ShieldingTransfer; /// The different variants of an Ibc message pub enum IbcMessage { @@ -31,7 +31,7 @@ pub struct MsgTransfer { /// IBC transfer message pub message: IbcMsgTransfer, /// Shieleded transfer for MASP transaction - pub transfer: Option, + pub transfer: Option, } impl BorshSerialize for MsgTransfer { @@ -50,7 +50,7 @@ impl BorshDeserialize for MsgTransfer { reader: &mut R, ) -> std::io::Result { use std::io::{Error, ErrorKind}; - let (msg, transfer): (Vec, Option) = + let (msg, transfer): (Vec, Option) = BorshDeserialize::deserialize_reader(reader)?; let message = IbcMsgTransfer::decode_vec(&msg) .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; @@ -64,7 +64,7 @@ pub struct MsgNftTransfer { /// IBC NFT transfer message pub message: IbcMsgNftTransfer, /// Shieleded transfer for MASP transaction - pub transfer: Option, + pub transfer: Option, } impl BorshSerialize for MsgNftTransfer { @@ -83,7 +83,7 @@ impl BorshDeserialize for MsgNftTransfer { reader: &mut R, ) -> std::io::Result { use std::io::{Error, ErrorKind}; - let (msg, transfer): (Vec, Option) = + let (msg, transfer): (Vec, Option) = BorshDeserialize::deserialize_reader(reader)?; let message = IbcMsgNftTransfer::decode_vec(&msg) .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; @@ -97,7 +97,7 @@ pub struct MsgRecvPacket { /// IBC receiving packet message pub message: IbcMsgRecvPacket, /// Shieleded transfer for MASP transaction - pub transfer: Option, + pub transfer: Option, } impl BorshSerialize for MsgRecvPacket { @@ -116,7 +116,7 @@ impl BorshDeserialize for MsgRecvPacket { reader: &mut R, ) -> std::io::Result { use std::io::{Error, ErrorKind}; - let (msg, transfer): (Vec, Option) = + let (msg, transfer): (Vec, Option) = BorshDeserialize::deserialize_reader(reader)?; let message = IbcMsgRecvPacket::decode_vec(&msg) .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; @@ -131,7 +131,7 @@ pub struct MsgAcknowledgement { /// IBC acknowledgement message pub message: IbcMsgAcknowledgement, /// Shieleded transfer for MASP transaction - pub transfer: Option, + pub transfer: Option, } impl BorshSerialize for MsgAcknowledgement { @@ -150,7 +150,7 @@ impl BorshDeserialize for MsgAcknowledgement { reader: &mut R, ) -> std::io::Result { use std::io::{Error, ErrorKind}; - let (msg, transfer): (Vec, Option) = + let (msg, transfer): (Vec, Option) = BorshDeserialize::deserialize_reader(reader)?; let message = IbcMsgAcknowledgement::decode_vec(&msg) .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; @@ -165,7 +165,7 @@ pub struct MsgTimeout { /// IBC timeout message pub message: IbcMsgTimeout, /// Shieleded transfer for MASP transaction - pub transfer: Option, + pub transfer: Option, } impl BorshSerialize for MsgTimeout { @@ -184,7 +184,7 @@ impl BorshDeserialize for MsgTimeout { reader: &mut R, ) -> std::io::Result { use std::io::{Error, ErrorKind}; - let (msg, transfer): (Vec, Option) = + let (msg, transfer): (Vec, Option) = BorshDeserialize::deserialize_reader(reader)?; let message = IbcMsgTimeout::decode_vec(&msg) .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; diff --git a/crates/node/src/bench_utils.rs b/crates/node/src/bench_utils.rs index f871f8c4ea..956dfe8bce 100644 --- a/crates/node/src/bench_utils.rs +++ b/crates/node/src/bench_utils.rs @@ -1201,7 +1201,7 @@ impl BenchShieldedCtx { timeout_timestamp_on_b: timeout_timestamp, }; - let transfer = UnshieldingTransfer::deserialize( + let transfer = ShieldingTransfer::deserialize( &mut tx.tx.data(&tx.cmt).unwrap().as_slice(), ) .unwrap(); diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index a2554b220c..fe965dde3d 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -2567,9 +2567,9 @@ pub async fn build_ibc_transfer( let transfer = shielded_parts.map(|(shielded_transfer, asset_types)| { let masp_tx_hash = tx.add_masp_tx_section(shielded_transfer.masp_tx.clone()).1; - let transfer = token::UnshieldingTransfer { + let transfer = token::ShieldingTransfer { // The token will be escrowed to IBC address - target: Address::Internal(InternalAddress::Ibc), + source: source.clone(), token: args.token.clone(), amount: validated_amount, // Link the Transfer to the MASP Transaction by hash code From 838cec13b9d1d4b71f864b526be7addbab1d80e5 Mon Sep 17 00:00:00 2001 From: yito88 Date: Fri, 31 May 2024 00:02:19 +0200 Subject: [PATCH 23/26] update hermes --- .github/workflows/scripts/hermes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scripts/hermes.txt b/.github/workflows/scripts/hermes.txt index 7588db229e..7258f5d5db 100644 --- a/.github/workflows/scripts/hermes.txt +++ b/.github/workflows/scripts/hermes.txt @@ -1 +1 @@ -1.8.2-namada-beta10-rc +1.8.2-namada-beta11-rc From 3e61a3aa8aff3fb12674b6e7c5aecee5c1ec71df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 3 Jun 2024 11:08:43 +0200 Subject: [PATCH 24/26] fixup! apps_lib: update for split-up transfer tx --- crates/apps_lib/src/client/tx.rs | 39 ++++++-------------------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index 44857b43e8..390191bcbb 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -802,39 +802,14 @@ pub async fn submit_shielding_transfer( namada: &impl Namada, args: args::TxShieldingTransfer, ) -> Result<(), error::Error> { - // Repeat once if the tx fails on a crossover of an epoch - for _ in 0..2 { - let (mut tx, signing_data, tx_epoch) = - args.clone().build(namada).await?; + let (mut tx, signing_data, tx_epoch) = args.clone().build(namada).await?; - if args.tx.dump_tx { - tx::dump_tx(namada.io(), &args.tx, tx); - break; - } else { - sign(namada, &mut tx, &args.tx, signing_data).await?; - let cmt_hash = tx.first_commitments().unwrap().get_hash(); - let result = namada.submit(tx, &args.tx).await?; - match result { - ProcessTxResponse::Applied(resp) if - // If a transaction is rejected by a VP - matches!(resp.batch_result().get(&cmt_hash), Some(InnerTxResult::VpsRejected(_))) => - { - let submission_epoch = rpc::query_and_print_epoch(namada).await; - // And its submission epoch doesn't match construction epoch - if tx_epoch != submission_epoch { - // Then we probably straddled an epoch boundary. Let's retry... - edisplay_line!(namada.io(), - "Shielding transaction rejected and this may be due to the \ - epoch changing. Attempting to resubmit transaction.", - ); - continue; - } - }, - // Otherwise either the transaction was successful or it will not - // benefit from resubmission - _ => break, - } - } + if args.tx.dump_tx { + tx::dump_tx(namada.io(), &args.tx, tx); + } else { + sign(namada, &mut tx, &args.tx, signing_data).await?; + let cmt_hash = tx.first_commitments().unwrap().get_hash(); + namada.submit(tx, &args.tx).await?; } Ok(()) } From 7ca37179029644c06a4f8f003e23fc3cb98c6503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 3 Jun 2024 11:27:34 +0200 Subject: [PATCH 25/26] fixup! apps_lib: update for split-up transfer tx --- crates/apps_lib/src/client/tx.rs | 41 ++++++-------------------------- crates/sdk/src/args.rs | 4 ++-- crates/sdk/src/tx.rs | 10 ++++---- 3 files changed, 13 insertions(+), 42 deletions(-) diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index 390191bcbb..e183b0a17d 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -761,39 +761,13 @@ pub async fn submit_shielded_transfer( namada: &impl Namada, args: args::TxShieldedTransfer, ) -> Result<(), error::Error> { - // Repeat once if the tx fails on a crossover of an epoch - for _ in 0..2 { - let (mut tx, signing_data, tx_epoch) = - args.clone().build(namada).await?; + let (mut tx, signing_data) = args.clone().build(namada).await?; - if args.tx.dump_tx { - tx::dump_tx(namada.io(), &args.tx, tx); - break; - } else { - sign(namada, &mut tx, &args.tx, signing_data).await?; - let cmt_hash = tx.first_commitments().unwrap().get_hash(); - let result = namada.submit(tx, &args.tx).await?; - match result { - ProcessTxResponse::Applied(resp) if - // If a transaction is rejected by a VP - matches!(resp.batch_result().get(&cmt_hash), Some(InnerTxResult::VpsRejected(_))) => - { - let submission_epoch = rpc::query_and_print_epoch(namada).await; - // And its submission epoch doesn't match construction epoch - if tx_epoch != submission_epoch { - // Then we probably straddled an epoch boundary. Let's retry... - edisplay_line!(namada.io(), - "Shielded transaction rejected and this may be due to the \ - epoch changing. Attempting to resubmit transaction.", - ); - continue; - } - }, - // Otherwise either the transaction was successful or it will not - // benefit from resubmission - _ => break, - } - } + if args.tx.dump_tx { + tx::dump_tx(namada.io(), &args.tx, tx); + } else { + sign(namada, &mut tx, &args.tx, signing_data).await?; + namada.submit(tx, &args.tx).await?; } Ok(()) } @@ -802,13 +776,12 @@ pub async fn submit_shielding_transfer( namada: &impl Namada, args: args::TxShieldingTransfer, ) -> Result<(), error::Error> { - let (mut tx, signing_data, tx_epoch) = args.clone().build(namada).await?; + let (mut tx, signing_data) = args.clone().build(namada).await?; if args.tx.dump_tx { tx::dump_tx(namada.io(), &args.tx, tx); } else { sign(namada, &mut tx, &args.tx, signing_data).await?; - let cmt_hash = tx.first_commitments().unwrap().get_hash(); namada.submit(tx, &args.tx).await?; } Ok(()) diff --git a/crates/sdk/src/args.rs b/crates/sdk/src/args.rs index b839c71903..4709e5d2c4 100644 --- a/crates/sdk/src/args.rs +++ b/crates/sdk/src/args.rs @@ -320,7 +320,7 @@ impl TxShieldedTransfer { pub async fn build( &mut self, context: &impl Namada, - ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Epoch)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_shielded_transfer(context, self).await } } @@ -347,7 +347,7 @@ impl TxShieldingTransfer { pub async fn build( &mut self, context: &impl Namada, - ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Epoch)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_shielding_transfer(context, self).await } } diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index fe965dde3d..1f004a8398 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -2907,7 +2907,7 @@ pub async fn build_transparent_transfer( pub async fn build_shielded_transfer( context: &N, args: &mut args::TxShieldedTransfer, -) -> Result<(Tx, SigningTxData, Epoch)> { +) -> Result<(Tx, SigningTxData)> { let default_signer = Some(MASP); let signing_data = signing::aux_signing_data( context, @@ -2937,7 +2937,6 @@ pub async fn build_shielded_transfer( ) .await? .expect("Shielded transfer must have shielded parts"); - let shielded_tx_epoch = shielded_parts.0.epoch; let add_shielded_parts = |tx: &mut Tx, data: &mut token::ShieldedTransfer| { @@ -2983,14 +2982,14 @@ pub async fn build_shielded_transfer( &signing_data.fee_payer, ) .await?; - Ok((tx, signing_data, shielded_tx_epoch)) + Ok((tx, signing_data)) } /// Build a shielding transfer pub async fn build_shielding_transfer( context: &N, args: &mut args::TxShieldingTransfer, -) -> Result<(Tx, SigningTxData, Epoch)> { +) -> Result<(Tx, SigningTxData)> { let source = &args.source; let default_signer = Some(source.clone()); let signing_data = signing::aux_signing_data( @@ -3045,7 +3044,6 @@ pub async fn build_shielding_transfer( ) .await? .expect("Shielding transfer must have shielded parts"); - let shielded_tx_epoch = shielded_parts.0.epoch; let add_shielded_parts = |tx: &mut Tx, data: &mut token::ShieldingTransfer| { @@ -3095,7 +3093,7 @@ pub async fn build_shielding_transfer( &signing_data.fee_payer, ) .await?; - Ok((tx, signing_data, shielded_tx_epoch)) + Ok((tx, signing_data)) } /// Build an unshielding transfer From 21c9d36fa34d747b7c3cc26f2a41971840eda8a7 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli Date: Mon, 3 Jun 2024 15:42:57 +0200 Subject: [PATCH 26/26] retry tx submission on shielding --- crates/apps_lib/src/client/tx.rs | 32 ++++++++++++++++---------------- crates/sdk/src/args.rs | 4 ++-- crates/sdk/src/tx.rs | 10 +++++----- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/crates/apps_lib/src/client/tx.rs b/crates/apps_lib/src/client/tx.rs index e183b0a17d..36b15a4898 100644 --- a/crates/apps_lib/src/client/tx.rs +++ b/crates/apps_lib/src/client/tx.rs @@ -775,21 +775,6 @@ pub async fn submit_shielded_transfer( pub async fn submit_shielding_transfer( namada: &impl Namada, args: args::TxShieldingTransfer, -) -> Result<(), error::Error> { - let (mut tx, signing_data) = args.clone().build(namada).await?; - - if args.tx.dump_tx { - tx::dump_tx(namada.io(), &args.tx, tx); - } else { - sign(namada, &mut tx, &args.tx, signing_data).await?; - namada.submit(tx, &args.tx).await?; - } - Ok(()) -} - -pub async fn submit_unshielding_transfer( - namada: &impl Namada, - args: args::TxUnshieldingTransfer, ) -> Result<(), error::Error> { // Repeat once if the tx fails on a crossover of an epoch for _ in 0..2 { @@ -813,7 +798,7 @@ pub async fn submit_unshielding_transfer( if tx_epoch != submission_epoch { // Then we probably straddled an epoch boundary. Let's retry... edisplay_line!(namada.io(), - "Unshielding transaction rejected and this may be due to the \ + "Shielding transaction rejected and this may be due to the \ epoch changing. Attempting to resubmit transaction.", ); continue; @@ -828,6 +813,21 @@ pub async fn submit_unshielding_transfer( Ok(()) } +pub async fn submit_unshielding_transfer( + namada: &impl Namada, + args: args::TxUnshieldingTransfer, +) -> Result<(), error::Error> { + let (mut tx, signing_data) = args.clone().build(namada).await?; + + if args.tx.dump_tx { + tx::dump_tx(namada.io(), &args.tx, tx); + } else { + sign(namada, &mut tx, &args.tx, signing_data).await?; + namada.submit(tx, &args.tx).await?; + } + Ok(()) +} + pub async fn submit_ibc_transfer( namada: &N, args: args::TxIbcTransfer, diff --git a/crates/sdk/src/args.rs b/crates/sdk/src/args.rs index 4709e5d2c4..b794bf7649 100644 --- a/crates/sdk/src/args.rs +++ b/crates/sdk/src/args.rs @@ -347,7 +347,7 @@ impl TxShieldingTransfer { pub async fn build( &mut self, context: &impl Namada, - ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Epoch)> { tx::build_shielding_transfer(context, self).await } } @@ -374,7 +374,7 @@ impl TxUnshieldingTransfer { pub async fn build( &mut self, context: &impl Namada, - ) -> crate::error::Result<(namada_tx::Tx, SigningTxData, Epoch)> { + ) -> crate::error::Result<(namada_tx::Tx, SigningTxData)> { tx::build_unshielding_transfer(context, self).await } } diff --git a/crates/sdk/src/tx.rs b/crates/sdk/src/tx.rs index 1f004a8398..afa2e5a5cf 100644 --- a/crates/sdk/src/tx.rs +++ b/crates/sdk/src/tx.rs @@ -2989,7 +2989,7 @@ pub async fn build_shielded_transfer( pub async fn build_shielding_transfer( context: &N, args: &mut args::TxShieldingTransfer, -) -> Result<(Tx, SigningTxData)> { +) -> Result<(Tx, SigningTxData, Epoch)> { let source = &args.source; let default_signer = Some(source.clone()); let signing_data = signing::aux_signing_data( @@ -3044,6 +3044,7 @@ pub async fn build_shielding_transfer( ) .await? .expect("Shielding transfer must have shielded parts"); + let shielded_tx_epoch = shielded_parts.0.epoch; let add_shielded_parts = |tx: &mut Tx, data: &mut token::ShieldingTransfer| { @@ -3093,14 +3094,14 @@ pub async fn build_shielding_transfer( &signing_data.fee_payer, ) .await?; - Ok((tx, signing_data)) + Ok((tx, signing_data, shielded_tx_epoch)) } /// Build an unshielding transfer pub async fn build_unshielding_transfer( context: &N, args: &mut args::TxUnshieldingTransfer, -) -> Result<(Tx, SigningTxData, Epoch)> { +) -> Result<(Tx, SigningTxData)> { let default_signer = Some(MASP); let signing_data = signing::aux_signing_data( context, @@ -3130,7 +3131,6 @@ pub async fn build_unshielding_transfer( ) .await? .expect("Shielding transfer must have shielded parts"); - let shielded_tx_epoch = shielded_parts.0.epoch; let add_shielded_parts = |tx: &mut Tx, data: &mut token::UnshieldingTransfer| { @@ -3179,7 +3179,7 @@ pub async fn build_unshielding_transfer( &signing_data.fee_payer, ) .await?; - Ok((tx, signing_data, shielded_tx_epoch)) + Ok((tx, signing_data)) } // Construct the shielded part of the transaction, if any