diff --git a/Dockerfile b/Dockerfile index 6babc8a84de..088b7570a3e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 && \ diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/auth.rs b/code/xcvm/cosmwasm/contracts/gateway/src/auth.rs index 249b582850d..17d3f488dc1 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/auth.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/auth.rs @@ -70,9 +70,9 @@ impl Auth { info: &MessageInfo, interpreter_origin: xc_core::InterpreterOrigin, ) -> Result { - 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()) } } diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs b/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs index 5bfaf936fc6..eb3e289a63f 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs @@ -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}; @@ -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 } => @@ -47,6 +43,7 @@ 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) }, @@ -54,7 +51,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: msg::ExecuteMsg) } } -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) => @@ -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), } } @@ -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( @@ -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( @@ -248,42 +242,3 @@ fn send_funds_to_interpreter( } Ok(response) } - -pub(crate) fn handle_instantiate_reply(deps: DepsMut, msg: Reply) -> StdResult { - 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()) -} diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/ics20.rs b/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/ics20.rs index dbfb3f65e87..ed0888d8043 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/ics20.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/ics20.rs @@ -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, @@ -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 @@ -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( @@ -121,7 +121,7 @@ pub(crate) fn ics20_message_hook( env: Env, info: MessageInfo, ) -> Result { - 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 }; diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/contract/mod.rs b/code/xcvm/cosmwasm/contracts/gateway/src/contract/mod.rs index 4ce7e91cad3..82c2acda6f8 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/contract/mod.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/contract/mod.rs @@ -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"); @@ -57,7 +57,7 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { 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), } } diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/interpreter.rs b/code/xcvm/cosmwasm/contracts/gateway/src/interpreter.rs new file mode 100644 index 00000000000..9c9fd63fdb0 --- /dev/null +++ b/code/xcvm/cosmwasm/contracts/gateway/src/interpreter.rs @@ -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, +) -> 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(), + }; + 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, +) -> Result { + 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 { + 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()), + )) +} diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/lib.rs b/code/xcvm/cosmwasm/contracts/gateway/src/lib.rs index c3a280e4d16..798d57c19d9 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/lib.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/lib.rs @@ -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; diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/state/interpreter.rs b/code/xcvm/cosmwasm/contracts/gateway/src/state/interpreter.rs index 6da9432dc1c..c9e88669eb6 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/state/interpreter.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/state/interpreter.rs @@ -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; + #[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 { + let id = INTERPRETERS_ORIGIN_TO_ID.load(deps.storage, origin)?; + INTERPRETERS.load(deps.storage, id) } -pub(crate) const INTERPRETERS: Map = Map::new("interpreters"); +pub(crate) const INTERPRETERS_COUNT: Item = Item::new("interpreter_count"); + +pub(crate) const INTERPRETERS_ORIGIN_TO_ID: Map = + Map::new("interpreters_origin_to_id"); + +pub(crate) const INTERPRETERS: Map = Map::new("interpreters"); diff --git a/code/xcvm/lib/core/src/gateway/config.rs b/code/xcvm/lib/core/src/gateway/config.rs index 3a4d8030d61..4b5e6ed781d 100644 --- a/code/xcvm/lib/core/src/gateway/config.rs +++ b/code/xcvm/lib/core/src/gateway/config.rs @@ -161,6 +161,10 @@ pub enum ConfigSubMsg { /// Message sent by an admin to remove an asset from registry. ForceRemoveAsset { asset_id: AssetId }, + + /// instantiates default interpreter on behalf of user + /// `salt` - human string, converted to hex or base64 depending on implementation + ForceInstantiate { user_origin: Addr, salt: Option }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] diff --git a/code/xcvm/lib/core/src/gateway/mod.rs b/code/xcvm/lib/core/src/gateway/mod.rs index 270b39c17db..205bd5c2299 100644 --- a/code/xcvm/lib/core/src/gateway/mod.rs +++ b/code/xcvm/lib/core/src/gateway/mod.rs @@ -274,4 +274,83 @@ mod tests { .unwrap(); assert_eq!(program, expected) } + + #[test] + fn osmosis_spawn_with_asset() { + let osmo_on_osmosis = generate_asset_id(3.into(), 0, 1001); + let osmo_on_centauri = generate_asset_id(2.into(), 0, 1001); + let program: ExecuteMsg = ExecuteMsg::ExecuteProgram { + execute_program: ExecuteProgramMsg { + salt: b"spawn_with_asset".to_vec(), + program: XcProgram { + tag: b"spawn_with_asset".to_vec(), + instructions: [Instruction::Spawn { + network: 2.into(), + salt: b"spawn_with_asset".to_vec(), + assets: vec![(osmo_on_centauri, 1_000_000_000u128)].into(), + program: XcProgram { + tag: b"spawn_with_asset".to_vec(), + instructions: [].into(), + }, + }] + .into(), + }, + assets: vec![(osmo_on_osmosis, 1_000_000_000u128)].into(), + }, + tip: Addr::unchecked("osmo12smx2wdlyttvyzvzg54y2vnqwq2qjatescq89n"), + }; + + let program = serde_json_wasm::to_string(&program).expect("serde"); + //assert_eq!(program, "123"); + let expected = serde_json_wasm::to_string( + &serde_json_wasm::from_str::( + r#" + { + "execute_program": { + "execute_program": { + "salt": "737061776e5f776974685f6173736574", + "program": { + "tag": "737061776e5f776974685f6173736574", + "instructions": [ + { + "spawn": { + "network": 2, + "salt": "737061776e5f776974685f6173736574", + "assets": [ + [ + "158456325028528675187087901673", + { + "amount": { + "intercept": "1000000000", + "slope": "0" + }, + "is_unit": false + } + ] + ], + "program": { + "tag": "737061776e5f776974685f6173736574", + "instructions": [] + } + } + } + ] + }, + "assets": [ + [ + "237684487542793012780631852009", + "1000000000" + ] + ] + }, + "tip": "osmo12smx2wdlyttvyzvzg54y2vnqwq2qjatescq89n" + } + } + "#, + ) + .unwrap(), + ) + .unwrap(); + assert_eq!(program, expected) + } } diff --git a/code/xcvm/lib/core/src/gateway/sad.json b/code/xcvm/lib/core/src/gateway/sad.json new file mode 100644 index 00000000000..60b4b6b05d2 --- /dev/null +++ b/code/xcvm/lib/core/src/gateway/sad.json @@ -0,0 +1 @@ +"{\"execute_program\":{\"execute_program\":{\"salt\":\"737061776e5f776974685f6173736574\",\"program\":{\"tag\":\"737061776e5f776974685f6173736574\",\"instructions\":[{\"spawn\":{\"network\":2,\"salt\":\"737061776e5f776974685f6173736574\",\"assets\":[[\"237684487542793012780631852009\",{\"amount\":{\"intercept\":\"1000000000\",\"slope\":\"0\"},\"is_unit\":false}]],\"program\":{\"tag\":\"737061776e5f776974685f6173736574\",\"instructions\":[]}}}]},\"assets\":[[\"237684487542793012780631852009\",\"1000000000\"]]},\"tip\":\"osmo12smx2wdlyttvyzvzg54y2vnqwq2qjatescq89n\"}} \ No newline at end of file diff --git a/code/xcvm/lib/core/src/transport/ibc/mod.rs b/code/xcvm/lib/core/src/transport/ibc/mod.rs index 3a4ec890a9a..3ca223921bd 100644 --- a/code/xcvm/lib/core/src/transport/ibc/mod.rs +++ b/code/xcvm/lib/core/src/transport/ibc/mod.rs @@ -1,7 +1,7 @@ pub mod ics20; pub mod picasso; -use crate::{prelude::*, proto::Encodable, shared::XcPacket, AssetId, NetworkId}; +use crate::{prelude::*, shared::XcPacket, AssetId, NetworkId}; use cosmwasm_std::{to_binary, CosmosMsg, IbcEndpoint, IbcTimeout, StdResult, WasmMsg}; use ibc_rs_scale::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; @@ -19,7 +19,7 @@ use self::ics20::{ #[cfg_attr(feature = "std", derive(schemars::JsonSchema))] pub struct XcMessageData { pub from_network_id: NetworkId, - pub data: Binary, + pub packet: XcPacket, } /// Message type for `sudo` entry_point @@ -31,7 +31,9 @@ pub enum SudoMsg { IBCLifecycleComplete(IBCLifecycleComplete), } +/// route is used to describe how to send a packet to another network #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "std", derive(schemars::JsonSchema))] #[serde(rename_all = "snake_case")] pub struct IbcRoute { pub from_network: NetworkId, @@ -45,13 +47,11 @@ pub struct IbcRoute { } pub fn to_cw_message(coin: Coin, route: IbcRoute, packet: XcPacket) -> StdResult> { - let memo = - XcMessageData { from_network_id: route.from_network, data: Binary::from(packet.encode()) }; + let memo = XcMessageData { from_network_id: route.from_network, packet }; let memo = SendMemo { inner: Memo { wasm: Some(Callback { contract: route.gateway_to_send_to.clone(), - msg: serde_cw_value::to_value(memo).expect("can always serde"), }), forward: None, diff --git a/flake/cosmos.nix b/flake/cosmos.nix index 7ddc85b6216..c2589297dbd 100644 --- a/flake/cosmos.nix +++ b/flake/cosmos.nix @@ -15,6 +15,14 @@ osmosis = "osmo12smx2wdlyttvyzvzg54y2vnqwq2qjateuf7thj"; }; + xcvm = { + mnemonic = + "apart ahead month tennis merge canvas possible cannon lady reward traffic city hamster monitor lesson nasty midnight sniff enough spatial rare multiply keep task"; + centauri = "centauri1qq0k7d56juu7h49arelzgw09jccdk8sujrcrjd"; + key = "A03mRJjzKKa8+4INiSDSdIzaMuA1nhbNs/B0fOVLlYNI"; + moniker = "xcvm"; + osmosis = "osmo1qq0k7d56juu7h49arelzgw09jccdk8su7jx0qv"; + }; }; }; } diff --git a/flake/osmosis.nix b/flake/osmosis.nix index bdb057172ce..badc1affb92 100644 --- a/flake/osmosis.nix +++ b/flake/osmosis.nix @@ -103,6 +103,7 @@ add-genesis-account "$VALIDATOR_MNEMONIC" "$VALIDATOR_MONIKER" add-genesis-account "$FAUCET_MNEMONIC" "faucet" add-genesis-account "$RELAYER_MNEMONIC" "relayer" + add-genesis-account "${cosmosTools.xcvm.mnemonic}" "xcvm" osmosisd gentx $VALIDATOR_MONIKER 500000000uosmo --keyring-backend=test --chain-id=$CHAIN_ID --home "$CHAIN_DATA" osmosisd collect-gentxs --home "$CHAIN_DATA" @@ -133,7 +134,7 @@ dasel put --type string --file $CONFIG_FOLDER/client.toml --value "test" '.keyring-backend' dasel put --type string --file $CONFIG_FOLDER/client.toml --value "json" '.output' - osmosisd start --home "$CHAIN_DATA" --rpc.unsafe --rpc.laddr tcp://0.0.0.0:$PORT --pruning=nothing --grpc.address localhost:19090 --address "tcp://0.0.0.0:36658" --p2p.external-address 43421 --p2p.laddr "tcp://0.0.0.0:36656" --p2p.pex false --p2p.upnp false --p2p.seed_mode true --log_level trace + osmosisd start --home "$CHAIN_DATA" --rpc.unsafe --rpc.laddr tcp://0.0.0.0:$PORT --pruning=nothing --grpc.address localhost:19090 --address "tcp://0.0.0.0:36658" --p2p.external-address 43421 --p2p.laddr "tcp://0.0.0.0:36656" --p2p.pex false --p2p.upnp false --p2p.seed_mode true --log_level trace --trace ''; }; @@ -151,7 +152,6 @@ runtimeInputs = devnetTools.withBaseContainerTools ++ [ osmosisd pkgs.jq pkgs.dasel ]; text = '' - # shellcheck disable=SC2034 HOME=/tmp/composable-devnet export HOME CHAIN_DATA="$HOME/.osmosisd" @@ -160,35 +160,35 @@ PORT=36657 BLOCK_SECONDS=5 FEE=uosmo - NETWORK_ID=4 - VALIDATOR_KEY=${validator-key} + NETWORK_ID=3 + KEY=${cosmosTools.xcvm.osmosis} BINARY=osmosisd - function init_xcvm() { + function init_xcvm() { local INSTANTIATE=$1 - echo $VALIDATOR_KEY echo $NETWORK_ID - "$BINARY" tx wasm store "${self'.packages.xc-cw-contracts}/lib/cw_xc_gateway.wasm" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" + "$BINARY" tx wasm store "${self'.packages.xc-cw-contracts}/lib/cw_xc_gateway.wasm" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" GATEWAY_CODE_ID=1 sleep $BLOCK_SECONDS - "$BINARY" tx wasm store "${self'.packages.xc-cw-contracts}/lib/cw_xc_interpreter.wasm" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" + "$BINARY" tx wasm store "${self'.packages.xc-cw-contracts}/lib/cw_xc_interpreter.wasm" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" INTERPRETER_CODE_ID=2 sleep $BLOCK_SECONDS - "$BINARY" tx wasm store "${self'.packages.cw20_base}" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" + "$BINARY" tx wasm store "${self'.packages.cw20_base}" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" sleep $BLOCK_SECONDS - "$BINARY" tx wasm instantiate2 $GATEWAY_CODE_ID "$INSTANTIATE" "1234" --label "xc-gateway" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --admin "$VALIDATOR_KEY" + "$BINARY" tx wasm instantiate2 $GATEWAY_CODE_ID "$INSTANTIATE" "1234" --label "xc-gateway" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" --admin "$KEY" sleep $BLOCK_SECONDS GATEWAY_CONTRACT_ADDRESS=$("$BINARY" query wasm list-contract-by-code "$GATEWAY_CODE_ID" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --home "$CHAIN_DATA" | dasel --read json '.contracts.[0]' --write yaml) + echo "$GATEWAY_CONTRACT_ADDRESS" > "$CHAIN_DATA/gateway_contract_address" } INSTANTIATE=$(cat << EOF { - "admin" : "$VALIDATOR_KEY", + "admin" : "$KEY", "here_id" : $NETWORK_ID } EOF @@ -208,7 +208,7 @@ "cosm_wasm": { "contract": "$GATEWAY_CONTRACT_ADDRESS", "interpreter_code_id": $INTERPRETER_CODE_ID, - "admin": "${validator-key}" + "admin": "$KEY" } }, "ibc": { @@ -229,7 +229,7 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_NETWORK_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_NETWORK_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS FORCE_NETWORK_CENTAURI=$(cat << EOF @@ -244,7 +244,7 @@ "cosm_wasm": { "contract": "$GATEWAY_CONTRACT_ADDRESS", "interpreter_code_id": $INTERPRETER_CODE_ID, - "admin": "${validator-key}" + "admin": "$KEY" } }, "ibc": { @@ -265,7 +265,7 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_NETWORK_CENTAURI" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_NETWORK_CENTAURI" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS @@ -290,7 +290,7 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_CENTAURI_TO_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_CENTAURI_TO_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS @@ -318,19 +318,19 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_PICA" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_PICA" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS - FORCE_UATOM=$(cat << EOF + FORCE_OSMO_DIRECT_ON_CENTAURI=$(cat << EOF { "config": { "force_asset": { - "asset_id": "237684487542793012780631851010", + "asset_id": "158456325028528675187087901673", "from_network_id": 3, "local": { "native": { - "denom" : "uatom" + "denom" : "uosmo" } } } @@ -338,12 +338,100 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_UATOM" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_OSMO_DIRECT_ON_CENTAURI" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" --trace --log_level trace + + sleep $BLOCK_SECONDS + FORCE_OSMO_ON_OSMOSIS=$(cat << EOF + { + "config": { + "force_asset": { + "asset_id": "237684487542793012780631852009", + "from_network_id": 3, + "local": { + "native": { + "denom" : "uosmo" + } + } + } + } + } + EOF + ) + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_OSMO_ON_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" --trace --log_level trace + sleep $BLOCK_SECONDS "$BINARY" query wasm contract-state all "$GATEWAY_CONTRACT_ADDRESS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --home "$CHAIN_DATA" ''; }; + + xc-transfer-osmo-from--osmosis-to-centauri = + pkgs.writeShellApplication { + name = "xc-transfer-osmo-from--osmosis-to-centauri"; + runtimeInputs = devnetTools.withBaseContainerTools + ++ [ osmosisd pkgs.jq ]; + text = '' + HOME=/tmp/composable-devnet + export HOME + CHAIN_DATA="$HOME/.osmosisd" + KEYRING_TEST=$CHAIN_DATA + CHAIN_ID="osmosis-dev" + PORT=36657 + BLOCK_SECONDS=5 + FEE=uosmo + BINARY=osmosisd + GATEWAY_CONTRACT_ADDRESS=$(cat "$CHAIN_DATA/gateway_contract_address") + + TRANSFER_PICA_TO_OSMOSIS=$(cat << EOF + { + "execute_program": { + "execute_program": { + "salt": "737061776e5f776974685f6173736574", + "program": { + "tag": "737061776e5f776974685f6173736574", + "instructions": [ + { + "spawn": { + "network": 2, + "salt": "737061776e5f776974685f6173736574", + "assets": [ + [ + "158456325028528675187087901673", + { + "amount": { + "intercept": "1000000000", + "slope": "0" + }, + "is_unit": false + } + ] + ], + "program": { + "tag": "737061776e5f776974685f6173736574", + "instructions": [] + } + } + } + ] + }, + "assets": [ + [ + "237684487542793012780631852009", + "1000000000" + ] + ] + }, + "tip": "osmo12smx2wdlyttvyzvzg54y2vnqwq2qjatescq89n" + } + } + EOF + ) + + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$TRANSFER_PICA_TO_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 1000000000"$FEE" --amount 1000000000"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.xcvm.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + sleep "$BLOCK_SECONDS" + ''; + }; + }; }; } diff --git a/flake/process-compose.nix b/flake/process-compose.nix index 4e28286d4c2..25d0f514ae1 100644 --- a/flake/process-compose.nix +++ b/flake/process-compose.nix @@ -439,6 +439,22 @@ availability = { restart = "on_failure"; }; }; + centauri-xcvm-init = { + command = self'.packages.centaurid-xcvm-init; + depends_on."centauri".condition = "process_healthy"; + log_location = "/tmp/composable-devnet/centauri-xcvm-init.log"; + availability = { restart = "on_failure"; }; + }; + + centauri-xcvm-config = { + command = self'.packages.centaurid-xcvm-config; + depends_on."centauri-xcvm-init".condition = + "process_completed_successfully"; + log_location = + "/tmp/composable-devnet/centauri-xcvm-config.log"; + availability = { restart = "on_failure"; }; + }; + osmosis = { command = self'.packages.osmosisd-gen; readiness_probe.http_get = { diff --git a/inputs/notional-labs/composable-centauri/flake-module.nix b/inputs/notional-labs/composable-centauri/flake-module.nix index 78724415062..4304c02ece3 100644 --- a/inputs/notional-labs/composable-centauri/flake-module.nix +++ b/inputs/notional-labs/composable-centauri/flake-module.nix @@ -58,41 +58,88 @@ VALIDATOR_KEY=${validator-key} PORT=26657 BLOCK_SECONDS=5 + FEE=ppica + BINARY=centaurid + + "$BINARY" tx gov submit-proposal ${ics10-grandpa-cw-proposal}/ics10_grandpa_cw.wasm.json --from "$VALIDATOR_KEY" --keyring-backend test --gas 9021526220000 --fees 92000000166$FEE --keyring-dir "$KEYRING_TEST" --chain-id "$CHAIN_ID" --yes --home "$CHAIN_DATA" --output json + sleep $BLOCK_SECONDS + "$BINARY" query auth module-account gov --chain-id "$CHAIN_ID" --node tcp://localhost:$PORT --home "$CHAIN_DATA" | jq '.account.base_account.address' --raw-output + PROPOSAL_ID=1 + "$BINARY" tx gov vote $PROPOSAL_ID yes --from "$VALIDATOR_KEY" --keyring-backend test --gas 9021526220000 --fees 92000000166$FEE --keyring-dir "$KEYRING_TEST" --chain-id "$CHAIN_ID" --yes --home "$CHAIN_DATA" --output json + sleep 20 + "$BINARY" query gov proposal $PROPOSAL_ID --chain-id "$CHAIN_ID" --node tcp://localhost:$PORT --home "$CHAIN_DATA" | jq '.status' + sleep $BLOCK_SECONDS + "$BINARY" query 08-wasm all-wasm-code --chain-id "$CHAIN_ID" --home "$CHAIN_DATA" --output json --node tcp://localhost:$PORT | jq '.code_ids[0]' --raw-output | tee "$CHAIN_DATA/code_id" + ''; + }; + + centaurid-xcvm-init = pkgs.writeShellApplication { + name = "centaurid-xcvm-init"; + runtimeInputs = devnetTools.withBaseContainerTools + ++ [ centaurid pkgs.jq self'.packages.xc-cw-contracts ]; + + text = '' + CHAIN_DATA="${devnet-root-directory}/.centaurid" + + CHAIN_ID="centauri-dev" + KEYRING_TEST="$CHAIN_DATA/keyring-test" + KEY=${cosmosTools.xcvm.centauri} + PORT=26657 + BLOCK_SECONDS=5 FEE=ppica NETWORK_ID=2 BINARY=centaurid function init_xcvm() { local INSTANTIATE=$1 - echo $VALIDATOR_KEY - echo $NETWORK_ID - "$BINARY" tx wasm store "${self'.packages.xc-cw-contracts}/lib/cw_xc_gateway.wasm" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" + "$BINARY" tx wasm store "${self'.packages.xc-cw-contracts}/lib/cw_xc_gateway.wasm" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" GATEWAY_CODE_ID=1 sleep $BLOCK_SECONDS - "$BINARY" tx wasm store "${self'.packages.xc-cw-contracts}/lib/cw_xc_interpreter.wasm" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" - INTERPRETER_CODE_ID=2 + "$BINARY" tx wasm store "${self'.packages.xc-cw-contracts}/lib/cw_xc_interpreter.wasm" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" sleep $BLOCK_SECONDS - "$BINARY" tx wasm store "${self'.packages.cw20_base}" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" + "$BINARY" tx wasm store "${self'.packages.cw20_base}" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" sleep $BLOCK_SECONDS - "$BINARY" tx wasm instantiate2 $GATEWAY_CODE_ID "$INSTANTIATE" "1234" --label "xc-gateway" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --admin "$VALIDATOR_KEY" + "$BINARY" tx wasm instantiate2 $GATEWAY_CODE_ID "$INSTANTIATE" "1234" --label "xc-gateway" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166$FEE --log_level info --keyring-backend test --home "$CHAIN_DATA" --from "$KEY" --keyring-dir "$KEYRING_TEST" --admin "$KEY" sleep $BLOCK_SECONDS GATEWAY_CONTRACT_ADDRESS=$("$BINARY" query wasm list-contract-by-code "$GATEWAY_CODE_ID" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --home "$CHAIN_DATA" | dasel --read json '.contracts.[0]' --write yaml) + echo "$GATEWAY_CONTRACT_ADDRESS" > "$CHAIN_DATA/gateway_contract_address" + echo "2" > "$CHAIN_DATA/interpreter_code_id" } INSTANTIATE=$(cat << EOF { - "admin" : "$VALIDATOR_KEY", + "admin" : "$KEY", "here_id" : $NETWORK_ID } EOF ) - init_xcvm "$INSTANTIATE" + init_xcvm "$INSTANTIATE" + ''; + }; + + centaurid-xcvm-config = pkgs.writeShellApplication { + name = "centaurid-xcvm-config"; + runtimeInputs = devnetTools.withBaseContainerTools + ++ [ centaurid pkgs.jq self'.packages.xc-cw-contracts ]; + + text = '' + CHAIN_DATA="${devnet-root-directory}/.centaurid" + + CHAIN_ID="centauri-dev" + KEYRING_TEST="$CHAIN_DATA/keyring-test" + KEY=${cosmosTools.xcvm.centauri} + PORT=26657 + BLOCK_SECONDS=5 + FEE=ppica + BINARY=centaurid + GATEWAY_CONTRACT_ADDRESS=$(cat $CHAIN_DATA/gateway_contract_address) + INTERPRETER_CODE_ID=$(cat $CHAIN_DATA/interpreter_code_id) FORCE_NETWORK_OSMOSIS=$(cat << EOF { @@ -106,7 +153,7 @@ "cosm_wasm": { "contract": "$GATEWAY_CONTRACT_ADDRESS", "interpreter_code_id": $INTERPRETER_CODE_ID, - "admin": "${validator-key}" + "admin": "$KEY" } }, "ibc": { @@ -127,7 +174,7 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_NETWORK_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_NETWORK_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.xcvm.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS FORCE_NETWORK_CENTAURI=$(cat << EOF @@ -142,7 +189,7 @@ "cosm_wasm": { "contract": "$GATEWAY_CONTRACT_ADDRESS", "interpreter_code_id": $INTERPRETER_CODE_ID, - "admin": "${validator-key}" + "admin": "$KEY" } }, "ibc": { @@ -163,7 +210,7 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_NETWORK_CENTAURI" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_NETWORK_CENTAURI" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.xcvm.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS @@ -188,9 +235,7 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_CENTAURI_TO_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace - - # + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_CENTAURI_TO_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.xcvm.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS FORCE_PICA=$(cat << EOF @@ -217,7 +262,7 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_PICA" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_PICA" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.xcvm.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS @@ -245,7 +290,7 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_PICA_ON_CENTAURI" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_PICA_ON_CENTAURI" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.xcvm.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS @@ -273,9 +318,7 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_PICA_ON_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace - - + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_PICA_ON_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.xcvm.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS @@ -295,22 +338,9 @@ } EOF ) - "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_UATOM" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.validators.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace - echo "$GATEWAY_CONTRACT_ADDRESS" > "$CHAIN_DATA/gateway_contract_address" + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_UATOM" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.xcvm.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace sleep $BLOCK_SECONDS "$BINARY" query wasm contract-state all "$GATEWAY_CONTRACT_ADDRESS" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --home "$CHAIN_DATA" - - # light client - centaurid tx gov submit-proposal ${ics10-grandpa-cw-proposal}/ics10_grandpa_cw.wasm.json --from "$VALIDATOR_KEY" --keyring-backend test --gas 9021526220000 --fees 92000000166ppica --keyring-dir "$KEYRING_TEST" --chain-id "$CHAIN_ID" --yes --home "$CHAIN_DATA" --output json - sleep $BLOCK_SECONDS - centaurid query auth module-account gov --chain-id "$CHAIN_ID" --node tcp://localhost:26657 --home "$CHAIN_DATA" | jq '.account.base_account.address' --raw-output - PROPOSAL_ID=1 - centaurid tx gov vote $PROPOSAL_ID yes --from "$VALIDATOR_KEY" --keyring-backend test --gas 9021526220000 --fees 92000000166ppica --keyring-dir "$KEYRING_TEST" --chain-id "$CHAIN_ID" --yes --home "$CHAIN_DATA" --output json - sleep 20 - centaurid query gov proposal $PROPOSAL_ID --chain-id "$CHAIN_ID" --node tcp://localhost:26657 --home "$CHAIN_DATA" | - jq '.status' - sleep $BLOCK_SECONDS - centaurid query 08-wasm all-wasm-code --chain-id "$CHAIN_ID" --home "$CHAIN_DATA" --output json --node tcp://localhost:26657 | jq '.code_ids[0]' --raw-output | tee "$CHAIN_DATA/code_id" ''; }; @@ -319,61 +349,62 @@ runtimeInputs = devnetTools.withBaseContainerTools ++ [ centaurid pkgs.jq ]; text = '' - TRANSFER_PICA_TO_OSMOSIS=$(cat < "$CHAIN_DATA/config/genesis-update.json" < "$CHAIN_DATA/config/genesis.json" + mv --force "$CHAIN_DATA/config/genesis-update.json" "$CHAIN_DATA/config/genesis.json" + } - mkdir --parents "$CHAIN_DATA" - mkdir --parents "$CHAIN_DATA/config/gentx" - mkdir --parents "$KEYRING_TEST" - echo "${validator-mnemonic}" | centaurid init "$CHAIN_ID" --chain-id "$CHAIN_ID" --default-denom ${native_denom} --home "$CHAIN_DATA" --recover - - function jq-genesis() { - jq -r "$1" > "$CHAIN_DATA/config/genesis-update.json" < "$CHAIN_DATA/config/genesis.json" - mv --force "$CHAIN_DATA/config/genesis-update.json" "$CHAIN_DATA/config/genesis.json" - } - - jq-genesis '.consensus_params.block.max_gas |= "-1"' - jq-genesis '.app_state.gov.params.voting_period |= "${gov.voting_period}"' - jq-genesis '.app_state.gov.params.max_deposit_period |= "${gov.max_deposit_period}"' - - jq-genesis '.app_state.transmiddleware.token_infos[0].ibc_denom |= "ibc/632DBFDB06584976F1351A66E873BF0F7A19FAA083425FEC9890C90993E5F0A4"' - jq-genesis '.app_state.transmiddleware.token_infos[0].channel_id |= "channel-0"' - jq-genesis '.app_state.transmiddleware.token_infos[0].native_denom |= "ppica"' - jq-genesis '.app_state.transmiddleware.token_infos[0].asset_id |= "1"' - - sed -i 's/keyring-backend = "os"/keyring-backend = "test"/' "$CHAIN_DATA/config/client.toml" - sed -i 's/keyring-backend = "os"/keyring-backend = "test"/' "$CHAIN_DATA/config/client.toml" - sed -i 's/keyring-backend = "os"/keyring-backend = "test"/' "$CHAIN_DATA/config/client.toml" - sed -i 's/output = "text"/output = "json"/' "$CHAIN_DATA/config/client.toml" - sed -i "s/cors_allowed_origins = \[\]/cors_allowed_origins = \[\"\*\"\]/" "$CHAIN_DATA/config/config.toml" - sed -i "s/swagger = false/swagger = true/" "$CHAIN_DATA/config/app.toml" - sed -i "s/rpc-max-body-bytes = 1000000/rpc-max-body-bytes = 10000000/" "$CHAIN_DATA/config/app.toml" - sed -i "s/max_body_bytes = 1000000/max_body_bytes = 10000000/" "$CHAIN_DATA/config/config.toml" - sed -i "s/max_header_bytes = 1048576/max_header_bytes = 10485760/" "$CHAIN_DATA/config/config.toml" - sed -i "s/max_tx_bytes = 1048576/max_tx_bytes = 10485760/" "$CHAIN_DATA/config/config.toml" - - echo "document prefer nurse marriage flavor cheese west when knee drink sorry minimum thunder tilt cherry behave cute stove elder couch badge gown coral expire" | centaurid keys add alice --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true - echo "bleak slush nose opinion document sample embark couple cabbage soccer cage slow father witness canyon ring distance hub denial topic great beyond actress problem" | centaurid keys add bob --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true - echo "coffee hospital claim ability wrap load display submit lecture solid secret law base barrel miss tattoo desert want wall bar ketchup sauce real unknown" | centaurid keys add charlie --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true - echo "${validator-mnemonic}" | centaurid keys add ${cosmosTools.validators.moniker} --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true - echo "notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius" | centaurid keys add test1 --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true - echo "quality vacuum heart guard buzz spike sight swarm shove special gym robust assume sudden deposit grid alcohol choice devote leader tilt noodle tide penalty" | centaurid keys add test2 --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true - echo "symbol force gallery make bulk round subway violin worry mixture penalty kingdom boring survey tool fringe patrol sausage hard admit remember broken alien absorb" | centaurid keys add test3 --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true - echo "black frequent sponsor nice claim rally hunt suit parent size stumble expire forest avocado mistake agree trend witness lounge shiver image smoke stool chicken" | centaurid keys add relayer --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true - function add-genesis-account () { - centaurid --keyring-backend test add-genesis-account "$1" "10000000000000000000000ppica" --keyring-backend test --home "$CHAIN_DATA" - } + jq-genesis '.consensus_params.block.max_gas |= "-1"' + jq-genesis '.app_state.gov.params.voting_period |= "${gov.voting_period}"' + jq-genesis '.app_state.gov.params.max_deposit_period |= "${gov.max_deposit_period}"' + + jq-genesis '.app_state.transmiddleware.token_infos[0].ibc_denom |= "ibc/632DBFDB06584976F1351A66E873BF0F7A19FAA083425FEC9890C90993E5F0A4"' + jq-genesis '.app_state.transmiddleware.token_infos[0].channel_id |= "channel-0"' + jq-genesis '.app_state.transmiddleware.token_infos[0].native_denom |= "ppica"' + jq-genesis '.app_state.transmiddleware.token_infos[0].asset_id |= "1"' + + sed -i 's/keyring-backend = "os"/keyring-backend = "test"/' "$CHAIN_DATA/config/client.toml" + sed -i 's/keyring-backend = "os"/keyring-backend = "test"/' "$CHAIN_DATA/config/client.toml" + sed -i 's/keyring-backend = "os"/keyring-backend = "test"/' "$CHAIN_DATA/config/client.toml" + sed -i 's/output = "text"/output = "json"/' "$CHAIN_DATA/config/client.toml" + sed -i "s/cors_allowed_origins = \[\]/cors_allowed_origins = \[\"\*\"\]/" "$CHAIN_DATA/config/config.toml" + sed -i "s/swagger = false/swagger = true/" "$CHAIN_DATA/config/app.toml" + sed -i "s/rpc-max-body-bytes = 1000000/rpc-max-body-bytes = 10000000/" "$CHAIN_DATA/config/app.toml" + sed -i "s/max_body_bytes = 1000000/max_body_bytes = 10000000/" "$CHAIN_DATA/config/config.toml" + sed -i "s/max_header_bytes = 1048576/max_header_bytes = 10485760/" "$CHAIN_DATA/config/config.toml" + sed -i "s/max_tx_bytes = 1048576/max_tx_bytes = 10485760/" "$CHAIN_DATA/config/config.toml" + + echo "document prefer nurse marriage flavor cheese west when knee drink sorry minimum thunder tilt cherry behave cute stove elder couch badge gown coral expire" | centaurid keys add alice --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true + echo "bleak slush nose opinion document sample embark couple cabbage soccer cage slow father witness canyon ring distance hub denial topic great beyond actress problem" | centaurid keys add bob --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true + echo "coffee hospital claim ability wrap load display submit lecture solid secret law base barrel miss tattoo desert want wall bar ketchup sauce real unknown" | centaurid keys add charlie --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true + echo "${validator-mnemonic}" | centaurid keys add ${cosmosTools.validators.moniker} --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true + echo "${cosmosTools.xcvm.mnemonic}" | centaurid keys add ${cosmosTools.xcvm.moniker} --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true + echo "notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius" | centaurid keys add test1 --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true + echo "quality vacuum heart guard buzz spike sight swarm shove special gym robust assume sudden deposit grid alcohol choice devote leader tilt noodle tide penalty" | centaurid keys add test2 --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true + echo "symbol force gallery make bulk round subway violin worry mixture penalty kingdom boring survey tool fringe patrol sausage hard admit remember broken alien absorb" | centaurid keys add test3 --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true + echo "black frequent sponsor nice claim rally hunt suit parent size stumble expire forest avocado mistake agree trend witness lounge shiver image smoke stool chicken" | centaurid keys add relayer --recover --keyring-backend test --keyring-dir "$KEYRING_TEST" || true + function add-genesis-account () { + centaurid --keyring-backend test add-genesis-account "$1" "10000000000000000000000ppica" --keyring-backend test --home "$CHAIN_DATA" + } - add-genesis-account centauri1qvdeu4x34rapp3wc8fym5g4wu343mswxxgc6wf - add-genesis-account centauri1zr4ng42laatyh9zx238n20r74spcrlct6jsqaw - add-genesis-account centauri1makf5hslxqxzl29uyeyyddf89ff7edxyr7ewm5 - add-genesis-account ${validator-key} - add-genesis-account centauri1cyyzpxplxdzkeea7kwsydadg87357qnamvg3y3 - add-genesis-account centauri18s5lynnmx37hq4wlrw9gdn68sg2uxp5ry85k7d - add-genesis-account centauri1qwexv7c6sm95lwhzn9027vyu2ccneaqapystyu - centaurid --keyring-backend test --keyring-dir "$KEYRING_TEST" --home "$CHAIN_DATA" gentx ${cosmosTools.validators.moniker} "250000000000000ppica" --chain-id="$CHAIN_ID" --amount="250000000000000ppica" - centaurid collect-gentxs --home "$CHAIN_DATA" --gentx-dir "$CHAIN_DATA/config/gentx" + add-genesis-account centauri1qvdeu4x34rapp3wc8fym5g4wu343mswxxgc6wf + add-genesis-account centauri1zr4ng42laatyh9zx238n20r74spcrlct6jsqaw + add-genesis-account centauri1makf5hslxqxzl29uyeyyddf89ff7edxyr7ewm5 + add-genesis-account ${validator-key} + add-genesis-account ${cosmosTools.xcvm.centauri} + add-genesis-account centauri1cyyzpxplxdzkeea7kwsydadg87357qnamvg3y3 + add-genesis-account centauri18s5lynnmx37hq4wlrw9gdn68sg2uxp5ry85k7d + add-genesis-account centauri1qwexv7c6sm95lwhzn9027vyu2ccneaqapystyu + centaurid --keyring-backend test --keyring-dir "$KEYRING_TEST" --home "$CHAIN_DATA" gentx ${cosmosTools.validators.moniker} "250000000000000ppica" --chain-id="$CHAIN_ID" --amount="250000000000000ppica" + centaurid collect-gentxs --home "$CHAIN_DATA" --gentx-dir "$CHAIN_DATA/config/gentx" + else + echo "WARNING: REUSING EXISTING DATA FOLDER" + fi centaurid start --rpc.unsafe --rpc.laddr tcp://0.0.0.0:26657 --pruning=nothing --minimum-gas-prices=0ppica --log_level debug --home "$CHAIN_DATA" --db_dir "$CHAIN_DATA/data" --trace --with-tendermint true --transport socket --trace-store $CHAIN_DATA/kvstore.log --grpc.address localhost:9090 --grpc.enable true --grpc-web.enable false --api.enable true --cpu-profile $CHAIN_DATA/cpu-profile.log --p2p.pex false --p2p.upnp false ''; }; in { packages = rec { inherit centaurid centaurid-gen centaurid-init centaurid-gen-fresh - ics10-grandpa-cw-proposal xc-transfer-pica-from-centauri-to-osmosis; + ics10-grandpa-cw-proposal xc-transfer-pica-from-centauri-to-osmosis + centaurid-xcvm-init centaurid-xcvm-config; + + centauri-exec = pkgs.writeShellApplication { + name = "centaurid-xcvm-config"; + runtimeInputs = devnetTools.withBaseContainerTools + ++ [ centaurid pkgs.jq self'.packages.xc-cw-contracts ]; + + text = '' + CHAIN_DATA="${devnet-root-directory}/.centaurid" + CHAIN_ID="centauri-dev" + KEYRING_TEST="$CHAIN_DATA/keyring-test" + PORT=26657 + FEE=ppica + BINARY=centaurid + GATEWAY_CONTRACT_ADDRESS=$(cat $CHAIN_DATA/gateway_contract_address) + MSG=$1 + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$MSG" --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level info --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.xcvm.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + ''; + }; }; }; }