diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/xcvm.rs b/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/xcvm.rs index f76c9129ab0..bf8799643cb 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/xcvm.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/xcvm.rs @@ -91,7 +91,7 @@ pub fn ibc_packet_receive( ) -> Result { let response = IbcReceiveResponse::default().add_event(make_event("receive")); let msg = (|| -> Result<_> { - let packet = XcPacket::try_decode(&msg.packet.data)?; + let packet = XcPacket::decode(&msg.packet.data)?; let call_origin = CallOrigin::Remote { user_origin: packet.user_origin }; let execute_program = msg::ExecuteProgramMsg { salt: packet.salt, @@ -119,7 +119,7 @@ pub fn ibc_packet_receive( pub fn ibc_packet_ack(_deps: DepsMut, _env: Env, msg: IbcPacketAckMsg) -> Result { let ack = XCVMAck::try_from(msg.acknowledgement.data.as_slice()) .map_err(|_| ContractError::InvalidAck)?; - XcPacket::try_decode(&msg.original_packet.data)?; + XcPacket::decode(&msg.original_packet.data)?; Ok(IbcBasicResponse::default().add_event(make_event("ack").add_attribute("ack", ack))) } @@ -129,7 +129,7 @@ pub fn ibc_packet_timeout( _env: Env, msg: IbcPacketTimeoutMsg, ) -> Result { - XcPacket::try_decode(&msg.packet.data)?; + XcPacket::decode(&msg.packet.data)?; // https://github.com/cosmos/ibc/pull/998 Ok(IbcBasicResponse::default()) } diff --git a/code/xcvm/lib/core/src/proto.rs b/code/xcvm/lib/core/src/proto.rs index 1ea090e0b74..d37e113936e 100644 --- a/code/xcvm/lib/core/src/proto.rs +++ b/code/xcvm/lib/core/src/proto.rs @@ -2,6 +2,7 @@ use prost::Message as _; pub mod common; pub mod pb; +pub mod result; pub mod xcvm; /// Defines an isomorphism between a Rust type `Self` and a protocol message. @@ -10,35 +11,29 @@ pub mod xcvm; /// type from binary representation of the protobuf. pub trait Isomorphism: Sized { /// Protobuf self is isomorphic with. - type Message: prost::Message; + type Message: Default + From + TryInto + prost::Message; /// Converts object to protobuf and encodes it as byte vector. - fn encode(self) -> alloc::vec::Vec - where - Self::Message: From, - { + fn encode(self) -> alloc::vec::Vec { Self::Message::encode_to_vec(&self.into()) } - /// Decodes a protobuf and then converts it to `T`. - fn decode(buffer: &[u8]) -> Result - where - Self::Message: Default + Into, - { - Self::Message::decode(buffer).map(|msg| msg.into()) - } - /// Decodes a protobuf and then tries to convert it to `T`. - fn try_decode(buffer: &[u8]) -> Result - where - Self::Message: Default + TryInto, - { + fn decode(buffer: &[u8]) -> Result { Self::Message::decode(buffer)? .try_into() .map_err(|_| DecodeError::BadIsomorphism) } } +impl Isomorphism for alloc::string::String { + type Message = Self; +} + +impl Isomorphism for () { + type Message = (); +} + /// Error when trying to decode protobuf into a Rust type. #[derive(Clone, Debug, derive_more::From)] pub enum DecodeError { diff --git a/code/xcvm/lib/core/src/proto/result.rs b/code/xcvm/lib/core/src/proto/result.rs new file mode 100644 index 00000000000..af3287a7dbe --- /dev/null +++ b/code/xcvm/lib/core/src/proto/result.rs @@ -0,0 +1,63 @@ +use super::{Isomorphism, NonEmptyExt}; +use prost::Message; + +/// A generic definition of a protocol message with an Ok and Err variants. +#[derive(Clone, PartialEq, Message)] +pub struct ResultMessage { + #[prost(oneof = "ResultEnum", tags = "1, 2")] + pub result: core::option::Option>, +} + +/// Nested enum type in [`ResultMessage`]. +#[derive(Clone, PartialEq, prost::Oneof)] +pub enum ResultEnum { + #[prost(message, tag = "1")] + Ok(T), + #[prost(message, tag = "2")] + Err(E), +} + +impl Isomorphism for Result +where + T: Isomorphism, + T::Message: Default + Send + Sync + TryInto + From, + E: Isomorphism, + E::Message: Default + Send + Sync + TryInto + From, +{ + type Message = ResultMessage; +} + +impl TryFrom> + for Result +{ + type Error = (); + fn try_from(result: ResultMessage) -> Result { + match result.result.non_empty()? { + ResultEnum::Ok(ok) => Ok(Ok(ok.try_into().map_err(|_| ())?)), + ResultEnum::Err(ok) => Ok(Err(ok.try_into().map_err(|_| ())?)), + } + } +} + +impl From> for ResultMessage { + fn from(result: Result) -> Self { + let result = match result { + Ok(ok) => ResultEnum::Ok(ok.into()), + Err(err) => ResultEnum::Err(err.into()), + }; + Self { result: Some(result) } + } +} + +#[test] +fn test_encoding() { + let want = Result::::Ok("ok".into()); + let encoded = want.clone().encode(); + let got = Result::::decode(encoded.as_slice()).unwrap(); + assert_eq!(want, got); + + let want = Result::::Err("err".into()); + let encoded = want.clone().encode(); + let got = Result::::decode(encoded.as_slice()).unwrap(); + assert_eq!(want, got); +} diff --git a/code/xcvm/lib/core/src/proto/xcvm.rs b/code/xcvm/lib/core/src/proto/xcvm.rs index 7184615806d..90f6e1e7141 100644 --- a/code/xcvm/lib/core/src/proto/xcvm.rs +++ b/code/xcvm/lib/core/src/proto/xcvm.rs @@ -23,9 +23,10 @@ pub type XCVMProgram = impl super::Isomorphism for XCVMPacket where - TAbiEncoded: Into>, - TAccount: Into>, - TAssets: Into>, + TAbiEncoded: TryFrom> + Into>, + TAccount: TryFrom> + Into>, + TAssets: + From> + Into>, { type Message = pb::xcvm::Packet; } @@ -33,9 +34,10 @@ where impl super::Isomorphism for XCVMProgram where - TAbiEncoded: Into>, - TAccount: Into>, - TAssets: Into>, + TAbiEncoded: TryFrom> + Into>, + TAccount: TryFrom> + Into>, + TAssets: + From> + Into>, { type Message = pb::xcvm::Program; }