Skip to content

Integrate module to module IBC messages #259

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

Merged
merged 46 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
9efa1f3
Started impl, first test creation
Kayanski Feb 16, 2024
5393958
formatting [skip ci]
Kayanski Feb 16, 2024
f449ec1
Restart impl
Kayanski Mar 6, 2024
4554f42
Added verifiedsender to ibc-callback
Kayanski Mar 6, 2024
81f1556
Modified implementation with InstalledModuleIdentification for all mo…
Kayanski Mar 6, 2024
883a09a
Added test
Kayanski Mar 7, 2024
76ed58d
Merged with main
Kayanski Mar 7, 2024
1515665
Module-to-module works
Kayanski Mar 7, 2024
658907b
Fixed tests and clippy
Kayanski Mar 7, 2024
c9c466a
Clippy
Kayanski Mar 7, 2024
301c373
Fixed test after error change
Kayanski Mar 7, 2024
de6a62e
Fixed error
Kayanski Mar 11, 2024
46d2920
Removed user initiated callbacks
Kayanski Mar 11, 2024
1eac1f8
Add source module in callback
Kayanski Mar 11, 2024
a75829f
Added callback handler identification
Kayanski Mar 11, 2024
2850733
Fixed tests
Kayanski Mar 11, 2024
4b9bcf9
Fixed tests
Kayanski Mar 11, 2024
62e836d
not use latest but current version for client and host verification
Kayanski Mar 11, 2024
6869f92
Removed cross-account calls
Kayanski Mar 11, 2024
43174c9
Fixed test
Kayanski Mar 11, 2024
502e4ef
Better addr getter for installed module
Kayanski Mar 11, 2024
ed19f5e
Added account + native module error
Kayanski Mar 12, 2024
d561c1c
rm unneeded clone
CyberHoward Mar 12, 2024
2085a47
use raw query for module install verification
CyberHoward Mar 12, 2024
e823031
update error msg
CyberHoward Mar 12, 2024
642a161
rm redundant type
CyberHoward Mar 12, 2024
1a686b5
Added docs
Kayanski Mar 13, 2024
539cfe6
Merged
Kayanski Mar 13, 2024
a7ea538
Some nits
Kayanski Mar 13, 2024
389706e
formatting [skip ci]
Kayanski Mar 13, 2024
6962d1e
Added error on no module ibc endpoint
Kayanski Mar 19, 2024
84f95e8
Fixed module queries and added calbackresult
Kayanski Mar 19, 2024
441dc3e
Merge with main
Kayanski Mar 19, 2024
867278a
Fixed clippies
Kayanski Mar 19, 2024
7777d60
Merge branch 'main' into feature/module-ibc
Kayanski Mar 25, 2024
4dea142
Removed cw-controllers too high version
Kayanski Mar 25, 2024
67d633f
Merge branch 'feature/module-ibc' of github.com:AbstractSDK/abstract …
Kayanski Mar 25, 2024
5ac11c5
Removed callback receiver
Kayanski Mar 25, 2024
6f5ece3
Added calling module security test
Kayanski Apr 23, 2024
4e77c7e
Clippy
Kayanski Apr 23, 2024
3c3606a
Merged with main
Kayanski Apr 23, 2024
2a5f2d3
Merge with main
Kayanski Apr 30, 2024
ad4ab6a
Simplified module IBC API
Kayanski Apr 30, 2024
a8afe4c
Merge remote-tracking branch 'origin/main' into feature/module-ibc
Kayanski Apr 30, 2024
f39fa36
formatting [skip ci]
Kayanski Apr 30, 2024
f9e778a
Double import fix
Kayanski Apr 30, 2024
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
165 changes: 136 additions & 29 deletions framework/contracts/native/ibc-client/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use abstract_core::{
ibc::CallbackInfo,
ibc_client::{
state::{IbcInfrastructure, IBC_INFRA, REVERSE_POLYTONE_NOTE},
IbcClientCallback,
IbcClientCallback, InstalledModuleIdentification,
},
ibc_host, manager,
manager::ModuleInstallConfig,
objects::{chain_name::ChainName, AccountId, AssetEntry},
ibc_host,
manager::{self, ModuleInstallConfig},
objects::{chain_name::ChainName, module_reference::ModuleReference, AccountId, AssetEntry},
version_control::AccountBase,
};
use abstract_sdk::{
Expand All @@ -22,8 +22,8 @@ use abstract_sdk::{
Resolve,
};
use cosmwasm_std::{
to_json_binary, wasm_execute, Coin, CosmosMsg, Deps, DepsMut, Empty, Env, IbcMsg, MessageInfo,
QueryRequest, Storage,
ensure_eq, to_json_binary, wasm_execute, Binary, Coin, CosmosMsg, Deps, DepsMut, Empty, Env,
IbcMsg, MessageInfo, QueryRequest, Storage,
};
use polytone::callbacks::CallbackRequest;

Expand Down Expand Up @@ -206,32 +206,37 @@ pub fn execute_send_packet(

let cfg = CONFIG.load(deps.storage)?;

// Verify that the sender is a proxy contract
let account_base = cfg
.version_control
.assert_proxy(&info.sender, &deps.querier)?;
// The packet we need to send depends on the action we want to execute

// get account_id
let account_id = account_base.account_id(deps.as_ref())?;

// Can only call non-internal actions
if let HostAction::Internal(_) = action {
return Err(IbcClientError::ForbiddenInternalCall {});
}
let note_message = match &action {
HostAction::Dispatch { .. } | HostAction::Helpers(_) => {
// Verify that the sender is a proxy contract
let account_base = cfg
.version_control
.assert_proxy(&info.sender, &deps.querier)?;

let callback_request = callback_info.map(|c| CallbackRequest {
receiver: env.contract.address.to_string(),
msg: to_json_binary(&IbcClientCallback::UserRemoteAction(c)).unwrap(),
});
// get account_id
let account_id = account_base.account_id(deps.as_ref())?;

let note_message = send_remote_host_action(
deps.as_ref(),
account_id,
account_base,
host_chain,
action,
callback_request,
)?;
let callback_request = callback_info.map(|c| CallbackRequest {
receiver: env.contract.address.to_string(),
msg: to_json_binary(&IbcClientCallback::UserRemoteAction(c)).unwrap(),
});

send_remote_host_action(
deps.as_ref(),
account_id,
account_base,
host_chain,
action,
callback_request,
)?
}
HostAction::Internal(_) => {
// Can only call non-internal actions
return Err(IbcClientError::ForbiddenInternalCall {});
}
};

Ok(IbcClientResponse::action("handle_send_msgs").add_message(note_message))
}
Expand All @@ -257,6 +262,108 @@ pub fn execute_send_query(
Ok(IbcClientResponse::action("handle_send_msgs").add_message(note_message))
}

/// Sends a packet with an optional callback.
/// This is the top-level function to do IBC related actions.
pub fn execute_send_module_to_module_packet(
deps: DepsMut,
env: Env,
info: MessageInfo,
host_chain: String,
source_module: InstalledModuleIdentification,
target_module: InstalledModuleIdentification,
msg: Binary,
callback_info: Option<CallbackInfo>,
) -> IbcClientResult {
let host_chain = ChainName::from_str(&host_chain)?;
let cfg = CONFIG.load(deps.storage)?;

// We make sure the source_module is indeed who they say they are
let registry = cfg
.version_control
.query_module(source_module.module_info.clone(), &deps.querier)?;

match registry.reference {
ModuleReference::AccountBase(_) => return Err(IbcClientError::Unauthorized {}),
ModuleReference::Native(_) => return Err(IbcClientError::Unauthorized {}),
ModuleReference::Adapter(addr) => {
// For adapters, we just need to verify they have the right address
if addr != info.sender {
return Err(IbcClientError::ForbiddenModuleCall {});
}
}
ModuleReference::App(code_id) | ModuleReference::Standalone(code_id) => {
// We verify the caller has indeed the right code id
let sender_code_id = deps
.querier
.query_wasm_contract_info(info.sender.clone())?
.code_id;
if code_id != sender_code_id {
return Err(IbcClientError::ForbiddenModuleCall {});
}
// If it does have the right code id, we verify the specified account has the app installed
let account_base = cfg.version_control.account_base(
&source_module
.account_id
.clone()
.ok_or(IbcClientError::ForbiddenModuleCall {})?,
&deps.querier,
)?;

let module_info: manager::ModuleAddressesResponse = deps.querier.query_wasm_smart(
account_base.manager,
&manager::QueryMsg::ModuleAddresses {
ids: vec![source_module.module_info.id()],
},
)?;
ensure_eq!(
module_info.modules[0].1,
info.sender,
IbcClientError::ForbiddenModuleCall {}
);
}
_ => unimplemented!(
"This module type didn't exist when implementing module-to-module interactions"
),
}

// We send a message to the target module on the remote chain
// Send this message via the Polytone implementation

let callback_request = callback_info.map(|c| CallbackRequest {
receiver: env.contract.address.to_string(),
msg: to_json_binary(&IbcClientCallback::ModuleRemoteAction {
sender_module: source_module.clone(),
callback_info: c,
})
.unwrap(),
});
let ibc_infra = IBC_INFRA.load(deps.storage, &host_chain)?;
let note_contract = ibc_infra.polytone_note;
let remote_ibc_host = ibc_infra.remote_abstract_host;

// message that will be called on the local note contract
let note_message = wasm_execute(
note_contract.to_string(),
&polytone_note::msg::ExecuteMsg::Execute {
msgs: vec![wasm_execute(
// The note's remote proxy will call the ibc host
remote_ibc_host,
&ibc_host::ExecuteMsg::ModuleExecute {
msg,
source_module,
target_module,
},
vec![],
)?
.into()],
callback: callback_request,
timeout_seconds: PACKET_LIFETIME.into(),
},
vec![],
)?;
Ok(IbcClientResponse::action("handle_send_module_to_module_packet").add_message(note_message))
}

/// Registers an Abstract Account on a remote chain.
pub fn execute_register_account(
deps: DepsMut,
Expand Down
16 changes: 16 additions & 0 deletions framework/contracts/native/ibc-client/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,22 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> I
ExecuteMsg::Callback(c) => {
ibc::receive_action_callback(deps, env, info, c).map_err(Into::into)
}
ExecuteMsg::ModuleIbcAction {
host_chain,
source_module,
target_module,
msg,
callback_info,
} => commands::execute_send_module_to_module_packet(
deps,
env,
info,
host_chain,
source_module,
target_module,
msg,
callback_info,
),
}
}

Expand Down
3 changes: 3 additions & 0 deletions framework/contracts/native/ibc-client/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ pub enum IbcClientError {
#[error("packages that contain internal calls are not allowed")]
ForbiddenInternalCall {},

#[error("A non-module package can execute an ibc module call")]
ForbiddenModuleCall {},

#[error("The host you are trying to connect is already connected")]
HostAlreadyExists {},

Expand Down
22 changes: 21 additions & 1 deletion framework/contracts/native/ibc-client/src/ibc.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use abstract_core::{
ibc::IbcResponseMsg,
ibc_client::{
state::{IBC_INFRA, REVERSE_POLYTONE_NOTE},
state::{CONFIG, IBC_INFRA, REVERSE_POLYTONE_NOTE},
IbcClientCallback,
},
objects::chain_name::ChainName,
Expand Down Expand Up @@ -100,5 +100,25 @@ pub fn receive_action_callback(
.add_attribute("chain", host_chain.to_string())
.add_attribute("callback_id", callback_info.id))
}
IbcClientCallback::ModuleRemoteAction {
callback_info,
sender_module,
} => {
// resolve the address for that sender module
let vc = CONFIG.load(deps.storage)?.version_control;

// TODO This will be used when the sender is part of the callback msg (https://github.com/AbstractSDK/abstract/pull/277)
let addr = sender_module.addr(deps.as_ref(), vc)?;

let callback = IbcResponseMsg {
id: callback_info.id.clone(),
msg: callback_info.msg,
result: callback.result,
};
Ok(IbcClientResponse::action("module_specific_callback")
.add_message(callback.into_cosmos_msg(callback_info.receiver)?)
.add_attribute("chain", host_chain.to_string())
.add_attribute("callback_id", callback_info.id))
}
}
}
10 changes: 9 additions & 1 deletion framework/contracts/native/ibc-host/src/endpoints/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use abstract_core::{
use abstract_sdk::{core::ibc_host::ExecuteMsg, feature_objects::VersionControlContract};
use cosmwasm_std::{DepsMut, Env, MessageInfo};

use super::packet::handle_host_action;
use super::packet::{handle_host_action, handle_host_module_action};
use crate::{
contract::{HostResponse, HostResult},
HostError,
Expand Down Expand Up @@ -44,6 +44,14 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> H
cw_ownable::update_ownership(deps, &env.block, &info.sender, action)?;
Ok(HostResponse::action("update_ownership"))
}
ExecuteMsg::ModuleExecute {
msg,
source_module,
target_module,
} => {
let client_chain: ChainName = REVERSE_CHAIN_PROXIES.load(deps.storage, &info.sender)?;
handle_host_module_action(deps, client_chain, source_module, target_module, msg)
}
}
}

Expand Down
52 changes: 49 additions & 3 deletions framework/contracts/native/ibc-host/src/endpoints/packet.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
use abstract_core::{
ibc::ModuleIbcMsg,
ibc_client::InstalledModuleIdentification,
ibc_host::{
state::{ActionAfterCreationCache, TEMP_ACTION_AFTER_CREATION},
state::{ActionAfterCreationCache, CONFIG, TEMP_ACTION_AFTER_CREATION},
HelperAction,
},
objects::{chain_name::ChainName, AccountId},
objects::{chain_name::ChainName, module_reference::ModuleReference, AccountId},
};
use abstract_sdk::core::ibc_host::{HostAction, InternalAction};
use cosmwasm_std::{DepsMut, Env};
use cosmwasm_std::{wasm_execute, Binary, DepsMut, Empty, Env, Response};

use crate::{
account_commands::{self, receive_dispatch, receive_register, receive_send_all_back},
contract::HostResult,
HostError,
};

use abstract_core::base::ExecuteMsg as MiddlewareExecMsg;

/// Handle actions that are passed to the IBC host contract
/// This function is not permissioned and access control needs to be handled outside of it
/// Usually the `client_chain` argument needs to be derived from the message sender
Expand Down Expand Up @@ -105,3 +110,44 @@ pub fn handle_host_action(
}
.map_err(Into::into)
}

/// Handle actions that are passed to the IBC host contract and originate from a registered module
pub fn handle_host_module_action(
deps: DepsMut,
client_chain: ChainName,
source_module: InstalledModuleIdentification,
target_module: InstalledModuleIdentification,
msg: Binary,
) -> HostResult {
// We resolve the target module
let vc = CONFIG.load(deps.storage)?.version_control;
let target_module_resolved =
vc.query_module(target_module.module_info.clone(), &deps.querier)?;

// We can't send module actions to accounts base and native apps
match target_module_resolved.reference {
ModuleReference::AccountBase(_) | ModuleReference::Native(_) => {
return Err(HostError::WrongModuleAction(
target_module.module_info.to_string(),
))
}
_ => (),
}

let target_addr = target_module.addr(deps.as_ref(), vc)?;

// We pass the message on to the module
let msg = wasm_execute(
target_addr,
&MiddlewareExecMsg::ModuleIbc::<Empty, Empty>(ModuleIbcMsg {
client_chain,
source_module,
msg,
}),
vec![],
)?;

Ok(Response::new()
.add_attribute("action", "module-ibc-call")
.add_message(msg))
}
16 changes: 15 additions & 1 deletion framework/contracts/native/ibc-host/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use abstract_core::{
objects::{ans_host::AnsHostError, version_control::VersionControlError},
objects::{ans_host::AnsHostError, version_control::VersionControlError, AccountId},
AbstractError,
};
use abstract_sdk::AbstractSdkError;
Expand Down Expand Up @@ -42,6 +42,20 @@ pub enum HostError {

#[error("Chain or proxy address already registered.")]
ProxyAddressExists {},

#[error("Can't send a module-to-module packet to {0}, wrong module type")]
WrongModuleAction(String),

#[error("Missing module {module_info} on account {account_id}")]
MissingModule {
module_info: String,
account_id: AccountId,
},

#[error(
"You need to specify an account id for an account-specific module (apps and standalone)"
)]
AccountIdNotSpecified {},
}

impl From<cw_semver::Error> for HostError {
Expand Down
1 change: 1 addition & 0 deletions framework/packages/abstract-adapter/src/endpoints.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod execute;
mod ibc_callback;
mod instantiate;
mod module_ibc;
mod query;
mod receive;
mod reply;
Expand Down
Loading