diff --git a/.circleci/config.yml b/.circleci/config.yml
index 2d2ac008d4..09dbda2c7f 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -173,17 +173,17 @@ jobs:
working_directory: ~/project/packages/vm
command: cargo build --locked
- run:
- name: Build with iterator
+ name: Build with all features
working_directory: ~/project/packages/vm
- command: cargo build --locked --features iterator
+ command: cargo build --locked --features iterator,staking,stargate
- run:
name: Test
working_directory: ~/project/packages/vm
command: cargo test --locked
- run:
- name: Test with iterator
+ name: Test with all features
working_directory: ~/project/packages/vm
- command: cargo test --locked --features iterator
+ command: cargo test --locked --features iterator,staking,stargate
- save_cache:
paths:
- /usr/local/cargo/registry
@@ -208,17 +208,17 @@ jobs:
working_directory: ~/project/packages/vm
command: cargo build --locked --features cranelift
- run:
- name: Build with iterator
+ name: Build with all features
working_directory: ~/project/packages/vm
- command: cargo build --locked --features cranelift,iterator
+ command: cargo build --locked --features cranelift,iterator,staking,stargate
- run:
name: Test
working_directory: ~/project/packages/vm
command: cargo test --locked --features cranelift
- run:
- name: Test with iterator
+ name: Test with all features
working_directory: ~/project/packages/vm
- command: cargo test --locked --features cranelift,iterator
+ command: cargo test --locked --features cranelift,iterator,staking,stargate
- save_cache:
paths:
- /usr/local/cargo/registry
diff --git a/packages/vm/Cargo.toml b/packages/vm/Cargo.toml
index 9423985a5c..8863e07aea 100644
--- a/packages/vm/Cargo.toml
+++ b/packages/vm/Cargo.toml
@@ -24,6 +24,8 @@ backtraces = []
# we keep this optional, to allow possible future integration (or different Cosmos Backends)
iterator = ["cosmwasm-std/iterator"]
staking = ["cosmwasm-std/staking"]
+# this enables all stargate-related functionality, including the ibc entry points
+stargate = ["cosmwasm-std/stargate"]
# Use cranelift backend instead of singlepass. This is required for development on Windows.
cranelift = ["wasmer/cranelift"]
diff --git a/packages/vm/src/calls.rs b/packages/vm/src/calls.rs
index 470a3f3a6b..974f965c81 100644
--- a/packages/vm/src/calls.rs
+++ b/packages/vm/src/calls.rs
@@ -168,7 +168,7 @@ where
/// Calls a function with the given arguments.
/// The exported function must return exactly one result (an offset to the result Region).
-fn call_raw(
+pub(crate) fn call_raw(
instance: &mut Instance,
name: &str,
args: &[&[u8]],
diff --git a/packages/vm/src/ibc_calls.rs b/packages/vm/src/ibc_calls.rs
new file mode 100644
index 0000000000..fe07a20fd4
--- /dev/null
+++ b/packages/vm/src/ibc_calls.rs
@@ -0,0 +1,233 @@
+#![cfg(feature = "stargate")]
+
+use cosmwasm_std::{
+ ContractResult, Env, IbcAcknowledgement, IbcBasicResponse, IbcChannel, IbcPacket,
+ IbcReceiveResponse,
+};
+use schemars::JsonSchema;
+use serde::de::DeserializeOwned;
+use std::fmt;
+
+use crate::backend::{Api, Querier, Storage};
+use crate::calls::call_raw;
+use crate::errors::VmResult;
+use crate::instance::Instance;
+use crate::serde::{from_slice, to_vec};
+
+const MAX_LENGTH_IBC: usize = 100_000;
+
+pub fn call_ibc_channel_open(
+ instance: &mut Instance,
+ env: &Env,
+ channel: &IbcChannel,
+) -> VmResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+{
+ let env = to_vec(env)?;
+ let channel = to_vec(channel)?;
+ let data = call_ibc_channel_open_raw(instance, &env, &channel)?;
+ let result: ContractResult<()> = from_slice(&data)?;
+ Ok(result)
+}
+
+pub fn call_ibc_channel_connect(
+ instance: &mut Instance,
+ env: &Env,
+ channel: &IbcChannel,
+) -> VmResult>>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+ U: DeserializeOwned + Clone + fmt::Debug + JsonSchema + PartialEq,
+{
+ let env = to_vec(env)?;
+ let channel = to_vec(channel)?;
+ let data = call_ibc_channel_connect_raw(instance, &env, &channel)?;
+ let result = from_slice(&data)?;
+ Ok(result)
+}
+
+pub fn call_ibc_channel_close(
+ instance: &mut Instance,
+ env: &Env,
+ channel: &IbcChannel,
+) -> VmResult>>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+ U: DeserializeOwned + Clone + fmt::Debug + JsonSchema + PartialEq,
+{
+ let env = to_vec(env)?;
+ let channel = to_vec(channel)?;
+ let data = call_ibc_channel_close_raw(instance, &env, &channel)?;
+ let result = from_slice(&data)?;
+ Ok(result)
+}
+
+pub fn call_ibc_packet_receive(
+ instance: &mut Instance,
+ env: &Env,
+ packet: &IbcPacket,
+) -> VmResult>>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+ U: DeserializeOwned + Clone + fmt::Debug + JsonSchema + PartialEq,
+{
+ let env = to_vec(env)?;
+ let packet = to_vec(packet)?;
+ let data = call_ibc_packet_receive_raw(instance, &env, &packet)?;
+ let result = from_slice(&data)?;
+ Ok(result)
+}
+
+pub fn call_ibc_packet_ack(
+ instance: &mut Instance,
+ env: &Env,
+ ack: &IbcAcknowledgement,
+) -> VmResult>>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+ U: DeserializeOwned + Clone + fmt::Debug + JsonSchema + PartialEq,
+{
+ let env = to_vec(env)?;
+ let ack = to_vec(ack)?;
+ let data = call_ibc_packet_ack_raw(instance, &env, &ack)?;
+ let result = from_slice(&data)?;
+ Ok(result)
+}
+
+pub fn call_ibc_packet_timeout(
+ instance: &mut Instance,
+ env: &Env,
+ packet: &IbcPacket,
+) -> VmResult>>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+ U: DeserializeOwned + Clone + fmt::Debug + JsonSchema + PartialEq,
+{
+ let env = to_vec(env)?;
+ let packet = to_vec(packet)?;
+ let data = call_ibc_packet_timeout_raw(instance, &env, &packet)?;
+ let result = from_slice(&data)?;
+ Ok(result)
+}
+
+pub fn call_ibc_channel_open_raw(
+ instance: &mut Instance,
+ env: &[u8],
+ channel: &[u8],
+) -> VmResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+{
+ instance.set_storage_readonly(false);
+ call_raw(
+ instance,
+ "ibc_channel_open",
+ &[env, channel],
+ MAX_LENGTH_IBC,
+ )
+}
+
+pub fn call_ibc_channel_connect_raw(
+ instance: &mut Instance,
+ env: &[u8],
+ channel: &[u8],
+) -> VmResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+{
+ instance.set_storage_readonly(false);
+ call_raw(
+ instance,
+ "ibc_channel_connect",
+ &[env, channel],
+ MAX_LENGTH_IBC,
+ )
+}
+
+pub fn call_ibc_channel_close_raw(
+ instance: &mut Instance,
+ env: &[u8],
+ channel: &[u8],
+) -> VmResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+{
+ instance.set_storage_readonly(false);
+ call_raw(
+ instance,
+ "ibc_channel_close",
+ &[env, channel],
+ MAX_LENGTH_IBC,
+ )
+}
+
+pub fn call_ibc_packet_receive_raw(
+ instance: &mut Instance,
+ env: &[u8],
+ packet: &[u8],
+) -> VmResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+{
+ instance.set_storage_readonly(false);
+ call_raw(
+ instance,
+ "ibc_packet_receive",
+ &[env, packet],
+ MAX_LENGTH_IBC,
+ )
+}
+
+pub fn call_ibc_packet_ack_raw(
+ instance: &mut Instance,
+ env: &[u8],
+ ack: &[u8],
+) -> VmResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+{
+ instance.set_storage_readonly(false);
+ call_raw(instance, "ibc_packet_ack", &[env, ack], MAX_LENGTH_IBC)
+}
+
+pub fn call_ibc_packet_timeout_raw(
+ instance: &mut Instance,
+ env: &[u8],
+ packet: &[u8],
+) -> VmResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+{
+ instance.set_storage_readonly(false);
+ call_raw(
+ instance,
+ "ibc_packet_timeout",
+ &[env, packet],
+ MAX_LENGTH_IBC,
+ )
+}
diff --git a/packages/vm/src/lib.rs b/packages/vm/src/lib.rs
index fb57ba199f..71fdf86ccf 100644
--- a/packages/vm/src/lib.rs
+++ b/packages/vm/src/lib.rs
@@ -9,6 +9,8 @@ mod conversion;
mod environment;
mod errors;
mod features;
+#[cfg(feature = "stargate")]
+mod ibc_calls;
mod imports;
mod instance;
mod limited;
@@ -32,6 +34,13 @@ pub use crate::errors::{
VmError, VmResult,
};
pub use crate::features::features_from_csv;
+#[cfg(feature = "stargate")]
+pub use crate::ibc_calls::{
+ call_ibc_channel_close, call_ibc_channel_close_raw, call_ibc_channel_connect,
+ call_ibc_channel_connect_raw, call_ibc_channel_open, call_ibc_channel_open_raw,
+ call_ibc_packet_ack, call_ibc_packet_ack_raw, call_ibc_packet_receive,
+ call_ibc_packet_receive_raw, call_ibc_packet_timeout, call_ibc_packet_timeout_raw,
+};
pub use crate::instance::{GasReport, Instance, InstanceOptions};
pub use crate::serde::{from_slice, to_vec};
pub use crate::size::Size;
diff --git a/packages/vm/src/testing/ibc_calls.rs b/packages/vm/src/testing/ibc_calls.rs
new file mode 100644
index 0000000000..d109baa4f2
--- /dev/null
+++ b/packages/vm/src/testing/ibc_calls.rs
@@ -0,0 +1,117 @@
+#![cfg(feature = "stargate")]
+use schemars::JsonSchema;
+use serde::de::DeserializeOwned;
+use std::fmt;
+
+use cosmwasm_std::{
+ ContractResult, Env, IbcAcknowledgement, IbcBasicResponse, IbcChannel, IbcPacket,
+ IbcReceiveResponse,
+};
+
+use crate::ibc_calls::{
+ call_ibc_channel_close, call_ibc_channel_connect, call_ibc_channel_open, call_ibc_packet_ack,
+ call_ibc_packet_receive, call_ibc_packet_timeout,
+};
+use crate::instance::Instance;
+use crate::{Api, Querier, Storage};
+
+// ibc_channel_open mimicks the call signature of the smart contracts.
+// thus it moves env and channel rather than take them as reference.
+// this is inefficient here, but only used in test code
+pub fn ibc_channel_open(
+ instance: &mut Instance,
+ env: Env,
+ channel: IbcChannel,
+) -> ContractResult<()>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+{
+ call_ibc_channel_open(instance, &env, &channel).expect("VM error")
+}
+
+// ibc_channel_connect mimicks the call signature of the smart contracts.
+// thus it moves env and channel rather than take them as reference.
+// this is inefficient here, but only used in test code
+pub fn ibc_channel_connect(
+ instance: &mut Instance,
+ env: Env,
+ channel: IbcChannel,
+) -> ContractResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+ U: DeserializeOwned + Clone + PartialEq + JsonSchema + fmt::Debug,
+{
+ call_ibc_channel_connect(instance, &env, &channel).expect("VM error")
+}
+
+// ibc_channel_close mimicks the call signature of the smart contracts.
+// thus it moves env and channel rather than take them as reference.
+// this is inefficient here, but only used in test code
+pub fn ibc_channel_close(
+ instance: &mut Instance,
+ env: Env,
+ channel: IbcChannel,
+) -> ContractResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+ U: DeserializeOwned + Clone + PartialEq + JsonSchema + fmt::Debug,
+{
+ call_ibc_channel_close(instance, &env, &channel).expect("VM error")
+}
+
+// ibc_packet_receive mimicks the call signature of the smart contracts.
+// thus it moves env and packet rather than take them as reference.
+// this is inefficient here, but only used in test code
+pub fn ibc_packet_receive(
+ instance: &mut Instance,
+ env: Env,
+ packet: IbcPacket,
+) -> ContractResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+ U: DeserializeOwned + Clone + PartialEq + JsonSchema + fmt::Debug,
+{
+ call_ibc_packet_receive(instance, &env, &packet).expect("VM error")
+}
+
+// ibc_packet_ack mimicks the call signature of the smart contracts.
+// thus it moves env and acknowledgement rather than take them as reference.
+// this is inefficient here, but only used in test code
+pub fn ibc_packet_ack(
+ instance: &mut Instance,
+ env: Env,
+ ack: IbcAcknowledgement,
+) -> ContractResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+ U: DeserializeOwned + Clone + PartialEq + JsonSchema + fmt::Debug,
+{
+ call_ibc_packet_ack(instance, &env, &ack).expect("VM error")
+}
+
+// ibc_packet_timeout mimicks the call signature of the smart contracts.
+// thus it moves env and packet rather than take them as reference.
+// this is inefficient here, but only used in test code
+pub fn ibc_packet_timeout(
+ instance: &mut Instance,
+ env: Env,
+ packet: IbcPacket,
+) -> ContractResult>
+where
+ A: Api + 'static,
+ S: Storage + 'static,
+ Q: Querier + 'static,
+ U: DeserializeOwned + Clone + PartialEq + JsonSchema + fmt::Debug,
+{
+ call_ibc_packet_timeout(instance, &env, &packet).expect("VM error")
+}
diff --git a/packages/vm/src/testing/instance.rs b/packages/vm/src/testing/instance.rs
index 8d6ac75238..e1eec97fb1 100644
--- a/packages/vm/src/testing/instance.rs
+++ b/packages/vm/src/testing/instance.rs
@@ -92,6 +92,18 @@ pub struct MockInstanceOptions<'a> {
pub memory_limit: Option,
}
+impl MockInstanceOptions<'_> {
+ #[cfg(feature = "stargate")]
+ fn default_features() -> HashSet {
+ features_from_csv("staking,stargate")
+ }
+
+ #[cfg(not(feature = "stargate"))]
+ fn default_features() -> HashSet {
+ features_from_csv("staking")
+ }
+}
+
impl Default for MockInstanceOptions<'_> {
fn default() -> Self {
Self {
@@ -101,7 +113,7 @@ impl Default for MockInstanceOptions<'_> {
backend_error: None,
// instance
- supported_features: features_from_csv("staking"),
+ supported_features: Self::default_features(),
gas_limit: DEFAULT_GAS_LIMIT,
print_debug: DEFAULT_PRINT_DEBUG,
memory_limit: DEFAULT_MEMORY_LIMIT,
diff --git a/packages/vm/src/testing/mod.rs b/packages/vm/src/testing/mod.rs
index d47c13c093..a31bc76473 100644
--- a/packages/vm/src/testing/mod.rs
+++ b/packages/vm/src/testing/mod.rs
@@ -1,12 +1,18 @@
// The external interface is `use cosmwasm_vm::testing::X` for all integration testing symbols, no matter where they live internally.
mod calls;
+mod ibc_calls;
mod instance;
mod mock;
mod querier;
mod storage;
pub use calls::{handle, init, migrate, query};
+#[cfg(feature = "stargate")]
+pub use ibc_calls::{
+ ibc_channel_close, ibc_channel_connect, ibc_channel_open, ibc_packet_ack, ibc_packet_receive,
+ ibc_packet_timeout,
+};
pub use instance::{
mock_instance, mock_instance_options, mock_instance_with_balances,
mock_instance_with_failing_api, mock_instance_with_gas_limit, mock_instance_with_options,