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(cw-xc): faster cosmos devnet with fix for interpreter instantiation #4041

Merged
merged 14 commits into from
Aug 10, 2023
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ RUN mkdir --parents /etc/nix/ && \
echo "cores = 32" >> /etc/nix/nix.conf && \
echo "allow-import-from-derivation = true" >> /etc/nix/nix.conf && \
echo "narinfo-cache-negative-ttl = 30" >> /etc/nix/nix.conf && \
echo "trusted-users = root vscode actiions-runner" >> /etc/nix/nix.conf && \
echo "trusted-users = root vscode actions-runner" >> /etc/nix/nix.conf && \
echo "substitute = true" >> /etc/nix/nix.conf && \
echo "substituters = https://cache.nixos.org/ https://composable-community.cachix.org/ https://devenv.cachix.org/ https://nix-community.cachix.org/" >> /etc/nix/nix.conf && \
echo "require-sigs = false" >> /etc/nix/nix.conf && \
Expand Down
6 changes: 3 additions & 3 deletions code/xcvm/cosmwasm/contracts/gateway/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ impl Auth<policy::Interpreter> {
info: &MessageInfo,
interpreter_origin: xc_core::InterpreterOrigin,
) -> Result<Self> {
let interpreter_address = state::interpreter::INTERPRETERS
.may_load(deps.storage, interpreter_origin)?
.map(|int| int.address);
let interpreter_address = state::interpreter::get_by_origin(deps, interpreter_origin)
.map(|int| int.address)
.ok();
Self::new(Some(&info.sender) == interpreter_address.as_ref())
}
}
Expand Down
87 changes: 21 additions & 66 deletions code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
use crate::{
assets, auth,
contract::INSTANTIATE_INTERPRETER_REPLY_ID,
error::{ContractError, Result},
events::make_event,
msg,
interpreter, msg,
network::{self, load_this},
state,
};

use cosmwasm_std::{
entry_point, to_binary, wasm_execute, Addr, BankMsg, Coin, CosmosMsg, Deps, DepsMut, Env,
MessageInfo, Reply, Response, StdError, StdResult, SubMsg, WasmMsg,
entry_point, wasm_execute, Addr, BankMsg, Coin, CosmosMsg, Deps, DepsMut, Env, MessageInfo,
Response,
};
use cw20::{Cw20Contract, Cw20ExecuteMsg};
use cw_xc_interpreter::contract::{
XCVM_INTERPRETER_EVENT_DATA_ORIGIN, XCVM_INTERPRETER_EVENT_PREFIX,
};

use xc_core::{gateway::ConfigSubMsg, CallOrigin, Displayed, Funds, InterpreterOrigin};

Expand All @@ -25,7 +21,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: msg::ExecuteMsg)
match msg {
ExecuteMsg::Config(msg) => {
let auth = auth::Admin::authorise(deps.as_ref(), &info)?;
handle_config_msg(auth, deps, msg)
handle_config_msg(auth, deps, msg, env)
},

msg::ExecuteMsg::ExecuteProgram { execute_program, tip } =>
Expand All @@ -47,14 +43,15 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: msg::ExecuteMsg)
}
},
msg::ExecuteMsg::MessageHook(msg) => {
deps.api.debug(&serde_json_wasm::to_string(&msg)?);
let auth = auth::WasmHook::authorise(deps.storage, &env, &info, msg.from_network_id)?;
super::ibc::ics20::ics20_message_hook(auth, msg, env, info)
},
msg::ExecuteMsg::Shortcut(msg) => handle_shortcut(deps, env, info, msg),
}
}

fn handle_config_msg(auth: auth::Admin, deps: DepsMut, msg: ConfigSubMsg) -> Result {
fn handle_config_msg(auth: auth::Admin, deps: DepsMut, msg: ConfigSubMsg, env: Env) -> Result {
deps.api.debug(serde_json_wasm::to_string(&msg)?.as_str());
match msg {
ConfigSubMsg::ForceNetworkToNetwork(msg) =>
Expand All @@ -63,6 +60,8 @@ fn handle_config_msg(auth: auth::Admin, deps: DepsMut, msg: ConfigSubMsg) -> Res
ConfigSubMsg::ForceRemoveAsset { asset_id } =>
assets::force_remove_asset(auth, deps, asset_id),
ConfigSubMsg::ForceNetwork(msg) => network::force_network(auth, deps, msg),
ConfigSubMsg::ForceInstantiate { user_origin, salt } =>
interpreter::force_instantiate(auth, env.contract.address, deps, user_origin, salt),
}
}

Expand Down Expand Up @@ -154,10 +153,10 @@ pub(crate) fn handle_execute_program_privilleged(
) -> Result {
let config = load_this(deps.storage)?;
let interpreter_origin =
InterpreterOrigin { user_origin: call_origin.user(config.network_id), salt };
InterpreterOrigin { user_origin: call_origin.user(config.network_id), salt: salt.clone() };
let interpreter =
state::interpreter::INTERPRETERS.may_load(deps.storage, interpreter_origin.clone())?;
if let Some(state::interpreter::Interpreter { address }) = interpreter {
state::interpreter::get_by_origin(deps.as_ref(), interpreter_origin.clone()).ok();
if let Some(state::interpreter::Interpreter { address, .. }) = interpreter {
deps.api.debug("reusing existing interpreter and adding funds");
let response = send_funds_to_interpreter(deps.as_ref(), address.clone(), assets)?;
let wasm_msg = wasm_execute(
Expand All @@ -176,22 +175,17 @@ pub(crate) fn handle_execute_program_privilleged(
let interpreter_code_id = match config.gateway.expect("expected setup") {
msg::GatewayId::CosmWasm { interpreter_code_id, .. } => interpreter_code_id,
};
deps.api.debug("instantiating interpreter");
let admin = env.contract.address.clone();

let interpreter_instantiate_submessage = crate::interpreter::instantiate(
deps.as_ref(),
admin,
interpreter_code_id,
&interpreter_origin,
salt,
)?;

let instantiate_msg: CosmosMsg = WasmMsg::Instantiate {
// router is the default admin of a contract
admin: Some(env.contract.address.clone().into_string()),
code_id: interpreter_code_id,
msg: to_binary(&cw_xc_interpreter::msg::InstantiateMsg {
gateway_address: env.contract.address.clone().into_string(),
interpreter_origin: interpreter_origin.clone(),
})?,
funds: vec![],
label: format!("xcvm-interpreter-{interpreter_origin}"),
}
.into();

let interpreter_instantiate_submessage =
SubMsg::reply_on_success(instantiate_msg, INSTANTIATE_INTERPRETER_REPLY_ID);
// Secondly, call itself again with the same parameters, so that this functions goes
// into `Ok` state and properly executes the interpreter
let self_call_message: CosmosMsg = wasm_execute(
Expand Down Expand Up @@ -248,42 +242,3 @@ fn send_funds_to_interpreter(
}
Ok(response)
}

pub(crate) fn handle_instantiate_reply(deps: DepsMut, msg: Reply) -> StdResult<Response> {
let response = msg.result.into_result().map_err(StdError::generic_err)?;

// Catch the default `instantiate` event which contains `_contract_address` attribute that
// has the instantiated contract's address
let address = &response
.events
.iter()
.find(|event| event.ty == "instantiate")
.ok_or_else(|| StdError::not_found("instantiate event not found"))?
.attributes
.iter()
.find(|attr| &attr.key == "_contract_address")
.ok_or_else(|| StdError::not_found("_contract_address attribute not found"))?
.value;
let interpreter_address = deps.api.addr_validate(address)?;

// Interpreter provides `network_id, user_id` pair as an event for the router to know which
// pair is instantiated
let event_name = format!("wasm-{}", XCVM_INTERPRETER_EVENT_PREFIX);
let interpreter_origin = &response
.events
.iter()
.find(|event| event.ty.starts_with(&event_name))
.ok_or_else(|| StdError::not_found("interpreter event not found"))?
.attributes
.iter()
.find(|attr| &attr.key == XCVM_INTERPRETER_EVENT_DATA_ORIGIN)
.ok_or_else(|| StdError::not_found("no data is returned from 'xcvm_interpreter'"))?
.value;
let interpreter_origin =
xc_core::shared::decode_base64::<_, InterpreterOrigin>(interpreter_origin.as_str())?;

let interpreter = state::interpreter::Interpreter { address: interpreter_address };
state::interpreter::INTERPRETERS.save(deps.storage, interpreter_origin, &interpreter)?;

Ok(Response::new())
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use cosmwasm_std::{
};
use xc_core::{
gateway::{AssetItem, ExecuteMsg, ExecuteProgramMsg, GatewayId, OtherNetworkItem},
proto::decode_packet,
shared::{XcPacket, XcProgram},
transport::ibc::{to_cw_message, IbcRoute, XcMessageData},
AssetId, CallOrigin,
Expand All @@ -29,6 +28,7 @@ pub(crate) fn handle_bridge_forward(
info: MessageInfo,
msg: xc_core::gateway::BridgeForwardMsg,
) -> Result {
deps.api.debug(&serde_json_wasm::to_string(&msg)?);
ensure_eq!(msg.msg.assets.0.len(), 1, ContractError::ProgramCannotBeHandledByDestination);
// algorithm to handle for multihop
// 1. recurse on program until can with memo
Expand All @@ -45,7 +45,7 @@ pub(crate) fn handle_bridge_forward(
let (local_asset, amount) = packet.assets.0.get(0).expect("proved above");

let route = get_route(deps.storage, msg.to, *local_asset)?;

deps.api.debug(&serde_json_wasm::to_string(&route)?);
let mut event = make_event("bridge")
.add_attribute("to_network_id", msg.to.to_string())
.add_attribute(
Expand Down Expand Up @@ -121,7 +121,7 @@ pub(crate) fn ics20_message_hook(
env: Env,
info: MessageInfo,
) -> Result<Response, ContractError> {
let packet: XcPacket = decode_packet(&msg.data).map_err(ContractError::Protobuf)?;
let packet: XcPacket = msg.packet;

ensure_anonymous(&packet.program)?;
let call_origin = CallOrigin::Remote { user_origin: packet.user_origin };
Expand Down
4 changes: 2 additions & 2 deletions code/xcvm/cosmwasm/contracts/gateway/src/contract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use cw2::set_contract_version;
use cw_utils::ensure_from_older_version;
use xc_core::XCVMAck;

use self::{execute::handle_instantiate_reply, ibc::make_ibc_failure_event};
use self::ibc::make_ibc_failure_event;

const CONTRACT_NAME: &str = "composable:xcvm-gateway";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
Expand Down Expand Up @@ -57,7 +57,7 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response> {
match msg.id {
EXEC_PROGRAM_REPLY_ID => handle_exec_reply(msg),
INSTANTIATE_INTERPRETER_REPLY_ID =>
handle_instantiate_reply(deps, msg).map_err(ContractError::from),
crate::interpreter::handle_instantiate_reply(deps, msg).map_err(ContractError::from),
_ => Err(ContractError::UnknownReply),
}
}
Expand Down
130 changes: 130 additions & 0 deletions code/xcvm/cosmwasm/contracts/gateway/src/interpreter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use crate::{
contract::INSTANTIATE_INTERPRETER_REPLY_ID,
error::{ContractError, Result},
events::make_event,
network::load_this,
state,
};
use cosmwasm_std::{
to_binary, Deps, DepsMut, Reply, Response, StdError, StdResult, SubMsg, WasmMsg,
};
use cw_xc_interpreter::contract::{
XCVM_INTERPRETER_EVENT_DATA_ORIGIN, XCVM_INTERPRETER_EVENT_PREFIX,
};
use xc_core::{CallOrigin, InterpreterOrigin};

use crate::{auth, prelude::*};

pub(crate) fn force_instantiate(
_: auth::Admin,
gateway: Addr,
deps: DepsMut,
user_origin: Addr,
salt: Option<String>,
) -> Result {
let config = load_this(deps.storage)?;
let interpreter_code_id = match config.gateway.expect("expected setup") {
GatewayId::CosmWasm { interpreter_code_id, .. } => interpreter_code_id,
};

let call_origin = CallOrigin::Local { user: user_origin };
let interpreter_origin = InterpreterOrigin {
user_origin: call_origin.user(config.network_id),
salt: salt.clone().map(|x| x.into_bytes()).unwrap_or_default(),
dzmitry-lahoda marked this conversation as resolved.
Show resolved Hide resolved
};
let msg = instantiate(
deps.as_ref(),
gateway,
interpreter_code_id,
&interpreter_origin,
salt.map(|x| x.into_bytes()).unwrap_or_default(),
)?;
Ok(Response::new().add_submessage(msg).add_event(make_event("interpreter.forced")))
}

pub fn instantiate(
deps: Deps,
admin: Addr,
interpreter_code_id: u64,
interpreter_origin: &InterpreterOrigin,
salt: Vec<u8>,
) -> Result<SubMsg, ContractError> {
let next_interpreter_id: u128 =
state::interpreter::INTERPRETERS_COUNT.load(deps.storage).unwrap_or_default() + 1;

let instantiate_msg = WasmMsg::Instantiate2 {
admin: Some(admin.clone().into_string()),
code_id: interpreter_code_id,
msg: to_binary(&cw_xc_interpreter::msg::InstantiateMsg {
gateway_address: admin.clone().into_string(),
interpreter_origin: interpreter_origin.clone(),
})?,
funds: vec![],
// and label has some unknown limits (including usage of special characters)
label: format!("xcvm_interpreter_{}", &next_interpreter_id),
// salt limit is 64 characters
salt: to_binary(&salt)?,
};
let interpreter_instantiate_submessage =
SubMsg::reply_on_success(instantiate_msg, INSTANTIATE_INTERPRETER_REPLY_ID);
Ok(interpreter_instantiate_submessage)
}

pub(crate) fn handle_instantiate_reply(deps: DepsMut, msg: Reply) -> StdResult<Response> {
deps.api.debug(&format!(
"xcvm:: {}",
serde_json_wasm::to_string(&msg).map_err(|e| StdError::generic_err(e.to_string()))?
));
let response = msg.result.into_result().map_err(StdError::generic_err)?;
// Catch the default `instantiate` event which contains `_contract_address` attribute that
// has the instantiated contract's address
let address = &response
.events
.iter()
.find(|event| event.ty == "instantiate")
.ok_or_else(|| StdError::not_found("instantiate event not found"))?
.attributes
.iter()
.find(|attr| &attr.key == "_contract_address")
.ok_or_else(|| StdError::not_found("_contract_address attribute not found"))?
.value;
let interpreter_address = deps.api.addr_validate(address)?;

// Interpreter provides `network_id, user_id` pair as an event for the router to know which
// pair is instantiated
let event_name = format!("wasm-{}", XCVM_INTERPRETER_EVENT_PREFIX);
let interpreter_origin = &response
.events
.iter()
.find(|event| event.ty.starts_with(&event_name))
.ok_or_else(|| StdError::not_found("interpreter event not found"))?
.attributes
.iter()
.find(|attr| &attr.key == XCVM_INTERPRETER_EVENT_DATA_ORIGIN)
.ok_or_else(|| StdError::not_found("no data is returned from 'xcvm_interpreter'"))?
.value;
let interpreter_origin =
xc_core::shared::decode_base64::<_, InterpreterOrigin>(interpreter_origin.as_str())?;

let interpreter_id: u128 =
state::interpreter::INTERPRETERS_COUNT.load(deps.storage).unwrap_or_default() + 1;
let interpreter = state::interpreter::Interpreter {
address: interpreter_address,
interpreter_id: interpreter_id.into(),
};

state::interpreter::INTERPRETERS_COUNT.save(deps.storage, &interpreter_id)?;
state::interpreter::INTERPRETERS.save(deps.storage, interpreter_id, &interpreter)?;
state::interpreter::INTERPRETERS_ORIGIN_TO_ID.save(
deps.storage,
interpreter_origin,
&interpreter_id,
)?;

deps.api.debug("xcvm:: saved interpreter");

Ok(Response::new().add_event(
make_event("xcvm.interpreter.instantiated")
.add_attribute("interpreter_id", interpreter_id.to_string()),
))
}
1 change: 1 addition & 0 deletions code/xcvm/cosmwasm/contracts/gateway/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod auth;
pub mod contract;
pub mod error;
mod events;
pub mod interpreter;
mod network;
mod prelude;
pub mod state;
19 changes: 17 additions & 2 deletions code/xcvm/cosmwasm/contracts/gateway/src/state/interpreter.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
use xc_core::InterpreterOrigin;
use cosmwasm_std::{Deps, StdResult};
use cw_storage_plus::Item;
use xc_core::{Displayed, InterpreterOrigin};

use crate::prelude::*;

pub type InterpreterId = Displayed<u128>;

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
pub(crate) struct Interpreter {
pub address: Addr,
pub interpreter_id: InterpreterId,
}

pub(crate) fn get_by_origin(deps: Deps, origin: InterpreterOrigin) -> StdResult<Interpreter> {
let id = INTERPRETERS_ORIGIN_TO_ID.load(deps.storage, origin)?;
INTERPRETERS.load(deps.storage, id)
}

pub(crate) const INTERPRETERS: Map<InterpreterOrigin, Interpreter> = Map::new("interpreters");
pub(crate) const INTERPRETERS_COUNT: Item<u128> = Item::new("interpreter_count");

pub(crate) const INTERPRETERS_ORIGIN_TO_ID: Map<InterpreterOrigin, u128> =
Map::new("interpreters_origin_to_id");

pub(crate) const INTERPRETERS: Map<u128, Interpreter> = Map::new("interpreters");
Loading
Loading