Skip to content

Commit

Permalink
Merge pull request #1637 from CosmWasm/upstream-stdack
Browse files Browse the repository at this point in the history
Add StdAck
  • Loading branch information
webmaster128 authored Aug 23, 2023
2 parents f0ab9bb + 94d0eea commit c151a84
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ and this project adheres to
- cosmwasm-std: Make `abs_diff` const for `Uint{256,512}` and
`Int{64,128,256,512}`. It is now const for all integer types.
- cosmwasm-std: Implement `TryFrom<Decimal256>` for `Decimal` ([#1832])
- cosmwasm-std: Add `StdAck`. ([#1512])

[#1512]: https://github.com/CosmWasm/cosmwasm/issues/1512
[#1799]: https://github.com/CosmWasm/cosmwasm/pull/1799
[#1806]: https://github.com/CosmWasm/cosmwasm/pull/1806
[#1832]: https://github.com/CosmWasm/cosmwasm/pull/1832
Expand Down
11 changes: 11 additions & 0 deletions packages/std/src/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,17 @@ impl<T> IbcReceiveResponse<T> {
}

/// Set the acknowledgement for this response.
///
/// ## Examples
///
/// ```
/// use cosmwasm_std::{StdAck, IbcReceiveResponse};
///
/// fn make_response_with_ack() -> IbcReceiveResponse {
/// let ack = StdAck::success(b"\x01"); // 0x01 is a FungibleTokenPacketSuccess from ICS-20.
/// IbcReceiveResponse::new().set_ack(ack)
/// }
/// ```
pub fn set_ack(mut self, ack: impl Into<Binary>) -> Self {
self.acknowledgement = ack.into();
self
Expand Down
2 changes: 2 additions & 0 deletions packages/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mod query;
mod results;
mod sections;
mod serde;
mod stdack;
mod storage;
mod timestamp;
mod traits;
Expand Down Expand Up @@ -87,6 +88,7 @@ pub use crate::results::{DistributionMsg, StakingMsg};
#[cfg(feature = "stargate")]
pub use crate::results::{GovMsg, VoteOption};
pub use crate::serde::{from_binary, from_slice, to_binary, to_vec};
pub use crate::stdack::StdAck;
pub use crate::storage::MemoryStorage;
pub use crate::timestamp::Timestamp;
pub use crate::traits::{Api, Querier, QuerierResult, QuerierWrapper, Storage};
Expand Down
163 changes: 163 additions & 0 deletions packages/std/src/stdack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::binary::Binary;
use crate::to_binary;

/// This is a standard IBC acknowledgement type. IBC application are free
/// to use any acknowledgement format they want. However, for compatibility
/// purposes it is recommended to use this.
///
/// The original proto definition can be found at <https://github.com/cosmos/cosmos-sdk/blob/v0.42.0/proto/ibc/core/channel/v1/channel.proto#L141-L147>
/// and <https://github.com/cosmos/ibc/tree/ed849c7bac/spec/core/ics-004-channel-and-packet-semantics#acknowledgement-envelope>.
///
/// In contrast to the original idea, [ICS-20](https://github.com/cosmos/ibc/tree/ed849c7bacf16204e9509f0f0df325391f3ce25c/spec/app/ics-020-fungible-token-transfer#technical-specification) and CosmWasm IBC protocols
/// use JSON instead of a protobuf serialization.
///
/// For compatibility, we use the field name "result" for the success case in JSON.
/// However, all Rust APIs use the term "success" for clarity and discriminability from [Result].
///
/// If ibc_receive_packet returns Err(), then x/wasm runtime will rollback the state and
/// return an error message in this format.
///
/// ## Examples
///
/// For your convenience, there are success and error constructors.
///
/// ```
/// use cosmwasm_std::StdAck;
///
/// let ack1 = StdAck::success(b"\x01"); // 0x01 is a FungibleTokenPacketSuccess from ICS-20.
/// assert!(ack1.is_success());
///
/// let ack2 = StdAck::error("kaputt"); // Some free text error message
/// assert!(ack2.is_error());
/// ```
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum StdAck {
#[serde(rename = "result")]
Success(Binary),
Error(String),
}

impl StdAck {
/// Creates a success ack with the given data
pub fn success(data: impl Into<Binary>) -> Self {
StdAck::Success(data.into())
}

/// Creates an error ack
pub fn error(err: impl Into<String>) -> Self {
StdAck::Error(err.into())
}

#[must_use = "if you intended to assert that this is a success, consider `.unwrap()` instead"]
#[inline]
pub const fn is_success(&self) -> bool {
matches!(*self, StdAck::Success(_))
}

#[must_use = "if you intended to assert that this is an error, consider `.unwrap_err()` instead"]
#[inline]
pub const fn is_error(&self) -> bool {
!self.is_success()
}

/// Serialized the ack to binary using JSON. This used for setting the acknowledgement
/// field in IbcReceiveResponse.
///
/// ## Examples
///
/// Show how the acknowledgement looks on the write:
///
/// ```
/// # use cosmwasm_std::StdAck;
/// let ack1 = StdAck::success(b"\x01"); // 0x01 is a FungibleTokenPacketSuccess from ICS-20.
/// assert_eq!(ack1.to_binary(), br#"{"result":"AQ=="}"#);
///
/// let ack2 = StdAck::error("kaputt"); // Some free text error message
/// assert_eq!(ack2.to_binary(), br#"{"error":"kaputt"}"#);
/// ```
///
/// Set acknowledgement field in `IbcReceiveResponse`:
///
/// ```
/// use cosmwasm_std::{StdAck, IbcReceiveResponse};
///
/// let ack = StdAck::success(b"\x01"); // 0x01 is a FungibleTokenPacketSuccess from ICS-20.
///
/// let res: IbcReceiveResponse = IbcReceiveResponse::new().set_ack(ack.to_binary());
/// let res: IbcReceiveResponse = IbcReceiveResponse::new().set_ack(ack); // Does the same but consumes the instance
/// ```
pub fn to_binary(&self) -> Binary {
// We need a non-failing StdAck -> Binary conversion to allow using StdAck in
// `impl Into<Binary>` arguments.
// Pretty sure this cannot fail. If that changes we can create a non-failing implementation here.
to_binary(&self).unwrap()
}

pub fn unwrap(self) -> Binary {
match self {
StdAck::Success(data) => data,
StdAck::Error(err) => panic!("{}", err),
}
}

pub fn unwrap_err(self) -> String {
match self {
StdAck::Success(_) => panic!("not an error"),
StdAck::Error(err) => err,
}
}
}

impl From<StdAck> for Binary {
fn from(original: StdAck) -> Binary {
original.to_binary()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn stdack_success_works() {
let success = StdAck::success(b"foo");
match success {
StdAck::Success(data) => assert_eq!(data, b"foo"),
StdAck::Error(_err) => panic!("must not be an error"),
}
}

#[test]
fn stdack_error_works() {
let err = StdAck::error("bar");
match err {
StdAck::Success(_data) => panic!("must not be a success"),
StdAck::Error(err) => assert_eq!(err, "bar"),
}
}

#[test]
fn stdack_is_success_is_error_work() {
let success = StdAck::success(b"foo");
let err = StdAck::error("bar");
// is_success
assert!(success.is_success());
assert!(!err.is_success());
// is_eror
assert!(!success.is_error());
assert!(err.is_error());
}

#[test]
fn stdack_to_binary_works() {
let ack1 = StdAck::success(b"\x01");
assert_eq!(ack1.to_binary(), br#"{"result":"AQ=="}"#);

let ack2 = StdAck::error("kaputt");
assert_eq!(ack2.to_binary(), br#"{"error":"kaputt"}"#);
}
}

0 comments on commit c151a84

Please sign in to comment.