Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(xcvm): add generic way to encode Result as protobuf #4117

Merged
merged 1 commit into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/xcvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub fn ibc_packet_receive(
) -> Result<IbcReceiveResponse> {
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,
Expand Down Expand Up @@ -119,7 +119,7 @@ pub fn ibc_packet_receive(
pub fn ibc_packet_ack(_deps: DepsMut, _env: Env, msg: IbcPacketAckMsg) -> Result<IbcBasicResponse> {
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)))
}

Expand All @@ -129,7 +129,7 @@ pub fn ibc_packet_timeout(
_env: Env,
msg: IbcPacketTimeoutMsg,
) -> Result<IbcBasicResponse> {
XcPacket::try_decode(&msg.packet.data)?;
XcPacket::decode(&msg.packet.data)?;
// https://github.com/cosmos/ibc/pull/998
Ok(IbcBasicResponse::default())
}
Expand Down
29 changes: 12 additions & 17 deletions code/xcvm/lib/core/src/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<Self> + TryInto<Self> + prost::Message;

/// Converts object to protobuf and encodes it as byte vector.
fn encode(self) -> alloc::vec::Vec<u8>
where
Self::Message: From<Self>,
{
fn encode(self) -> alloc::vec::Vec<u8> {
Self::Message::encode_to_vec(&self.into())
}

/// Decodes a protobuf and then converts it to `T`.
fn decode(buffer: &[u8]) -> Result<Self, prost::DecodeError>
where
Self::Message: Default + Into<Self>,
{
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<Self, DecodeError>
where
Self::Message: Default + TryInto<Self>,
{
fn decode(buffer: &[u8]) -> Result<Self, DecodeError> {
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 {
Expand Down
63 changes: 63 additions & 0 deletions code/xcvm/lib/core/src/proto/result.rs
Original file line number Diff line number Diff line change
@@ -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<T: Default + Send + Sync + Message, E: Default + Send + Sync + Message> {
#[prost(oneof = "ResultEnum", tags = "1, 2")]
pub result: core::option::Option<ResultEnum<T, E>>,
}

/// Nested enum type in [`ResultMessage`].
#[derive(Clone, PartialEq, prost::Oneof)]
pub enum ResultEnum<T: Default + Send + Sync + Message, E: Default + Send + Sync + Message> {
#[prost(message, tag = "1")]
Ok(T),
#[prost(message, tag = "2")]
Err(E),
}

impl<T, E> Isomorphism for Result<T, E>
where
T: Isomorphism,
T::Message: Default + Send + Sync + TryInto<T> + From<T>,
E: Isomorphism,
E::Message: Default + Send + Sync + TryInto<E> + From<E>,
{
type Message = ResultMessage<T::Message, E::Message>;
}

impl<T: Isomorphism, E: Isomorphism> TryFrom<ResultMessage<T::Message, E::Message>>
for Result<T, E>
{
type Error = ();
fn try_from(result: ResultMessage<T::Message, E::Message>) -> Result<Self, Self::Error> {
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<T: Isomorphism, E: Isomorphism> From<Result<T, E>> for ResultMessage<T::Message, E::Message> {
fn from(result: Result<T, E>) -> 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::<String, String>::Ok("ok".into());
let encoded = want.clone().encode();
let got = Result::<String, String>::decode(encoded.as_slice()).unwrap();
assert_eq!(want, got);

let want = Result::<String, String>::Err("err".into());
let encoded = want.clone().encode();
let got = Result::<String, String>::decode(encoded.as_slice()).unwrap();
assert_eq!(want, got);
}
14 changes: 8 additions & 6 deletions code/xcvm/lib/core/src/proto/xcvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,21 @@ pub type XCVMProgram<TAbiEncoded, TAccount, TAssets> =
impl<TAbiEncoded, TAccount, TAssets> super::Isomorphism
for XCVMPacket<TAbiEncoded, TAccount, TAssets>
where
TAbiEncoded: Into<Vec<u8>>,
TAccount: Into<Vec<u8>>,
TAssets: Into<Vec<(crate::AssetId, crate::Balance)>>,
TAbiEncoded: TryFrom<Vec<u8>> + Into<Vec<u8>>,
TAccount: TryFrom<Vec<u8>> + Into<Vec<u8>>,
TAssets:
From<Vec<(crate::AssetId, crate::Balance)>> + Into<Vec<(crate::AssetId, crate::Balance)>>,
{
type Message = pb::xcvm::Packet;
}

impl<TAbiEncoded, TAccount, TAssets> super::Isomorphism
for XCVMProgram<TAbiEncoded, TAccount, TAssets>
where
TAbiEncoded: Into<Vec<u8>>,
TAccount: Into<Vec<u8>>,
TAssets: Into<Vec<(crate::AssetId, crate::Balance)>>,
TAbiEncoded: TryFrom<Vec<u8>> + Into<Vec<u8>>,
TAccount: TryFrom<Vec<u8>> + Into<Vec<u8>>,
TAssets:
From<Vec<(crate::AssetId, crate::Balance)>> + Into<Vec<(crate::AssetId, crate::Balance)>>,
{
type Message = pb::xcvm::Program;
}
Expand Down