Skip to content

Commit

Permalink
Create dedicated SubMsgResult
Browse files Browse the repository at this point in the history
  • Loading branch information
webmaster128 committed Feb 25, 2022
1 parent 7c835df commit f98ac72
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 21 deletions.
14 changes: 7 additions & 7 deletions contracts/ibc-reflect/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use cosmwasm_std::{
entry_point, from_slice, to_binary, wasm_execute, BankMsg, Binary, ContractResult, CosmosMsg,
Deps, DepsMut, Empty, Env, Event, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg,
entry_point, from_slice, to_binary, wasm_execute, BankMsg, Binary, CosmosMsg, Deps, DepsMut,
Empty, Env, Event, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg,
IbcChannelOpenMsg, IbcOrder, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg,
IbcReceiveResponse, MessageInfo, Order, QueryResponse, Reply, Response, StdError, StdResult,
SubMsg, SubMsgExecutionResponse, WasmMsg,
SubMsg, SubMsgExecutionResponse, SubMsgResult, WasmMsg,
};

use crate::msg::{
Expand Down Expand Up @@ -35,10 +35,10 @@ pub fn instantiate(
#[entry_point]
pub fn reply(deps: DepsMut, _env: Env, reply: Reply) -> StdResult<Response> {
match (reply.id, reply.result) {
(RECEIVE_DISPATCH_ID, ContractResult::Err(err)) => {
(RECEIVE_DISPATCH_ID, SubMsgResult::Err(err)) => {
Ok(Response::new().set_data(encode_ibc_error(err)))
}
(INIT_CALLBACK_ID, ContractResult::Ok(response)) => handle_init_callback(deps, response),
(INIT_CALLBACK_ID, SubMsgResult::Ok(response)) => handle_init_callback(deps, response),
_ => Err(StdError::generic_err("invalid reply id or result")),
}
}
Expand Down Expand Up @@ -387,7 +387,7 @@ mod tests {
// fake a reply and ensure this works
let response = Reply {
id,
result: ContractResult::Ok(SubMsgExecutionResponse {
result: SubMsgResult::Ok(SubMsgExecutionResponse {
events: fake_events(&account),
data: None,
}),
Expand Down Expand Up @@ -462,7 +462,7 @@ mod tests {
// fake a reply and ensure this works
let response = Reply {
id,
result: ContractResult::Ok(SubMsgExecutionResponse {
result: SubMsgResult::Ok(SubMsgExecutionResponse {
events: fake_events(REFLECT_ADDR),
data: None,
}),
Expand Down
6 changes: 3 additions & 3 deletions contracts/ibc-reflect/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use cosmwasm_std::testing::{
};
use cosmwasm_std::{
attr, coins, BankMsg, ContractResult, CosmosMsg, Event, IbcBasicResponse, IbcOrder,
IbcReceiveResponse, Reply, Response, SubMsgExecutionResponse, WasmMsg,
IbcReceiveResponse, Reply, Response, SubMsgExecutionResponse, SubMsgResult, WasmMsg,
};
use cosmwasm_vm::testing::{
ibc_channel_connect, ibc_channel_open, ibc_packet_receive, instantiate, mock_env, mock_info,
Expand Down Expand Up @@ -95,7 +95,7 @@ fn connect(
// fake a reply and ensure this works
let response = Reply {
id,
result: ContractResult::Ok(SubMsgExecutionResponse {
result: SubMsgResult::Ok(SubMsgExecutionResponse {
events: fake_events(&account),
data: None,
}),
Expand Down Expand Up @@ -171,7 +171,7 @@ fn proper_handshake_flow() {
// we get the callback from reflect
let response = Reply {
id,
result: ContractResult::Ok(SubMsgExecutionResponse {
result: SubMsgResult::Ok(SubMsgExecutionResponse {
events: fake_events(REFLECT_ADDR),
data: None,
}),
Expand Down
6 changes: 3 additions & 3 deletions contracts/reflect/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ mod tests {
use crate::testing::mock_dependencies_with_custom_querier;
use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR};
use cosmwasm_std::{
coin, coins, from_binary, AllBalanceResponse, BankMsg, BankQuery, Binary, ContractResult,
Event, StakingMsg, StdError, SubMsgExecutionResponse,
coin, coins, from_binary, AllBalanceResponse, BankMsg, BankQuery, Binary, Event,
StakingMsg, StdError, SubMsgExecutionResponse, SubMsgResult,
};

#[test]
Expand Down Expand Up @@ -435,7 +435,7 @@ mod tests {
let id = 123u64;
let data = Binary::from(b"foobar");
let events = vec![Event::new("message").add_attribute("signer", "caller-addr")];
let result = ContractResult::Ok(SubMsgExecutionResponse {
let result = SubMsgResult::Ok(SubMsgExecutionResponse {
events: events.clone(),
data: Some(data.clone()),
});
Expand Down
4 changes: 2 additions & 2 deletions contracts/reflect/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

use cosmwasm_std::{
coin, coins, from_binary, BankMsg, Binary, Coin, ContractResult, Event, Reply, Response,
StakingMsg, SubMsg, SubMsgExecutionResponse, SystemResult,
StakingMsg, SubMsg, SubMsgExecutionResponse, SubMsgResult, SystemResult,
};
use cosmwasm_vm::{
testing::{
Expand Down Expand Up @@ -226,7 +226,7 @@ fn reply_and_query() {
let id = 123u64;
let data = Binary::from(b"foobar");
let events = vec![Event::new("message").add_attribute("signer", "caller-addr")];
let result = ContractResult::Ok(SubMsgExecutionResponse {
let result = SubMsgResult::Ok(SubMsgExecutionResponse {
events: events.clone(),
data: Some(data.clone()),
});
Expand Down
2 changes: 1 addition & 1 deletion packages/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub use crate::query::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdRe
pub use crate::results::{
attr, wasm_execute, wasm_instantiate, Attribute, BankMsg, ContractResult, CosmosMsg, CustomMsg,
Empty, Event, QueryResponse, Reply, ReplyOn, Response, SubMsg, SubMsgExecutionResponse,
SystemResult, WasmMsg,
SubMsgResult, SystemResult, WasmMsg,
};
#[cfg(feature = "staking")]
pub use crate::results::{DistributionMsg, StakingMsg};
Expand Down
2 changes: 1 addition & 1 deletion packages/std/src/results/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ pub use empty::Empty;
pub use events::{attr, Attribute, Event};
pub use query::QueryResponse;
pub use response::Response;
pub use submessages::{Reply, ReplyOn, SubMsg, SubMsgExecutionResponse};
pub use submessages::{Reply, ReplyOn, SubMsg, SubMsgExecutionResponse, SubMsgResult};
pub use system_result::SystemResult;
204 changes: 201 additions & 3 deletions packages/std/src/results/submessages.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::{Binary, ContractResult};
use crate::Binary;

use super::{CosmosMsg, Empty, Event};

Expand Down Expand Up @@ -100,12 +100,210 @@ pub struct Reply {
/// The ID that the contract set when emitting the `SubMsg`.
/// Use this to identify which submessage triggered the `reply`.
pub id: u64,
pub result: ContractResult<SubMsgExecutionResponse>,
pub result: SubMsgResult,
}

/// The information we get back from a successful sub-call, with full sdk events
/// This is the result type that is returned from a sub message execution.
///
/// We use a custom type here instead of Rust's Result because we want to be able to
/// define the serialization, which is a public interface. Every language that compiles
/// to Wasm and runs in the ComsWasm VM needs to create the same JSON representation.
///
/// Until version 1.0.0-beta5, `ContractResult<SubMsgExecutionResponse>` was used instead
/// of this type. Once serialized, the two types are the same. However, in the Rust type
/// system we want different types for clarity and documenation reasons.
///
/// # Examples
///
/// Success:
///
/// ```
/// # use cosmwasm_std::{to_vec, Binary, Event, SubMsgExecutionResponse, SubMsgResult};
/// let response = SubMsgExecutionResponse {
/// data: Some(Binary::from_base64("MTIzCg==").unwrap()),
/// events: vec![Event::new("wasm").add_attribute("fo", "ba")],
/// };
/// let result: SubMsgResult = SubMsgResult::Ok(response);
/// assert_eq!(to_vec(&result).unwrap(), br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#);
/// ```
///
/// Failure:
///
/// ```
/// # use cosmwasm_std::{to_vec, SubMsgResult, Response};
/// let error_msg = String::from("Something went wrong");
/// let result = SubMsgResult::Err(error_msg);
/// assert_eq!(to_vec(&result).unwrap(), br#"{"error":"Something went wrong"}"#);
/// ```
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SubMsgResult {
Ok(SubMsgExecutionResponse),
/// An error type that every custom error created by contract developers can be converted to.
/// This could potientially have more structure, but String is the easiest.
#[serde(rename = "error")]
Err(String),
}

// Implementations here mimic the Result API and should be implemented via a conversion to Result
// to ensure API consistency
impl SubMsgResult {
/// Converts a `SubMsgResult<S>` to a `Result<S, String>` as a convenient way
/// to access the full Result API.
pub fn into_result(self) -> Result<SubMsgExecutionResponse, String> {
Result::<SubMsgExecutionResponse, String>::from(self)
}

pub fn unwrap(self) -> SubMsgExecutionResponse {
self.into_result().unwrap()
}

pub fn unwrap_err(self) -> String {
self.into_result().unwrap_err()
}

pub fn is_ok(&self) -> bool {
matches!(self, SubMsgResult::Ok(_))
}

pub fn is_err(&self) -> bool {
matches!(self, SubMsgResult::Err(_))
}
}

impl<E: ToString> From<Result<SubMsgExecutionResponse, E>> for SubMsgResult {
fn from(original: Result<SubMsgExecutionResponse, E>) -> SubMsgResult {
match original {
Ok(value) => SubMsgResult::Ok(value),
Err(err) => SubMsgResult::Err(err.to_string()),
}
}
}

impl From<SubMsgResult> for Result<SubMsgExecutionResponse, String> {
fn from(original: SubMsgResult) -> Result<SubMsgExecutionResponse, String> {
match original {
SubMsgResult::Ok(value) => Ok(value),
SubMsgResult::Err(err) => Err(err),
}
}
}

/// The information we get back from a successful sub message execution,
/// with full Cosmos SDK events.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct SubMsgExecutionResponse {
pub events: Vec<Event>,
pub data: Option<Binary>,
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{from_slice, to_vec, StdError, StdResult};

#[test]
fn sub_msg_result_serialization_works() {
let result = SubMsgResult::Ok(SubMsgExecutionResponse {
data: None,
events: vec![],
});
assert_eq!(
&to_vec(&result).unwrap(),
br#"{"ok":{"events":[],"data":null}}"#
);

let result = SubMsgResult::Ok(SubMsgExecutionResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![Event::new("wasm").add_attribute("fo", "ba")],
});
assert_eq!(
&to_vec(&result).unwrap(),
br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#
);

let result: SubMsgResult = SubMsgResult::Err("broken".to_string());
assert_eq!(&to_vec(&result).unwrap(), b"{\"error\":\"broken\"}");
}

#[test]
fn sub_msg_result_deserialization_works() {
let result: SubMsgResult = from_slice(br#"{"ok":{"events":[],"data":null}}"#).unwrap();
assert_eq!(
result,
SubMsgResult::Ok(SubMsgExecutionResponse {
events: vec![],
data: None,
})
);

let result: SubMsgResult = from_slice(
br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#).unwrap();
assert_eq!(
result,
SubMsgResult::Ok(SubMsgExecutionResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![Event::new("wasm").add_attribute("fo", "ba")],
})
);

let result: SubMsgResult = from_slice(br#"{"error":"broken"}"#).unwrap();
assert_eq!(result, SubMsgResult::Err("broken".to_string()));

// fails for additional attributes
let parse: StdResult<SubMsgResult> = from_slice(br#"{"unrelated":321,"error":"broken"}"#);
match parse.unwrap_err() {
StdError::ParseErr { .. } => {}
err => panic!("Unexpected error: {:?}", err),
}
let parse: StdResult<SubMsgResult> = from_slice(br#"{"error":"broken","unrelated":321}"#);
match parse.unwrap_err() {
StdError::ParseErr { .. } => {}
err => panic!("Unexpected error: {:?}", err),
}
}

#[test]
fn sub_msg_result_can_convert_from_core_result() {
let original: Result<SubMsgExecutionResponse, StdError> = Ok(SubMsgExecutionResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![],
});
let converted: SubMsgResult = original.into();
assert_eq!(
converted,
SubMsgResult::Ok(SubMsgExecutionResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![],
})
);

let original: Result<SubMsgExecutionResponse, StdError> =
Err(StdError::generic_err("broken"));
let converted: SubMsgResult = original.into();
assert_eq!(
converted,
SubMsgResult::Err("Generic error: broken".to_string())
);
}

#[test]
fn sub_msg_result_can_convert_to_core_result() {
let original = SubMsgResult::Ok(SubMsgExecutionResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![],
});
let converted: Result<SubMsgExecutionResponse, String> = original.into();
assert_eq!(
converted,
Ok(SubMsgExecutionResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![],
})
);

let original = SubMsgResult::Err("went wrong".to_string());
let converted: Result<SubMsgExecutionResponse, String> = original.into();
assert_eq!(converted, Err("went wrong".to_string()));
}
}
3 changes: 2 additions & 1 deletion packages/vm/src/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ mod tests {
};
use cosmwasm_std::{
Empty, Event, IbcAcknowledgement, IbcOrder, Reply, ReplyOn, SubMsgExecutionResponse,
SubMsgResult,
};
static CONTRACT: &[u8] = include_bytes!("../testdata/ibc_reflect.wasm");
const IBC_VERSION: &str = "ibc-reflect-v1";
Expand Down Expand Up @@ -726,7 +727,7 @@ mod tests {
// which creates a reflect account. here we get the callback
let response = Reply {
id,
result: ContractResult::Ok(SubMsgExecutionResponse {
result: SubMsgResult::Ok(SubMsgExecutionResponse {
events: vec![event],
data: None,
}),
Expand Down

0 comments on commit f98ac72

Please sign in to comment.