diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/assets.rs b/code/xcvm/cosmwasm/contracts/gateway/src/assets.rs index 310d4cea1c2..582bb7ebcfe 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/assets.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/assets.rs @@ -15,7 +15,7 @@ use xc_core::{AssetId, NetworkId}; pub(crate) fn force_asset(_: auth::Admin, deps: DepsMut, msg: AssetItem) -> Result { let config = crate::state::load(deps.storage)?; ASSETS.save(deps.storage, msg.asset_id, &msg)?; - if msg.from_network_id == config.here_id { + if msg.network_id == config.network_id { LOCAL_ASSETS.save(deps.storage, msg.local.clone(), &msg)?; } Ok(Response::new().add_event( @@ -50,7 +50,7 @@ pub(crate) fn force_remove_asset( let config = crate::state::load(deps.storage)?; let asset = ASSETS.load(deps.storage, asset_id)?; ASSETS.remove(deps.storage, asset_id); - if asset.from_network_id == config.here_id { + if asset.network_id == config.network_id { LOCAL_ASSETS.remove(deps.storage, asset.local); } Ok(Response::new() diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/auth.rs b/code/xcvm/cosmwasm/contracts/gateway/src/auth.rs index 17d3f488dc1..14da0956f43 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/auth.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/auth.rs @@ -1,9 +1,9 @@ //! Module with authorisation checks. use crate::{ error::{ContractError, Result}, - msg, state, + msg, network, state, }; -use cosmwasm_std::{Deps, Env, MessageInfo, Storage}; +use cosmwasm_std::{Deps, Env, MessageInfo}; use xc_core::{gateway::OtherNetworkItem, NetworkId}; /// Authorisation token indicating call is authorised according to policy @@ -39,18 +39,32 @@ impl Auth { impl Auth { pub(crate) fn authorise( - storage: &dyn Storage, + deps: Deps, env: &Env, info: &MessageInfo, network_id: NetworkId, ) -> Result { - let this = state::load(storage)?; - let this_to_other: OtherNetworkItem = - state::NETWORK_TO_NETWORK.load(storage, (this.here_id, network_id))?; + let this = network::load_this(deps.storage)?; + let this_to_other: OtherNetworkItem = state::NETWORK_TO_NETWORK + .load(deps.storage, (this.network_id, network_id)) + .map_err(|_| { + ContractError::NoConnectionInformationFromThisToOtherNetwork( + this.network_id, + network_id, + ) + })?; + let prefix = this + .accounts + .map(|x| match x { + msg::Prefix::SS58(prefix) => prefix.to_string(), + msg::Prefix::Bech(prefix) => prefix, + }) + .unwrap_or_default(); let sender = state::NETWORK - .load(storage, network_id)? + .load(deps.storage, network_id)? .gateway - .ok_or(ContractError::NotAuthorized)?; + .ok_or(ContractError::GatewayForNetworkNotFound(network_id))?; + let sender = match sender { msg::GatewayId::CosmWasm { contract, .. } => contract.to_string(), }; @@ -58,9 +72,13 @@ impl Auth { let channel = this_to_other.ics_20.ok_or(ContractError::ICS20NotFound)?.source; let hash_of_channel_and_sender = xc_core::transport::ibc::ics20::hook::derive_intermediate_sender( - &channel, &sender, "", + &channel, &sender, &prefix, )?; - Self::new(hash_of_channel_and_sender == info.sender && info.sender == env.contract.address) + deps.api.debug(&format!( + "xcvm::gateway:auth:: {0} {1}", + &hash_of_channel_and_sender, &info.sender + )); + Self::new(hash_of_channel_and_sender == info.sender || info.sender == env.contract.address) } } diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs b/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs index 2b533bd1d13..8362809c300 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs @@ -18,6 +18,13 @@ use xc_core::{gateway::ConfigSubMsg, CallOrigin, Displayed, Funds, InterpreterOr #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: msg::ExecuteMsg) -> Result { use msg::ExecuteMsg; + let sender = &info.sender; + let canonical_sender = deps.api.addr_canonicalize(sender.as_str())?; + deps.api.debug(&format!( + "xcvm::gateway::execute sender on chain {}, sender cross chain {}", + sender, + &serde_json_wasm::to_string(&canonical_sender)? + )); match msg { ExecuteMsg::Config(msg) => { let auth = auth::Admin::authorise(deps.as_ref(), &info)?; @@ -37,15 +44,17 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: msg::ExecuteMsg) auth::Interpreter::authorise(deps.as_ref(), &info, msg.interpreter_origin.clone())?; if !msg.msg.assets.0.is_empty() { - super::ibc::ics20::handle_bridge_forward(auth, deps, info, msg) + super::ibc::ics20::handle_bridge_forward(auth, deps, info, msg, env.block) } else { - super::ibc::xcvm::handle_bridge_forward_no_assets(auth, deps, info, msg) + super::ibc::xcvm::handle_bridge_forward_no_assets(auth, deps, info, msg, env.block) } }, 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) + deps.api.debug(&format!("xcvm::gateway::execute::message_hook {:?}", msg)); + + let auth = auth::WasmHook::authorise(deps.as_ref(), &env, &info, msg.from_network_id)?; + + super::ibc::ics20::ics20_message_hook(auth, deps.as_ref(), msg, env, info) }, msg::ExecuteMsg::Shortcut(msg) => handle_shortcut(deps, env, info, msg), } 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 c7ce7ffe7da..076b9d9ace5 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/ics20.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/ics20.rs @@ -2,15 +2,16 @@ //! Allows to map asset identifiers, contracts, networks, channels, denominations from, to and on //! each chain via contract storage, precompiles, host extensions. //! handles PFM and IBC wasm hooks -use crate::prelude::*; +use crate::{network, prelude::*}; use cosmwasm_std::{ - ensure_eq, wasm_execute, Binary, Coin, DepsMut, Env, MessageInfo, Response, Storage, SubMsg, + ensure_eq, wasm_execute, Binary, BlockInfo, Coin, Deps, DepsMut, Env, MessageInfo, Response, + Storage, SubMsg, }; use xc_core::{ - gateway::{AssetItem, ExecuteMsg, ExecuteProgramMsg, GatewayId, OtherNetworkItem}, - shared::{XcPacket, XcProgram}, + gateway::{AssetItem, ExecuteMsg, ExecuteProgramMsg, GatewayId}, + shared::{XcFunds, XcPacket, XcProgram}, transport::ibc::{to_cw_message, IbcIcs20Route, XcMessageData}, - AssetId, CallOrigin, + AssetId, CallOrigin, Displayed, }; use crate::{ @@ -27,6 +28,7 @@ pub(crate) fn handle_bridge_forward( deps: DepsMut, info: MessageInfo, msg: xc_core::gateway::BridgeForwardMsg, + block: BlockInfo, ) -> Result { deps.api.debug(&format!( "xcvm::gateway:: forwarding over IBC ICS20 MEMO {}", @@ -37,17 +39,26 @@ pub(crate) fn handle_bridge_forward( // 1. recurse on program until can with memo // 2. as soon as see no Spawn/Transfer, stop memo and do Wasm call with remaining Packet + let (local_asset, amount) = msg.msg.assets.0.get(0).expect("proved above"); + + let route: IbcIcs20Route = get_route(deps.storage, msg.to, *local_asset)?; + + let asset = msg + .msg + .assets + .0 + .get(0) + .map(|(_, amount)| (route.on_remote_asset, *amount)) + .expect("not empty"); + let packet = XcPacket { interpreter: String::from(info.sender).into_bytes(), user_origin: msg.interpreter_origin.user_origin, salt: msg.msg.salt, program: msg.msg.program, - assets: msg.msg.assets, + assets: vec![asset].into(), }; - 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(&format!( "xcvm::gateway::ibc::ics20 route {}", &serde_json_wasm::to_string(&route)? @@ -71,7 +82,7 @@ pub(crate) fn handle_bridge_forward( let coin = Coin::new(amount.0, route.local_native_denom.clone()); - let msg = to_cw_message(coin, route, packet)?; + let msg = to_cw_message(deps.api, coin, route, packet, block)?; deps.api.debug(&format!( "xcvm::gateway::ibc::ics20:: payload {}", &serde_json_wasm::to_string(&msg)? @@ -89,19 +100,14 @@ pub fn get_route( this_asset_id: AssetId, ) -> Result { let this = load_this(storage)?; - let other: NetworkItem = state::NETWORK - .load(storage, to) - .map_err(|_| ContractError::UnknownTargetNetwork)?; - let this_to_other: OtherNetworkItem = state::NETWORK_TO_NETWORK - .load(storage, (this.network_id, to)) - .map_err(|_| ContractError::NoConnectionInformationFromThisToOtherNetwork)?; + let other = network::load_other(storage, to)?; let asset: AssetItem = state::assets::ASSETS .load(storage, this_asset_id) .map_err(|_| ContractError::AssetNotFoundById(this_asset_id))?; let to_asset: AssetId = state::assets::NETWORK_ASSET .load(storage, (this_asset_id, to)) .map_err(|_| ContractError::AssetCannotBeTransferredToNetwork(this_asset_id, to))?; - let gateway_to_send_to = other.gateway.ok_or(ContractError::UnsupportedNetwork)?; + let gateway_to_send_to = other.network.gateway.ok_or(ContractError::UnsupportedNetwork)?; let gateway_to_send_to = match gateway_to_send_to { GatewayId::CosmWasm { contract, .. } => contract, }; @@ -110,7 +116,7 @@ pub fn get_route( GatewayId::CosmWasm { contract, .. } => contract, }; - let channel = this_to_other.ics_20.ok_or(ContractError::ICS20NotFound)?.source; + let channel = other.connection.ics_20.ok_or(ContractError::ICS20NotFound)?.source; Ok(IbcIcs20Route { from_network: this.network_id, @@ -118,7 +124,7 @@ pub fn get_route( channel_to_send_over: channel, gateway_to_send_to, sender_gateway, - counterparty_timeout: this_to_other.counterparty_timeout, + counterparty_timeout: other.connection.counterparty_timeout, ibc_ics_20_sender: this .ibc .ok_or(ContractError::ICS20NotFound)? @@ -133,15 +139,32 @@ pub fn get_route( pub(crate) fn ics20_message_hook( _: auth::WasmHook, + deps: Deps, msg: XcMessageData, env: Env, info: MessageInfo, ) -> Result { let packet: XcPacket = msg.packet; ensure_anonymous(&packet.program)?; + deps.api.debug(&format!( + "xcvm::gateway::ibc::ics20:: received assets {:?}, packet assets {:?}", + &info.funds, &packet.assets + )); + + let assets: Result = info + .funds + .into_iter() + .map(|coin| { + let asset = crate::assets::get_local_asset_by_reference( + deps, + AssetReference::Native { denom: coin.denom }, + )?; + Ok((asset.asset_id, Displayed::::from(coin.amount.u128()))) + }) + .collect(); let call_origin = CallOrigin::Remote { user_origin: packet.user_origin }; let execute_program = - ExecuteProgramMsg { salt: packet.salt, program: packet.program, assets: packet.assets }; + ExecuteProgramMsg { salt: packet.salt, program: packet.program, assets: assets?.into() }; let msg = ExecuteMsg::ExecuteProgramPrivileged { call_origin, execute_program, tip: info.sender }; let msg = wasm_execute(env.contract.address, &msg, Default::default())?; @@ -153,7 +176,7 @@ fn ensure_anonymous(program: &XcProgram) -> Result<()> { match ix { xc_core::Instruction::Transfer { .. } => {}, xc_core::Instruction::Spawn { program, .. } => ensure_anonymous(program)?, - _ => Err(ContractError::NotAuthorized)?, + _ => Err(ContractError::AnonymousCallsCanDoOnlyLimitedSetOfActions)?, } } Ok(()) diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/xcvm.rs b/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/xcvm.rs index b92c0c3c5fd..ee37d6a9c55 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/xcvm.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/contract/ibc/xcvm.rs @@ -13,10 +13,10 @@ use crate::{ }; use cosmwasm_std::{ - ensure_eq, wasm_execute, Binary, DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, - IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcChannelOpenResponse, IbcMsg, - IbcOrder, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, - MessageInfo, Response, SubMsg, + ensure_eq, wasm_execute, Binary, BlockInfo, DepsMut, Env, Ibc3ChannelOpenResponse, + IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, + IbcChannelOpenResponse, IbcMsg, IbcOrder, IbcPacketAckMsg, IbcPacketReceiveMsg, + IbcPacketTimeoutMsg, IbcReceiveResponse, MessageInfo, Response, SubMsg, }; use ibc_rs_scale::core::ics24_host::identifier::{ChannelId, ConnectionId}; use xc_core::{ @@ -143,6 +143,7 @@ pub(crate) fn handle_bridge_forward_no_assets( deps: DepsMut, info: MessageInfo, msg: msg::BridgeForwardMsg, + block: BlockInfo, ) -> Result { ensure_eq!(msg.msg.assets.0.len(), 0, ContractError::CannotTransferAssets); let other = load_other(deps.storage, msg.to)?; @@ -178,6 +179,6 @@ pub(crate) fn handle_bridge_forward_no_assets( channel_id: channel_id.to_string(), data: Binary::from(packet.encode()), // TODO: should be a parameter or configuration - timeout: other.connection.counterparty_timeout, + timeout: other.connection.counterparty_timeout.absolute(block), })) } diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/error.rs b/code/xcvm/cosmwasm/contracts/gateway/src/error.rs index 07af55a08b3..e5107c173f3 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/error.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/error.rs @@ -65,13 +65,17 @@ pub enum ContractError { NetworkConfig, #[error("Unknown target network")] UnknownTargetNetwork, - #[error("No connection information from this to other network")] - NoConnectionInformationFromThisToOtherNetwork, + #[error("No connection information from this {0} to other network {1}")] + NoConnectionInformationFromThisToOtherNetwork(NetworkId, NetworkId), #[error("Asset {0} not found by id")] AssetNotFoundById(AssetId), #[error("Asset {0} cannot be transferred to network {1}")] AssetCannotBeTransferredToNetwork(AssetId, NetworkId), + #[error("Gateway for network {0} not found")] + GatewayForNetworkNotFound(NetworkId), + #[error("Anonymous calls can do only limitet set of actions")] + AnonymousCallsCanDoOnlyLimitedSetOfActions, } impl From for ContractError { diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/interpreter.rs b/code/xcvm/cosmwasm/contracts/gateway/src/interpreter.rs index f94ad911db7..c79bb6aa6eb 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/interpreter.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/interpreter.rs @@ -20,13 +20,13 @@ pub(crate) fn force_instantiate( gateway: Addr, deps: DepsMut, user_origin: Addr, - salt: Option, + salt: 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 salt = salt.map_or(<_>::default(), |x| x.into_bytes()); + let salt = salt.into_bytes(); let call_origin = CallOrigin::Local { user: user_origin }; let interpreter_origin = diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/network.rs b/code/xcvm/cosmwasm/contracts/gateway/src/network.rs index 4abe3b47473..a0f9292264f 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/network.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/network.rs @@ -8,7 +8,7 @@ use crate::error::{ContractError, Result}; pub fn load_this(storage: &dyn Storage) -> Result { state::load(storage) - .and_then(|this| NETWORK.load(storage, this.here_id)) + .and_then(|this| NETWORK.load(storage, this.network_id)) .map_err(|_| ContractError::NetworkConfig) } @@ -19,10 +19,10 @@ pub struct OtherNetwork { pub connection: OtherNetworkItem, } -pub fn load_other(storage: &dyn Storage, _other: NetworkId) -> Result { +pub fn load_other(storage: &dyn Storage, other: NetworkId) -> Result { let this = state::load(storage)?; - let other = NETWORK.load(storage, this.here_id)?; - let connection = NETWORK_TO_NETWORK.load(storage, (this.here_id, other.network_id))?; + let other = NETWORK.load(storage, other)?; + let connection = NETWORK_TO_NETWORK.load(storage, (this.network_id, other.network_id))?; Ok(OtherNetwork { network: other, connection }) } diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/prelude.rs b/code/xcvm/cosmwasm/contracts/gateway/src/prelude.rs index e5b28664513..f8fe02f87d4 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/prelude.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/prelude.rs @@ -1,3 +1,4 @@ +pub use alloc::format; pub use cosmwasm_std::{to_binary, Addr}; pub use cw_storage_plus::Map; pub use ibc_rs_scale::core::ics24_host::identifier::{ChannelId, ConnectionId}; diff --git a/code/xcvm/cosmwasm/contracts/interpreter/src/contract.rs b/code/xcvm/cosmwasm/contracts/interpreter/src/contract.rs index 09ef59dbe9f..cad231e510a 100644 --- a/code/xcvm/cosmwasm/contracts/interpreter/src/contract.rs +++ b/code/xcvm/cosmwasm/contracts/interpreter/src/contract.rs @@ -342,7 +342,7 @@ pub fn interpret_transfer( assets: Funds, ) -> Result { let Config { gateway_address: gateway, .. } = CONFIG.load(deps.storage)?; - + deps.api.debug(&format!("xcvm::interpreter:: transfer to {:?}", &to)); let recipient = match to { Destination::Account(account) => deps.api.addr_humanize(&account)?.into_string(), Destination::Tip => tip.into(), diff --git a/code/xcvm/cosmwasm/tests/src/tests/framework.rs b/code/xcvm/cosmwasm/tests/src/tests/framework.rs index a4ad96ef581..a2c10050041 100644 --- a/code/xcvm/cosmwasm/tests/src/tests/framework.rs +++ b/code/xcvm/cosmwasm/tests/src/tests/framework.rs @@ -223,7 +223,7 @@ impl TestVM<()> { tx.info.clone(), tx.gas, xc_core::gateway::InstantiateMsg(HereItem { - here_id: self.network_id, + network_id: self.network_id, admin: todo!(), }), )?; @@ -274,7 +274,7 @@ impl TestVM> { tx.gas, xc_core::gateway::ExecuteMsg::Config(ConfigSubMsg::ForceAsset(AssetItem { asset_id, - from_network_id: todo!("restore"), + network_id: todo!("restore"), local: xc_core::gateway::AssetReference::Cw20 { contract: asset_address.clone().into(), }, diff --git a/code/xcvm/lib/core/schema/raw/execute.json b/code/xcvm/lib/core/schema/raw/execute.json index d3a27227df0..90fdc7d2b70 100644 --- a/code/xcvm/lib/core/schema/raw/execute.json +++ b/code/xcvm/lib/core/schema/raw/execute.json @@ -464,6 +464,34 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "force_asset_to_network_map" + ], + "properties": { + "force_asset_to_network_map": { + "type": "object", + "required": [ + "other_asset", + "other_network", + "this_asset" + ], + "properties": { + "other_asset": { + "$ref": "#/definitions/AssetId" + }, + "other_network": { + "$ref": "#/definitions/NetworkId" + }, + "this_asset": { + "$ref": "#/definitions/AssetId" + } + } + } + }, + "additionalProperties": false + }, { "description": "Message sent by an admin to remove an asset from registry.", "type": "object", @@ -484,6 +512,33 @@ } }, "additionalProperties": false + }, + { + "description": "instantiates default interpreter on behalf of user `salt` - human string, converted to hex or base64 depending on implementation", + "type": "object", + "required": [ + "force_instantiate" + ], + "properties": { + "force_instantiate": { + "type": "object", + "required": [ + "user_origin" + ], + "properties": { + "salt": { + "type": [ + "string", + "null" + ] + }, + "user_origin": { + "$ref": "#/definitions/Addr" + } + } + } + }, + "additionalProperties": false } ] }, @@ -544,7 +599,7 @@ ] }, "salt": { - "description": "The program salt.", + "description": "The program salt. If JSON, than hex encoded non prefixed lower case string.", "type": "string" } } @@ -732,54 +787,6 @@ } ] }, - "IbcTimeout": { - "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", - "type": "object", - "properties": { - "block": { - "anyOf": [ - { - "$ref": "#/definitions/IbcTimeoutBlock" - }, - { - "type": "null" - } - ] - }, - "timestamp": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - } - }, - "IbcTimeoutBlock": { - "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", - "type": "object", - "required": [ - "height", - "revision" - ], - "properties": { - "height": { - "description": "block height after which the packet times out. the height within the given revision", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "revision": { - "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - } - }, "Ics20Channel": { "type": "object", "required": [ @@ -948,6 +955,7 @@ "$ref": "#/definitions/Program_for_Array_of_Instruction_for_Array_of_uint8_and_CanonicalAddr_and_Funds_for_Balance" }, "salt": { + "description": "If JSON, than hex encoded non prefixed lower case string.", "type": "string" } } @@ -1060,7 +1068,7 @@ "description": "default timeout to use for direct send", "allOf": [ { - "$ref": "#/definitions/IbcTimeout" + "$ref": "#/definitions/RelativeTimeout" } ] }, @@ -1090,6 +1098,50 @@ "PFM": { "type": "object" }, + "Packet_for_Program_for_Array_of_Instruction_for_Array_of_uint8_and_CanonicalAddr_and_Funds_for_Balance": { + "type": "object", + "required": [ + "assets", + "interpreter", + "program", + "salt", + "user_origin" + ], + "properties": { + "assets": { + "description": "The assets that were attached to the program.", + "allOf": [ + { + "$ref": "#/definitions/Funds_for_Displayed_for_uint128" + } + ] + }, + "interpreter": { + "description": "The interpreter that was the origin of this packet.", + "type": "string" + }, + "program": { + "description": "The protobuf encoded program.", + "allOf": [ + { + "$ref": "#/definitions/Program_for_Array_of_Instruction_for_Array_of_uint8_and_CanonicalAddr_and_Funds_for_Balance" + } + ] + }, + "salt": { + "description": "The salt associated with the program.", + "type": "string" + }, + "user_origin": { + "description": "The user that originated the first XCVM call.", + "allOf": [ + { + "$ref": "#/definitions/UserOrigin" + } + ] + } + } + }, "Prefix": { "description": "given prefix you may form accounts from 32 bit addresses or partially identify chains", "oneOf": [ @@ -1153,6 +1205,7 @@ } }, "tag": { + "description": "If JSON, than hex encoded non prefixed lower case string.", "type": "string" } } @@ -1189,6 +1242,25 @@ } ] }, + "RelativeTimeout": { + "oneOf": [ + { + "description": "Timeout is relative to the current block timestamp of counter party", + "type": "object", + "required": [ + "seconds" + ], + "properties": { + "seconds": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + }, "ShortcutSubMsg": { "oneOf": [ { @@ -1238,22 +1310,10 @@ } ] }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", "type": "string" }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - }, "UserId": { "description": "Arbitrary `User` type that represent the identity of a user on a given network, usually a public key.", "type": "string" @@ -1289,15 +1349,15 @@ "description": "This message should be send as part of wasm termination memo. So that can match it to sender hash and know what channel and origin was used to send message. All information here is not secured until compared with existing secured data.", "type": "object", "required": [ - "data", - "from_network_id" + "from_network_id", + "packet" ], "properties": { - "data": { - "$ref": "#/definitions/Binary" - }, "from_network_id": { "$ref": "#/definitions/NetworkId" + }, + "packet": { + "$ref": "#/definitions/Packet_for_Program_for_Array_of_Instruction_for_Array_of_uint8_and_CanonicalAddr_and_Funds_for_Balance" } } } diff --git a/code/xcvm/lib/core/schema/raw/query.json b/code/xcvm/lib/core/schema/raw/query.json index 53268118375..775c72f29cb 100644 --- a/code/xcvm/lib/core/schema/raw/query.json +++ b/code/xcvm/lib/core/schema/raw/query.json @@ -22,9 +22,58 @@ } }, "additionalProperties": false + }, + { + "description": "Returns [`AssetItem`] for an asset with given local reference.", + "type": "object", + "required": [ + "get_local_asset_by_reference" + ], + "properties": { + "get_local_asset_by_reference": { + "type": "object", + "required": [ + "reference" + ], + "properties": { + "reference": { + "$ref": "#/definitions/AssetReference" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_ibc_ics20_route" + ], + "properties": { + "get_ibc_ics20_route": { + "type": "object", + "required": [ + "for_asset", + "to_network" + ], + "properties": { + "for_asset": { + "$ref": "#/definitions/AssetId" + }, + "to_network": { + "$ref": "#/definitions/NetworkId" + } + } + } + }, + "additionalProperties": false } ], "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "AssetId": { "description": "Newtype for XCVM assets ID. Must be unique for each asset and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", "allOf": [ @@ -33,11 +82,62 @@ } ] }, + "AssetReference": { + "description": "Definition of an asset on this local chain to operate with", + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "object", + "required": [ + "denom" + ], + "properties": { + "denom": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "object", + "required": [ + "contract" + ], + "properties": { + "contract": { + "$ref": "#/definitions/Addr" + } + } + } + }, + "additionalProperties": false + } + ] + }, "Displayed_for_uint128": { "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", "type": "integer", "format": "uint128", "minimum": 0.0 + }, + "NetworkId": { + "description": "Newtype for XCVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 } } } diff --git a/code/xcvm/lib/core/schema/raw/response_to_get_asset_by_id.json b/code/xcvm/lib/core/schema/raw/response_to_get_asset_by_id.json index 09b777b63cb..aade0feb1be 100644 --- a/code/xcvm/lib/core/schema/raw/response_to_get_asset_by_id.json +++ b/code/xcvm/lib/core/schema/raw/response_to_get_asset_by_id.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "GetAssetByIdResponse", + "title": "GetAssetResponse", "type": "object", "required": [ "asset" diff --git a/code/xcvm/lib/core/schema/raw/response_to_get_ibc_ics20_route.json b/code/xcvm/lib/core/schema/raw/response_to_get_ibc_ics20_route.json new file mode 100644 index 00000000000..19d40b17533 --- /dev/null +++ b/code/xcvm/lib/core/schema/raw/response_to_get_ibc_ics20_route.json @@ -0,0 +1,124 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetIbcIcs20RouteResponse", + "type": "object", + "required": [ + "route" + ], + "properties": { + "route": { + "$ref": "#/definitions/IbcIcs20Route" + } + }, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "AssetId": { + "description": "Newtype for XCVM assets ID. Must be unique for each asset and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", + "allOf": [ + { + "$ref": "#/definitions/Displayed_for_uint128" + } + ] + }, + "ChannelId": { + "type": "string" + }, + "Displayed_for_uint128": { + "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", + "type": "integer", + "format": "uint128", + "minimum": 0.0 + }, + "IbcIcs20Route": { + "description": "route is used to describe how to send a packet to another network", + "type": "object", + "required": [ + "channel_to_send_over", + "counterparty_timeout", + "from_network", + "gateway_to_send_to", + "ibc_ics_20_sender", + "local_native_denom", + "on_remote_asset", + "sender_gateway" + ], + "properties": { + "channel_to_send_over": { + "$ref": "#/definitions/ChannelId" + }, + "counterparty_timeout": { + "$ref": "#/definitions/RelativeTimeout" + }, + "from_network": { + "$ref": "#/definitions/NetworkId" + }, + "gateway_to_send_to": { + "$ref": "#/definitions/Addr" + }, + "ibc_ics_20_sender": { + "$ref": "#/definitions/IbcIcs20Sender" + }, + "local_native_denom": { + "type": "string" + }, + "on_remote_asset": { + "$ref": "#/definitions/AssetId" + }, + "sender_gateway": { + "$ref": "#/definitions/Addr" + } + } + }, + "IbcIcs20Sender": { + "oneOf": [ + { + "type": "string", + "enum": [ + "CosmosStargateIbcApplicationsTransferV1MsgTransfer", + "CosmWasmStd1_3" + ] + }, + { + "type": "object", + "required": [ + "SubstratePrecompile" + ], + "properties": { + "SubstratePrecompile": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "NetworkId": { + "description": "Newtype for XCVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "RelativeTimeout": { + "oneOf": [ + { + "description": "Timeout is relative to the current block timestamp of counter party", + "type": "object", + "required": [ + "seconds" + ], + "properties": { + "seconds": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/code/xcvm/lib/core/schema/raw/response_to_get_local_asset_by_reference.json b/code/xcvm/lib/core/schema/raw/response_to_get_local_asset_by_reference.json new file mode 100644 index 00000000000..aade0feb1be --- /dev/null +++ b/code/xcvm/lib/core/schema/raw/response_to_get_local_asset_by_reference.json @@ -0,0 +1,158 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetAssetResponse", + "type": "object", + "required": [ + "asset" + ], + "properties": { + "asset": { + "$ref": "#/definitions/AssetItem" + } + }, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "AssetId": { + "description": "Newtype for XCVM assets ID. Must be unique for each asset and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", + "allOf": [ + { + "$ref": "#/definitions/Displayed_for_uint128" + } + ] + }, + "AssetItem": { + "type": "object", + "required": [ + "asset_id", + "from_network_id", + "local" + ], + "properties": { + "asset_id": { + "$ref": "#/definitions/AssetId" + }, + "bridged": { + "anyOf": [ + { + "$ref": "#/definitions/BridgeAsset" + }, + { + "type": "null" + } + ] + }, + "from_network_id": { + "$ref": "#/definitions/NetworkId" + }, + "local": { + "$ref": "#/definitions/AssetReference" + } + } + }, + "AssetReference": { + "description": "Definition of an asset on this local chain to operate with", + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "object", + "required": [ + "denom" + ], + "properties": { + "denom": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "object", + "required": [ + "contract" + ], + "properties": { + "contract": { + "$ref": "#/definitions/Addr" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "BridgeAsset": { + "type": "object", + "required": [ + "location_on_network" + ], + "properties": { + "location_on_network": { + "$ref": "#/definitions/ForeignAssetId" + } + } + }, + "Displayed_for_uint128": { + "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", + "type": "integer", + "format": "uint128", + "minimum": 0.0 + }, + "ForeignAssetId": { + "oneOf": [ + { + "type": "object", + "required": [ + "ibc_ics20" + ], + "properties": { + "ibc_ics20": { + "$ref": "#/definitions/PrefixedDenom" + } + }, + "additionalProperties": false + } + ] + }, + "NetworkId": { + "description": "Newtype for XCVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "PrefixedDenom": { + "description": "A type that contains the base denomination for ICS20 and the source tracing information path.", + "type": "object", + "required": [ + "base_denom", + "trace_path" + ], + "properties": { + "base_denom": { + "description": "Base denomination of the relayed fungible token.", + "type": "string" + }, + "trace_path": { + "description": "A series of `{port-id}/{channel-id}`s for tracing the source of the token.", + "type": "string" + } + } + } + } +} diff --git a/code/xcvm/lib/core/schema/xc-core.json b/code/xcvm/lib/core/schema/xc-core.json index b522cce4c90..c4ddd22ecad 100644 --- a/code/xcvm/lib/core/schema/xc-core.json +++ b/code/xcvm/lib/core/schema/xc-core.json @@ -514,6 +514,34 @@ }, "additionalProperties": false }, + { + "type": "object", + "required": [ + "force_asset_to_network_map" + ], + "properties": { + "force_asset_to_network_map": { + "type": "object", + "required": [ + "other_asset", + "other_network", + "this_asset" + ], + "properties": { + "other_asset": { + "$ref": "#/definitions/AssetId" + }, + "other_network": { + "$ref": "#/definitions/NetworkId" + }, + "this_asset": { + "$ref": "#/definitions/AssetId" + } + } + } + }, + "additionalProperties": false + }, { "description": "Message sent by an admin to remove an asset from registry.", "type": "object", @@ -534,6 +562,33 @@ } }, "additionalProperties": false + }, + { + "description": "instantiates default interpreter on behalf of user `salt` - human string, converted to hex or base64 depending on implementation", + "type": "object", + "required": [ + "force_instantiate" + ], + "properties": { + "force_instantiate": { + "type": "object", + "required": [ + "user_origin" + ], + "properties": { + "salt": { + "type": [ + "string", + "null" + ] + }, + "user_origin": { + "$ref": "#/definitions/Addr" + } + } + } + }, + "additionalProperties": false } ] }, @@ -594,7 +649,7 @@ ] }, "salt": { - "description": "The program salt.", + "description": "The program salt. If JSON, than hex encoded non prefixed lower case string.", "type": "string" } } @@ -782,54 +837,6 @@ } ] }, - "IbcTimeout": { - "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", - "type": "object", - "properties": { - "block": { - "anyOf": [ - { - "$ref": "#/definitions/IbcTimeoutBlock" - }, - { - "type": "null" - } - ] - }, - "timestamp": { - "anyOf": [ - { - "$ref": "#/definitions/Timestamp" - }, - { - "type": "null" - } - ] - } - } - }, - "IbcTimeoutBlock": { - "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", - "type": "object", - "required": [ - "height", - "revision" - ], - "properties": { - "height": { - "description": "block height after which the packet times out. the height within the given revision", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "revision": { - "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - } - }, "Ics20Channel": { "type": "object", "required": [ @@ -998,6 +1005,7 @@ "$ref": "#/definitions/Program_for_Array_of_Instruction_for_Array_of_uint8_and_CanonicalAddr_and_Funds_for_Balance" }, "salt": { + "description": "If JSON, than hex encoded non prefixed lower case string.", "type": "string" } } @@ -1110,7 +1118,7 @@ "description": "default timeout to use for direct send", "allOf": [ { - "$ref": "#/definitions/IbcTimeout" + "$ref": "#/definitions/RelativeTimeout" } ] }, @@ -1140,6 +1148,50 @@ "PFM": { "type": "object" }, + "Packet_for_Program_for_Array_of_Instruction_for_Array_of_uint8_and_CanonicalAddr_and_Funds_for_Balance": { + "type": "object", + "required": [ + "assets", + "interpreter", + "program", + "salt", + "user_origin" + ], + "properties": { + "assets": { + "description": "The assets that were attached to the program.", + "allOf": [ + { + "$ref": "#/definitions/Funds_for_Displayed_for_uint128" + } + ] + }, + "interpreter": { + "description": "The interpreter that was the origin of this packet.", + "type": "string" + }, + "program": { + "description": "The protobuf encoded program.", + "allOf": [ + { + "$ref": "#/definitions/Program_for_Array_of_Instruction_for_Array_of_uint8_and_CanonicalAddr_and_Funds_for_Balance" + } + ] + }, + "salt": { + "description": "The salt associated with the program.", + "type": "string" + }, + "user_origin": { + "description": "The user that originated the first XCVM call.", + "allOf": [ + { + "$ref": "#/definitions/UserOrigin" + } + ] + } + } + }, "Prefix": { "description": "given prefix you may form accounts from 32 bit addresses or partially identify chains", "oneOf": [ @@ -1203,6 +1255,7 @@ } }, "tag": { + "description": "If JSON, than hex encoded non prefixed lower case string.", "type": "string" } } @@ -1239,6 +1292,25 @@ } ] }, + "RelativeTimeout": { + "oneOf": [ + { + "description": "Timeout is relative to the current block timestamp of counter party", + "type": "object", + "required": [ + "seconds" + ], + "properties": { + "seconds": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + }, "ShortcutSubMsg": { "oneOf": [ { @@ -1288,22 +1360,10 @@ } ] }, - "Timestamp": { - "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", - "allOf": [ - { - "$ref": "#/definitions/Uint64" - } - ] - }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", "type": "string" }, - "Uint64": { - "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", - "type": "string" - }, "UserId": { "description": "Arbitrary `User` type that represent the identity of a user on a given network, usually a public key.", "type": "string" @@ -1339,15 +1399,15 @@ "description": "This message should be send as part of wasm termination memo. So that can match it to sender hash and know what channel and origin was used to send message. All information here is not secured until compared with existing secured data.", "type": "object", "required": [ - "data", - "from_network_id" + "from_network_id", + "packet" ], "properties": { - "data": { - "$ref": "#/definitions/Binary" - }, "from_network_id": { "$ref": "#/definitions/NetworkId" + }, + "packet": { + "$ref": "#/definitions/Packet_for_Program_for_Array_of_Instruction_for_Array_of_uint8_and_CanonicalAddr_and_Funds_for_Balance" } } } @@ -1377,9 +1437,58 @@ } }, "additionalProperties": false + }, + { + "description": "Returns [`AssetItem`] for an asset with given local reference.", + "type": "object", + "required": [ + "get_local_asset_by_reference" + ], + "properties": { + "get_local_asset_by_reference": { + "type": "object", + "required": [ + "reference" + ], + "properties": { + "reference": { + "$ref": "#/definitions/AssetReference" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_ibc_ics20_route" + ], + "properties": { + "get_ibc_ics20_route": { + "type": "object", + "required": [ + "for_asset", + "to_network" + ], + "properties": { + "for_asset": { + "$ref": "#/definitions/AssetId" + }, + "to_network": { + "$ref": "#/definitions/NetworkId" + } + } + } + }, + "additionalProperties": false } ], "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "AssetId": { "description": "Newtype for XCVM assets ID. Must be unique for each asset and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", "allOf": [ @@ -1388,11 +1497,62 @@ } ] }, + "AssetReference": { + "description": "Definition of an asset on this local chain to operate with", + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "object", + "required": [ + "denom" + ], + "properties": { + "denom": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "object", + "required": [ + "contract" + ], + "properties": { + "contract": { + "$ref": "#/definitions/Addr" + } + } + } + }, + "additionalProperties": false + } + ] + }, "Displayed_for_uint128": { "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", "type": "integer", "format": "uint128", "minimum": 0.0 + }, + "NetworkId": { + "description": "Newtype for XCVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 } } }, @@ -1401,7 +1561,289 @@ "responses": { "get_asset_by_id": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "GetAssetByIdResponse", + "title": "GetAssetResponse", + "type": "object", + "required": [ + "asset" + ], + "properties": { + "asset": { + "$ref": "#/definitions/AssetItem" + } + }, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "AssetId": { + "description": "Newtype for XCVM assets ID. Must be unique for each asset and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", + "allOf": [ + { + "$ref": "#/definitions/Displayed_for_uint128" + } + ] + }, + "AssetItem": { + "type": "object", + "required": [ + "asset_id", + "from_network_id", + "local" + ], + "properties": { + "asset_id": { + "$ref": "#/definitions/AssetId" + }, + "bridged": { + "anyOf": [ + { + "$ref": "#/definitions/BridgeAsset" + }, + { + "type": "null" + } + ] + }, + "from_network_id": { + "$ref": "#/definitions/NetworkId" + }, + "local": { + "$ref": "#/definitions/AssetReference" + } + } + }, + "AssetReference": { + "description": "Definition of an asset on this local chain to operate with", + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "object", + "required": [ + "denom" + ], + "properties": { + "denom": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "object", + "required": [ + "contract" + ], + "properties": { + "contract": { + "$ref": "#/definitions/Addr" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "BridgeAsset": { + "type": "object", + "required": [ + "location_on_network" + ], + "properties": { + "location_on_network": { + "$ref": "#/definitions/ForeignAssetId" + } + } + }, + "Displayed_for_uint128": { + "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", + "type": "integer", + "format": "uint128", + "minimum": 0.0 + }, + "ForeignAssetId": { + "oneOf": [ + { + "type": "object", + "required": [ + "ibc_ics20" + ], + "properties": { + "ibc_ics20": { + "$ref": "#/definitions/PrefixedDenom" + } + }, + "additionalProperties": false + } + ] + }, + "NetworkId": { + "description": "Newtype for XCVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "PrefixedDenom": { + "description": "A type that contains the base denomination for ICS20 and the source tracing information path.", + "type": "object", + "required": [ + "base_denom", + "trace_path" + ], + "properties": { + "base_denom": { + "description": "Base denomination of the relayed fungible token.", + "type": "string" + }, + "trace_path": { + "description": "A series of `{port-id}/{channel-id}`s for tracing the source of the token.", + "type": "string" + } + } + } + } + }, + "get_ibc_ics20_route": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetIbcIcs20RouteResponse", + "type": "object", + "required": [ + "route" + ], + "properties": { + "route": { + "$ref": "#/definitions/IbcIcs20Route" + } + }, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "AssetId": { + "description": "Newtype for XCVM assets ID. Must be unique for each asset and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", + "allOf": [ + { + "$ref": "#/definitions/Displayed_for_uint128" + } + ] + }, + "ChannelId": { + "type": "string" + }, + "Displayed_for_uint128": { + "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", + "type": "integer", + "format": "uint128", + "minimum": 0.0 + }, + "IbcIcs20Route": { + "description": "route is used to describe how to send a packet to another network", + "type": "object", + "required": [ + "channel_to_send_over", + "counterparty_timeout", + "from_network", + "gateway_to_send_to", + "ibc_ics_20_sender", + "local_native_denom", + "on_remote_asset", + "sender_gateway" + ], + "properties": { + "channel_to_send_over": { + "$ref": "#/definitions/ChannelId" + }, + "counterparty_timeout": { + "$ref": "#/definitions/RelativeTimeout" + }, + "from_network": { + "$ref": "#/definitions/NetworkId" + }, + "gateway_to_send_to": { + "$ref": "#/definitions/Addr" + }, + "ibc_ics_20_sender": { + "$ref": "#/definitions/IbcIcs20Sender" + }, + "local_native_denom": { + "type": "string" + }, + "on_remote_asset": { + "$ref": "#/definitions/AssetId" + }, + "sender_gateway": { + "$ref": "#/definitions/Addr" + } + } + }, + "IbcIcs20Sender": { + "oneOf": [ + { + "type": "string", + "enum": [ + "CosmosStargateIbcApplicationsTransferV1MsgTransfer", + "CosmWasmStd1_3" + ] + }, + { + "type": "object", + "required": [ + "SubstratePrecompile" + ], + "properties": { + "SubstratePrecompile": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "NetworkId": { + "description": "Newtype for XCVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "RelativeTimeout": { + "oneOf": [ + { + "description": "Timeout is relative to the current block timestamp of counter party", + "type": "object", + "required": [ + "seconds" + ], + "properties": { + "seconds": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + } + } + }, + "get_local_asset_by_reference": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetAssetResponse", "type": "object", "required": [ "asset" diff --git a/code/xcvm/lib/core/src/cosmos.rs b/code/xcvm/lib/core/src/cosmos.rs index 8102464491e..988826f6256 100644 --- a/code/xcvm/lib/core/src/cosmos.rs +++ b/code/xcvm/lib/core/src/cosmos.rs @@ -19,8 +19,9 @@ pub fn addess_hash(typ: &str, key: &[u8]) -> [u8; 32] { // takes a transfer message and returns ibc/ // https://ibc.cosmos.network/main/architecture/adr-001-coin-source-tracing.html // so can infer for some chain denom on hops -pub fn hash_denom_trace(unwrapped: &str) -> String { - let digest = Sha256::digest(unwrapped.as_bytes()); +pub fn hash_denom_trace(denom: &PrefixedDenom) -> String { + let denom = denom.to_string(); + let digest = Sha256::digest(denom.as_bytes()); ["ibc/", &hex::encode_upper(digest)].concat() } @@ -31,21 +32,27 @@ mod tests { // various devnet channels hashes #[test] fn devnet() { - let pica = hash_denom_trace("/transfer/channel-1/1"); - assert_eq!(pica, "ibc/B62D63F2BD5A7B70AB15F84BCB70EAC88222D3A8E8E0B22793EE788068EA22BA"); - let pica = hash_denom_trace("/transfer/channel-0/1"); - assert_eq!(pica, "ibc/F2B6EF5B6F86990A3863B78687ADE3D95E412657AAEB7CF2B3B8131B8055C1F1"); - - let pica: String = hash_denom_trace("/transfer/channel-1/ppica"); - assert_eq!(pica, "ibc/661BD30059657725608DF36907F06B70C1FA7A1772FF92AEE1844A3E35A80D63"); - - let pica: String = hash_denom_trace("/transfer/channel-0/ppica"); - assert_eq!(pica, "ibc/F0E228914E0E69E7B5E9231282FE6B7595CF90CB76E7193C6AFDCACDF5E83821"); - - let osmo: String = hash_denom_trace("/transfer/channel-1/uosmo"); - assert_eq!(osmo, "ibc/BCACECE44E39A9009D793D68CC5DF76B402607B1C574379DCB4F3A5D24BC1936"); - - let osmo: String = hash_denom_trace("/transfer/channel-0/uosmo"); - assert_eq!(osmo, "ibc/B4511F40A2844906F5940444691EE3AE877E8E0DD8354C8C4D36670A46C5680D"); + let pica = + hash_denom_trace(&PrefixedDenom::from_str("transfer/channel-1/1").expect("const")); + assert_eq!(pica, "ibc/71B5DB2263A5A5B160BBA26A307BF5441BDB330534C19A9F551F63D9CC0C3026"); + let pica = + hash_denom_trace(&PrefixedDenom::from_str("transfer/channel-0/1").expect("const")); + assert_eq!(pica, "ibc/632DBFDB06584976F1351A66E873BF0F7A19FAA083425FEC9890C90993E5F0A4"); + + let pica: String = + hash_denom_trace(&PrefixedDenom::from_str("transfer/channel-1/ppica").expect("const")); + assert_eq!(pica, "ibc/6188228DA6C48BB205E30BD8850E2E5ADBD75010B9BF542F7E77A87D9D7DCCB7"); + + let pica: String = + hash_denom_trace(&PrefixedDenom::from_str("transfer/channel-0/ppica").expect("const")); + assert_eq!(pica, "ibc/3262D378E1636BE287EC355990D229DCEB828F0C60ED5049729575E235C60E8B"); + + let osmo: String = + hash_denom_trace(&PrefixedDenom::from_str("transfer/channel-1/uosmo").expect("const")); + assert_eq!(osmo, "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B"); + + let osmo: String = + hash_denom_trace(&PrefixedDenom::from_str("transfer/channel-0/uosmo").expect("const")); + assert_eq!(osmo, "ibc/ED07A3391A112B175915CD8FAF43A2DA8E4790EDE12566649D0C2F97716B8518"); } } diff --git a/code/xcvm/lib/core/src/gateway/config.rs b/code/xcvm/lib/core/src/gateway/config.rs index 14e7dd0bece..3380265803b 100644 --- a/code/xcvm/lib/core/src/gateway/config.rs +++ b/code/xcvm/lib/core/src/gateway/config.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::IbcTimeout; +use cosmwasm_std::{BlockInfo, IbcTimeout}; use ibc_rs_scale::core::ics24_host::identifier::ChannelId; use crate::{ @@ -127,13 +127,32 @@ pub struct IcsPair { pub sink: ChannelId, } +/// relative timeout to CW/IBC-rs time. +/// very small, assumed messages are arriving fast enough, like less than hours +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Encode, Decode)] +#[serde(rename_all = "snake_case")] +#[cfg_attr(feature = "std", derive(schemars::JsonSchema))] +pub enum RelativeTimeout { + /// Timeout is relative to the current block timestamp of counter party + Seconds(u16), +} + +impl RelativeTimeout { + pub fn absolute(&self, block: BlockInfo) -> IbcTimeout { + match self { + RelativeTimeout::Seconds(seconds) => + IbcTimeout::with_timestamp(block.time.plus_seconds(*seconds as u64)), + } + } +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(rename_all = "snake_case")] #[cfg_attr(feature = "std", derive(schemars::JsonSchema))] pub struct OtherNetworkItem { pub ics_20: Option, /// default timeout to use for direct send - pub counterparty_timeout: IbcTimeout, + pub counterparty_timeout: RelativeTimeout, /// if there is custom IBC channel opened pub xcvm_channel: Option, } @@ -174,7 +193,8 @@ pub enum ConfigSubMsg { /// `salt` - human string, converted to hex or base64 depending on implementation ForceInstantiate { user_origin: Addr, - salt: Option, + #[serde(skip_serializing_if = "String::is_empty", default)] + salt: String, }, } @@ -187,8 +207,8 @@ pub struct InstantiateMsg(pub HereItem); #[serde(rename_all = "snake_case")] #[cfg_attr(feature = "std", derive(schemars::JsonSchema))] pub struct HereItem { - /// Network ID of this network - pub here_id: NetworkId, + /// Network ID of this network where contract is deployed + pub network_id: NetworkId, /// The admin which is allowed to update the bridge list. pub admin: Addr, } @@ -213,8 +233,10 @@ pub enum GatewayId { pub struct AssetItem { pub asset_id: AssetId, - pub from_network_id: NetworkId, + /// network id on which this asset id can be used locally + pub network_id: NetworkId, pub local: AssetReference, + /// if asset was bridged, it would have way to identify bridge/source/channel pub bridged: Option, } diff --git a/code/xcvm/lib/core/src/gateway/mod.rs b/code/xcvm/lib/core/src/gateway/mod.rs index 82f8f22238d..eb277596a69 100644 --- a/code/xcvm/lib/core/src/gateway/mod.rs +++ b/code/xcvm/lib/core/src/gateway/mod.rs @@ -226,6 +226,8 @@ impl Gateway { #[cfg(test)] mod tests { + use cosmwasm_std::CanonicalAddr; + use crate::{ gateway::{ExecuteMsg, ExecuteProgramMsg}, generate_asset_id, @@ -387,6 +389,116 @@ mod tests { assert_eq!(program, expected) } + #[test] + fn spawn_with_asset_and_transfer() { + let pica_on_centauri = generate_asset_id(2.into(), 0, 1); + let pica_on_osmosis = generate_asset_id(3.into(), 0, 1); + + let program = 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: 3.into(), + salt: b"spawn_with_asset".to_vec(), + assets: vec![(pica_on_osmosis, 1_000_000_000u128)].into(), + program: XcProgram { + tag: b"spawn_with_asset".to_vec(), + instructions: [XcInstruction::Transfer { + to: crate::Destination::Account(CanonicalAddr( + Binary::from_base64("AB9vNpqXOevUvR5+JDnlljDbHhw=").unwrap(), + )), + assets: crate::Funds(vec![( + pica_on_osmosis, + 1_000_000_000u128.into(), + )]), + }] + .into(), + }, + }] + .into(), + }, + assets: vec![(pica_on_centauri, 1_000_000_000u128)].into(), + }, + tip: Addr::unchecked("centauri12smx2wdlyttvyzvzg54y2vnqwq2qjatescq89n"), + }; + + //pica_on_osmosis + + let program = serde_json_wasm::to_string(&program).expect("serde"); + 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": 3, + "salt": "737061776e5f776974685f6173736574", + "assets": [ + [ + "237684487542793012780631851009", + { + "amount": { + "intercept": "1000000000", + "slope": "0" + }, + "is_unit": false + } + ] + ], + "program": { + "tag": "737061776e5f776974685f6173736574", + "instructions": [ + { + "transfer": { + "to": { + "account": "AB9vNpqXOevUvR5+JDnlljDbHhw=" + }, + "assets": [ + [ + "237684487542793012780631851009", + { + "amount": { + "intercept": "1000000000", + "slope": "0" + }, + "is_unit": false + } + ] + ] + } + } + ] + } + } + } + ] + }, + "assets": [ + [ + "158456325028528675187087900673", + "1000000000" + ] + ] + }, + "tip": "centauri12smx2wdlyttvyzvzg54y2vnqwq2qjatescq89n" + } + } + "#, + ) + .unwrap(), + ) + .unwrap(); + assert_eq!(program, expected) + } + #[test] fn osmosis_spawn_with_asset() { let osmo_on_osmosis = generate_asset_id(3.into(), 0, 1001); diff --git a/code/xcvm/lib/core/src/gateway/sad.json b/code/xcvm/lib/core/src/gateway/sad.json deleted file mode 100644 index 60b4b6b05d2..00000000000 --- a/code/xcvm/lib/core/src/gateway/sad.json +++ /dev/null @@ -1 +0,0 @@ -"{\"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/prelude.rs b/code/xcvm/lib/core/src/prelude.rs index ddcc9b75d49..3558b9fa792 100644 --- a/code/xcvm/lib/core/src/prelude.rs +++ b/code/xcvm/lib/core/src/prelude.rs @@ -1,6 +1,7 @@ pub use alloc::{ boxed::Box, collections::VecDeque, + format, string::{String, ToString}, vec, vec::Vec, diff --git a/code/xcvm/lib/core/src/shared.rs b/code/xcvm/lib/core/src/shared.rs index 48fb8773697..8f80d641570 100644 --- a/code/xcvm/lib/core/src/shared.rs +++ b/code/xcvm/lib/core/src/shared.rs @@ -1,11 +1,12 @@ -use crate::prelude::*; +use crate::{prelude::*, AssetId, Displayed}; use cosmwasm_std::{from_binary, to_binary, Binary, CanonicalAddr, StdResult}; use serde::{de::DeserializeOwned, Serialize}; +pub type Salt = Vec; +pub type XcFunds = Vec<(AssetId, Displayed)>; pub type XcInstruction = crate::Instruction, CanonicalAddr, crate::Funds>; -pub type XcProgram = crate::Program>; pub type XcPacket = crate::Packet; -pub type Salt = Vec; +pub type XcProgram = crate::Program>; pub fn encode_base64(x: &T) -> StdResult { Ok(to_binary(x)?.to_base64()) diff --git a/code/xcvm/lib/core/src/transport/ibc/ics20/hook.rs b/code/xcvm/lib/core/src/transport/ibc/ics20/hook.rs index 0677d8290d7..b200bce4e3e 100644 --- a/code/xcvm/lib/core/src/transport/ibc/ics20/hook.rs +++ b/code/xcvm/lib/core/src/transport/ibc/ics20/hook.rs @@ -28,7 +28,14 @@ pub enum IBCLifecycleComplete { }, } -/// from Go code to make compliant wasm hook +/// derives the sender address to be used when calling wasm hooks +/// https://github.com/osmosis-labs/osmosis/blob/master/x/ibc-hooks/keeper/keeper.go#L170 +/// ```rust +/// let channel = ibc_rs_scale::core::ics24_host::identifier::ChannelId::new(0); +/// let original_sender = "juno12smx2wdlyttvyzvzg54y2vnqwq2qjatezqwqxu"; +/// let hashed_sender = xc_core::transport::ibc::ics20::hook::derive_intermediate_sender(&channel, original_sender, "osmo").expect("new address"); +/// assert_eq!(hashed_sender, "osmo1nt0pudh879m6enw4j6z4mvyu3vmwawjv5gr7xw6lvhdsdpn3m0qs74xdjl"); +/// ``` pub fn derive_intermediate_sender( channel: &ChannelId, original_sender: &str, @@ -50,6 +57,6 @@ pub struct Callback { // really Addr, but it does not have scale, I guess we need to impl `type XcAddr = SS58 | // Bech32` with signer inside for serde pub contract: Addr, - /// really serde_cw_value::Value, but it has not scale + /// Is a valid JSON object. The contract will be called with this as the message. pub msg: serde_cw_value::Value, } diff --git a/code/xcvm/lib/core/src/transport/ibc/ics20/mod.rs b/code/xcvm/lib/core/src/transport/ibc/ics20/mod.rs index 13549fb76ca..f08499b2483 100644 --- a/code/xcvm/lib/core/src/transport/ibc/ics20/mod.rs +++ b/code/xcvm/lib/core/src/transport/ibc/ics20/mod.rs @@ -12,6 +12,7 @@ use self::{hook::Callback, pfm::ForwardingMemo}; #[cfg_attr(feature = "std", derive(schemars::JsonSchema))] // Encode, Decode, scale_info::TypeInfo, to be manually implemented for subset of know messages pub struct Memo { + /// memo has at least one key, with value "wasm", than wasm hooks will try to execute it #[serde(skip_serializing_if = "Option::is_none")] pub wasm: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/code/xcvm/lib/core/src/transport/ibc/mod.rs b/code/xcvm/lib/core/src/transport/ibc/mod.rs index 4a887f6198d..78c0e9ea1ae 100644 --- a/code/xcvm/lib/core/src/transport/ibc/mod.rs +++ b/code/xcvm/lib/core/src/transport/ibc/mod.rs @@ -1,8 +1,13 @@ pub mod ics20; pub mod picasso; -use crate::{prelude::*, shared::XcPacket, AssetId, NetworkId}; -use cosmwasm_std::{to_binary, CosmosMsg, IbcEndpoint, IbcTimeout, StdResult, WasmMsg}; +use crate::{ + gateway::{self, RelativeTimeout}, + prelude::*, + shared::XcPacket, + AssetId, NetworkId, +}; +use cosmwasm_std::{to_binary, Api, BlockInfo, CosmosMsg, IbcEndpoint, StdResult, WasmMsg}; use ibc_rs_scale::core::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; @@ -40,37 +45,43 @@ pub struct IbcIcs20Route { pub local_native_denom: String, pub channel_to_send_over: ChannelId, pub sender_gateway: Addr, + /// the contract address of the gateway to send to assets pub gateway_to_send_to: Addr, - pub counterparty_timeout: IbcTimeout, + pub counterparty_timeout: RelativeTimeout, pub ibc_ics_20_sender: IbcIcs20Sender, pub on_remote_asset: AssetId, } pub fn to_cw_message( + api: &dyn Api, coin: Coin, route: IbcIcs20Route, packet: XcPacket, + block: BlockInfo, ) -> StdResult> { - let memo = XcMessageData { from_network_id: route.from_network, packet }; + let msg = gateway::ExecuteMsg::MessageHook(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"), + msg: serde_cw_value::to_value(msg).expect("can always serde"), }), forward: None, }, ibc_callback: None, }; let memo = serde_json_wasm::to_string(&memo).expect("any memo can be to string"); - + api.debug(&format!("xcvm::ibc::ics20 callback {}", &memo)); match route.ibc_ics_20_sender { IbcIcs20Sender::SubstratePrecompile(addr) => { let transfer = picasso::IbcMsg::Transfer { channel_id: route.channel_to_send_over.clone(), to_address: route.gateway_to_send_to, amount: coin, - timeout: route.counterparty_timeout, + timeout: route.counterparty_timeout.absolute(block), memo: Some(memo), }; Ok(WasmMsg::Execute { @@ -95,16 +106,17 @@ pub fn to_cw_message( token: Some(Coin { denom: coin.denom, amount: coin.amount.to_string() }), sender: route.sender_gateway.to_string(), receiver: route.gateway_to_send_to.to_string(), - timeout_height: route.counterparty_timeout.block().map(|x| { - ibc_proto::ibc::core::client::v1::Height { + timeout_height: route.counterparty_timeout.absolute(block.clone()).block().map( + |x| ibc_proto::ibc::core::client::v1::Height { revision_height: x.height, revision_number: x.revision, - } - }), + }, + ), timeout_timestamp: route .counterparty_timeout + .absolute(block) .timestamp() - .map(|x| x.seconds()) + .map(|x| x.nanos()) .unwrap_or_default(), memo, } diff --git a/flake/hermes.nix b/flake/hermes.nix index 3af4d514a75..9cce98c4220 100644 --- a/flake/hermes.nix +++ b/flake/hermes.nix @@ -4,6 +4,7 @@ let devnet-root-directory = "/tmp/composable-devnet"; validator-key = "osmo12smx2wdlyttvyzvzg54y2vnqwq2qjateuf7thj"; + log = "debug"; in { packages = rec { hermes = self.inputs.cosmos.packages.${system}.hermes; @@ -11,6 +12,7 @@ runtimeInputs = devnetTools.withBaseContainerTools ++ [ hermes ]; name = "osmosis-centauri-hermes-init"; text = '' + RUST_LOG=${log} mkdir --parents "${devnet-root-directory}" HOME=${devnet-root-directory} export HOME @@ -24,7 +26,6 @@ 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" > "$MNEMONIC_FILE" hermes keys add --chain centauri-dev --mnemonic-file "$MNEMONIC_FILE" --key-name centauri-dev --overwrite hermes keys add --chain osmosis-dev --mnemonic-file "$MNEMONIC_FILE" --key-name osmosis-dev --overwrite - RUST_LOG=info export RUST_LOG hermes create channel --a-chain centauri-dev --b-chain osmosis-dev --a-port transfer --b-port transfer --new-client-connection --yes ''; @@ -33,10 +34,10 @@ runtimeInputs = devnetTools.withBaseContainerTools ++ [ hermes ]; name = "osmosis-centauri-hermes-relay"; text = '' + RUST_LOG=${log} mkdir --parents "${devnet-root-directory}" HOME=${devnet-root-directory} export HOME - RUST_LOG=info export RUST_LOG hermes start ''; diff --git a/flake/hermes.toml b/flake/hermes.toml index b05995ca74b..234e28d70a0 100644 --- a/flake/hermes.toml +++ b/flake/hermes.toml @@ -35,7 +35,7 @@ rpc_timeout = '20s' account_prefix = 'centauri' key_name = 'centauri-dev' store_prefix = 'ibc' -default_gas = 1000000 +default_gas = 10000000 max_gas = 400000000 gas_price = { price = 0.001, denom = 'ppica' } gas_multiplier = 1.1 @@ -43,7 +43,7 @@ max_msg_num = 30 max_tx_size = 2097152 clock_drift = '5s' max_block_time = '30s' -trusting_period = '60s' +trusting_period = '480s' trust_threshold = { numerator = '1', denominator = '3' } type = 'CosmosSdk' address_type = { derivation = 'cosmos' } @@ -59,15 +59,15 @@ account_prefix = 'osmo' key_name = 'osmosis-dev' store_prefix = 'ibc' key_store_type = 'Test' -default_gas = 100000 -max_gas = 400000 +default_gas = 10000000 +max_gas = 400000000 gas_price = { price = 0.01, denom = 'uosmo' } gas_multiplier = 1.1 max_msg_num = 30 max_tx_size = 2097152 clock_drift = '5s' max_block_time = '30s' -trusting_period = '60s' +trusting_period = '480s' trust_threshold = { numerator = '1', denominator = '3' } type = 'CosmosSdk' address_type = { derivation = 'cosmos' } diff --git a/flake/osmosis.nix b/flake/osmosis.nix index badc1affb92..47d05ab2458 100644 --- a/flake/osmosis.nix +++ b/flake/osmosis.nix @@ -45,7 +45,7 @@ } dasel-genesis '.app_state.staking.params.bond_denom' 'uosmo' - dasel-genesis '.app_state.staking.params.unbonding_time' '120s' + dasel-genesis '.app_state.staking.params.unbonding_time' '960s' dasel put --type json --file "$GENESIS" --value "[{},{}]" 'app_state.bank.denom_metadata' dasel-genesis '.app_state.bank.denom_metadata.[0].description' 'Registered denom uion for localosmosis testing' dasel put --type json --file "$GENESIS" --value "[{}]" '.app_state.bank.denom_metadata.[0].denom_units' @@ -147,8 +147,8 @@ ''; }; - osmosisd-init = pkgs.writeShellApplication { - name = "osmosisd-init"; + osmosisd-xcvm-init = pkgs.writeShellApplication { + name = "osmosisd-xcvm-init"; runtimeInputs = devnetTools.withBaseContainerTools ++ [ osmosisd pkgs.jq pkgs.dasel ]; text = '' @@ -184,23 +184,47 @@ 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 "$INTERPRETER_CODE_ID" > "$CHAIN_DATA/interpreter_code_id" } INSTANTIATE=$(cat << EOF { "admin" : "$KEY", - "here_id" : $NETWORK_ID + "network_id" : $NETWORK_ID } EOF ) - init_xcvm "$INSTANTIATE" + init_xcvm "$INSTANTIATE" + ''; + }; + + osmosisd-xcvm-config = pkgs.writeShellApplication { + name = "osmosisd-xcvm-config"; + runtimeInputs = devnetTools.withBaseContainerTools + ++ [ osmosisd pkgs.jq pkgs.dasel ]; + 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 + NETWORK_ID=3 + KEY=${cosmosTools.xcvm.osmosis} + BINARY=osmosisd + + GATEWAY_CONTRACT_ADDRESS=$(cat $CHAIN_DATA/gateway_contract_address) + CENTAURI_GATEWAY_CONTRACT_ADDRESS=$(cat "$HOME/.centaurid/gateway_contract_address") + INTERPRETER_CODE_ID=$(cat $CHAIN_DATA/interpreter_code_id) FORCE_NETWORK_OSMOSIS=$(cat << EOF { "config": { "force_network": { - "network_id": 3, + "network_id": $NETWORK_ID, "accounts": { "bech": "osmo" }, @@ -242,7 +266,7 @@ }, "gateway": { "cosm_wasm": { - "contract": "$GATEWAY_CONTRACT_ADDRESS", + "contract": "$CENTAURI_GATEWAY_CONTRACT_ADDRESS", "interpreter_code_id": $INTERPRETER_CODE_ID, "admin": "$KEY" } @@ -277,7 +301,7 @@ "to": 3, "other": { "counterparty_timeout": { - "timestamp": "60" + "seconds" : 120 }, "ics_20": { "source" : "channel-0", @@ -293,16 +317,41 @@ "$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 + FORCE_OSMOSIS_TO_CENTAURI=$(cat << EOF + { + "config": { + "force_network_to_network": { + "from": 3, + "to": 2, + "other": { + "counterparty_timeout": { + "seconds" : 120 + }, + "ics_20": { + "source" : "channel-0", + "sink" : "channel-0" + } + + } + } + } + } + EOF + ) + "$BINARY" tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_OSMOSIS_TO_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_PICA=$(cat << EOF { "config": { "force_asset": { - "asset_id": "79228162514264337593543950337", - "from_network_id": 2, + "asset_id": "237684487542793012780631851009", + "network_id": 3, "local": { "native": { - "denom": "ppica" + "denom": "ibc/3262D378E1636BE287EC355990D229DCEB828F0C60ED5049729575E235C60E8B" } }, "bridged": { @@ -327,10 +376,10 @@ "config": { "force_asset": { "asset_id": "158456325028528675187087901673", - "from_network_id": 3, + "network_id": 2, "local": { "native": { - "denom" : "uosmo" + "denom" : "transfer/channel-0/uosmo" } } } @@ -346,7 +395,7 @@ "config": { "force_asset": { "asset_id": "237684487542793012780631852009", - "from_network_id": 3, + "network_id": 3, "local": { "native": { "denom" : "uosmo" @@ -399,7 +448,7 @@ "158456325028528675187087901673", { "amount": { - "intercept": "1000000000", + "intercept": "1234567890", "slope": "0" }, "is_unit": false @@ -417,7 +466,7 @@ "assets": [ [ "237684487542793012780631852009", - "1000000000" + "1234567890" ] ] }, @@ -427,7 +476,7 @@ 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 + "$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 1234567890"$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 25d0f514ae1..eea61ada033 100644 --- a/flake/process-compose.nix +++ b/flake/process-compose.nix @@ -12,8 +12,8 @@ runtimeInputs = devnetTools.withBaseContainerTools; name = "devnet-xc-fresh-background"; text = '' - rm --force --recursive /tmp/composable-devnet - mkdir --parents /tmp/composable-devnet + rm --force --recursive ${devnet-root-directory} + mkdir --parents ${devnet-root-directory} ${pkgs.lib.meta.getExe self'.packages.devnet-xc-background} ''; }; @@ -22,8 +22,8 @@ runtimeInputs = devnetTools.withBaseContainerTools; name = "devnet-xc-dotsama-fresh-background"; text = '' - rm --force --recursive /tmp/composable-devnet - mkdir --parents /tmp/composable-devnet + rm --force --recursive ${devnet-root-directory} + mkdir --parents ${devnet-root-directory} ${pkgs.lib.meta.getExe self'.packages.devnet-xc-dotsama-background} ''; }; @@ -48,8 +48,8 @@ runtimeInputs = devnetTools.withBaseContainerTools; name = "devnet-xc-clean"; text = '' - rm --force --recursive /tmp/composable-devnet - mkdir --parents /tmp/composable-devnet + rm --force --recursive ${devnet-root-directory} + mkdir --parents ${devnet-root-directory} ''; }; @@ -57,8 +57,8 @@ runtimeInputs = devnetTools.withBaseContainerTools; name = "devnet-xc-cosmos-fresh"; text = '' - rm --force --recursive /tmp/composable-devnet - mkdir --parents /tmp/composable-devnet + rm --force --recursive ${devnet-root-directory} + mkdir --parents ${devnet-root-directory} ${pkgs.lib.meta.getExe self'.packages.devnet-xc-cosmos} ''; }; @@ -67,8 +67,8 @@ runtimeInputs = devnetTools.withBaseContainerTools; name = "devnet-xc-fresh"; text = '' - rm --force --recursive /tmp/composable-devnet - mkdir --parents /tmp/composable-devnet + rm --force --recursive ${devnet-root-directory} + mkdir --parents ${devnet-root-directory} ${pkgs.lib.meta.getExe self'.packages.devnet-xc} ''; }; @@ -82,25 +82,31 @@ settings = { processes = { centauri = { - command = self'.packages.centaurid-gen; + command = pkgs.writeShellApplication { + runtimeInputs = devnetTools.withBaseContainerTools; + name = "centauri"; + text = '' + ${pkgs.lib.meta.getExe self'.packages.centaurid-gen} reuse 0 + ''; + }; readiness_probe.http_get = { host = "127.0.0.1"; port = 26657; }; - log_location = "/tmp/composable-devnet/centauri.log"; + log_location = "${devnet-root-directory}/centauri.log"; availability = { restart = "on_failure"; }; }; centauri-init = { command = self'.packages.centaurid-init; depends_on."centauri".condition = "process_healthy"; - log_location = "/tmp/composable-devnet/centauri-init.log"; + log_location = "${devnet-root-directory}/centauri-init.log"; availability = { restart = "on_failure"; }; }; picasso = { command = self'.packages.zombienet-rococo-local-picasso-dev; availability = { restart = "on_failure"; }; - log_location = "/tmp/composable-devnet/picasso.log"; + log_location = "${devnet-root-directory}/picasso.log"; readiness_probe = { initial_delay_seconds = 32; period_seconds = 8; @@ -114,7 +120,7 @@ composable = { command = self'.packages.zombienet-composable-centauri-b; availability = { restart = "on_failure"; }; - log_location = "/tmp/composable-devnet/composable.log"; + log_location = "${devnet-root-directory}/composable.log"; readiness_probe = { initial_delay_seconds = 32; period_seconds = 8; @@ -128,7 +134,7 @@ picasso-centauri-ibc-init = { command = self'.packages.picasso-centauri-ibc-init; log_location = - "/tmp/composable-devnet/picasso-centauri-ibc-init.log"; + "${devnet-root-directory}/picasso-centauri-ibc-init.log"; depends_on = { "centauri-init".condition = "process_completed_successfully"; "centauri".condition = "process_healthy"; @@ -139,7 +145,7 @@ picasso-centauri-ibc-connection-init = { command = self'.packages.picasso-centauri-ibc-connection-init; log_location = - "/tmp/composable-devnet/picasso-centauri-ibc-connection-init.log"; + "${devnet-root-directory}/picasso-centauri-ibc-connection-init.log"; depends_on = { "picasso-centauri-ibc-init".condition = "process_completed_successfully"; @@ -150,7 +156,7 @@ picasso-centauri-ibc-channels-init = { command = self'.packages.picasso-centauri-ibc-channels-init; log_location = - "/tmp/composable-devnet/picasso-centauri-ibc-channels-init.log"; + "${devnet-root-directory}/picasso-centauri-ibc-channels-init.log"; depends_on = { "picasso-centauri-ibc-connection-init".condition = "process_completed_successfully"; @@ -161,7 +167,7 @@ picasso-centauri-ibc-relay = { command = self'.packages.picasso-centauri-ibc-relay; log_location = - "/tmp/composable-devnet/picasso-centauri-ibc-relay.log"; + "${devnet-root-directory}/picasso-centauri-ibc-relay.log"; depends_on = { "picasso-centauri-ibc-channels-init".condition = "process_completed_successfully"; @@ -172,7 +178,7 @@ composable-picasso-ibc-init = { command = self'.packages.composable-picasso-ibc-init; log_location = - "/tmp/composable-devnet/composable-picasso-ibc-init.log"; + "${devnet-root-directory}/composable-picasso-ibc-init.log"; depends_on = { "picasso-centauri-ibc-channels-init".condition = "process_completed_successfully"; @@ -183,14 +189,14 @@ }; composable-picasso-ibc-connection-init = { command = '' - HOME="/tmp/composable-devnet/composable-picasso-ibc" + HOME="${devnet-root-directory}/composable-picasso-ibc" export HOME RUST_LOG="hyperspace=info,hyperspace_parachain=debug,hyperspace_cosmos=debug" export RUST_LOG - ${self'.packages.hyperspace-composable-rococo-picasso-rococo}/bin/hyperspace create-connection --config-a /tmp/composable-devnet/composable-picasso-ibc/config-chain-a.toml --config-b /tmp/composable-devnet/composable-picasso-ibc/config-chain-b.toml --config-core /tmp/composable-devnet/composable-picasso-ibc/config-core.toml --delay-period 10 + ${self'.packages.hyperspace-composable-rococo-picasso-rococo}/bin/hyperspace create-connection --config-a ${devnet-root-directory}/composable-picasso-ibc/config-chain-a.toml --config-b ${devnet-root-directory}/composable-picasso-ibc/config-chain-b.toml --config-core ${devnet-root-directory}/composable-picasso-ibc/config-core.toml --delay-period 10 ''; log_location = - "/tmp/composable-devnet/composable-picasso-ibc-connection-init.log"; + "${devnet-root-directory}/composable-picasso-ibc-connection-init.log"; depends_on = { "composable-picasso-ibc-init".condition = "process_completed_successfully"; @@ -200,14 +206,14 @@ composable-picasso-ibc-channels-init = { command = '' - HOME="/tmp/composable-devnet/composable-picasso-ibc" + HOME="${devnet-root-directory}/composable-picasso-ibc" export HOME RUST_LOG="hyperspace=info,hyperspace_parachain=debug,hyperspace_cosmos=debug" export RUST_LOG - ${self'.packages.hyperspace-composable-rococo-picasso-rococo}/bin/hyperspace create-channel --config-a /tmp/composable-devnet/composable-picasso-ibc/config-chain-a.toml --config-b /tmp/composable-devnet/composable-picasso-ibc/config-chain-b.toml --config-core /tmp/composable-devnet/composable-picasso-ibc/config-core.toml --delay-period 10 --port-id transfer --version ics20-1 --order unordered + ${self'.packages.hyperspace-composable-rococo-picasso-rococo}/bin/hyperspace create-channel --config-a ${devnet-root-directory}/composable-picasso-ibc/config-chain-a.toml --config-b ${devnet-root-directory}/composable-picasso-ibc/config-chain-b.toml --config-core ${devnet-root-directory}/composable-picasso-ibc/config-core.toml --delay-period 10 --port-id transfer --version ics20-1 --order unordered ''; log_location = - "/tmp/composable-devnet/composable-picasso-ibc-channels-init.log"; + "${devnet-root-directory}/composable-picasso-ibc-channels-init.log"; depends_on = { "composable-picasso-ibc-connection-init".condition = "process_completed_successfully"; @@ -217,7 +223,7 @@ composable-picasso-ibc-relay = { command = self'.packages.composable-picasso-ibc-relay; log_location = - "/tmp/composable-devnet/composable-picasso-ibc-relay.log"; + "${devnet-root-directory}/composable-picasso-ibc-relay.log"; depends_on = { "composable-picasso-ibc-channels-init".condition = "process_completed_successfully"; @@ -233,18 +239,24 @@ settings = { processes = { centauri = { - command = self'.packages.centaurid-gen; + command = pkgs.writeShellApplication { + runtimeInputs = devnetTools.withBaseContainerTools; + name = "centauri"; + text = '' + ${pkgs.lib.meta.getExe self'.packages.centaurid-gen} reuse 0 + ''; + }; readiness_probe.http_get = { host = "127.0.0.1"; port = 26657; }; - log_location = "/tmp/composable-devnet/centauri.log"; + log_location = "${devnet-root-directory}/centauri.log"; availability = { restart = "on_failure"; }; }; centauri-init = { command = self'.packages.centaurid-init; depends_on."centauri".condition = "process_healthy"; - log_location = "/tmp/composable-devnet/centauri-init.log"; + log_location = "${devnet-root-directory}/centauri-init.log"; availability = { restart = "on_failure"; }; }; @@ -254,19 +266,20 @@ host = "127.0.0.1"; port = 36657; }; - log_location = "/tmp/composable-devnet/osmosis.log"; + log_location = "${devnet-root-directory}/osmosis.log"; }; - osmosis-init = { - command = self'.packages.osmosisd-init; + osmosisd-xcvm-init = { + command = self'.packages.osmosisd-xcvm-init; depends_on."osmosis".condition = "process_healthy"; - log_location = "/tmp/composable-devnet/osmosis-init.log"; + log_location = + "${devnet-root-directory}/osmosisd-xcvm-init.log"; availability = { restart = "on_failure"; }; }; picasso = { command = self'.packages.zombienet-rococo-local-picasso-dev; availability = { restart = "on_failure"; }; - log_location = "/tmp/composable-devnet/picasso.log"; + log_location = "${devnet-root-directory}/picasso.log"; readiness_probe = { initial_delay_seconds = 32; period_seconds = 8; @@ -280,7 +293,7 @@ composable = { command = self'.packages.zombienet-composable-centauri-b; availability = { restart = "on_failure"; }; - log_location = "/tmp/composable-devnet/composable.log"; + log_location = "${devnet-root-directory}/composable.log"; readiness_probe = { initial_delay_seconds = 32; period_seconds = 8; @@ -300,7 +313,7 @@ "osmosis".condition = "process_healthy"; }; log_location = - "/tmp/composable-devnet/osmosis-centauri-hermes-init.log"; + "${devnet-root-directory}/osmosis-centauri-hermes-init.log"; availability = { restart = "on_failure"; }; }; @@ -311,14 +324,14 @@ "process_completed_successfully"; }; log_location = - "/tmp/composable-devnet/osmosis-centauri-hermes-relay.log"; + "${devnet-root-directory}/osmosis-centauri-hermes-relay.log"; availability = { restart = relay; }; }; picasso-centauri-ibc-init = { command = self'.packages.picasso-centauri-ibc-init; log_location = - "/tmp/composable-devnet/picasso-centauri-ibc-init.log"; + "${devnet-root-directory}/picasso-centauri-ibc-init.log"; depends_on = { "centauri-init".condition = "process_completed_successfully"; "centauri".condition = "process_healthy"; @@ -330,7 +343,7 @@ picasso-centauri-ibc-connection-init = { command = self'.packages.picasso-centauri-ibc-connection-init; log_location = - "/tmp/composable-devnet/picasso-centauri-ibc-connection-init.log"; + "${devnet-root-directory}/picasso-centauri-ibc-connection-init.log"; depends_on = { "picasso-centauri-ibc-init".condition = "process_completed_successfully"; @@ -341,7 +354,7 @@ picasso-centauri-ibc-channels-init = { command = self'.packages.picasso-centauri-ibc-channels-init; log_location = - "/tmp/composable-devnet/picasso-centauri-ibc-channels-init.log"; + "${devnet-root-directory}/picasso-centauri-ibc-channels-init.log"; depends_on = { "picasso-centauri-ibc-connection-init".condition = "process_completed_successfully"; @@ -352,7 +365,7 @@ picasso-centauri-ibc-relay = { command = self'.packages.picasso-centauri-ibc-relay; log_location = - "/tmp/composable-devnet/picasso-centauri-ibc-relay.log"; + "${devnet-root-directory}/picasso-centauri-ibc-relay.log"; depends_on = { "picasso-centauri-ibc-channels-init".condition = "process_completed_successfully"; @@ -363,7 +376,7 @@ composable-picasso-ibc-init = { command = self'.packages.composable-picasso-ibc-init; log_location = - "/tmp/composable-devnet/composable-picasso-ibc-init.log"; + "${devnet-root-directory}/composable-picasso-ibc-init.log"; depends_on = { "picasso-centauri-ibc-channels-init".condition = "process_completed_successfully"; @@ -374,14 +387,14 @@ }; composable-picasso-ibc-connection-init = { command = '' - HOME="/tmp/composable-devnet/composable-picasso-ibc" + HOME="${devnet-root-directory}/composable-picasso-ibc" export HOME RUST_LOG="hyperspace=info,hyperspace_parachain=debug,hyperspace_cosmos=debug" export RUST_LOG - ${self'.packages.hyperspace-composable-rococo-picasso-rococo}/bin/hyperspace create-connection --config-a /tmp/composable-devnet/composable-picasso-ibc/config-chain-a.toml --config-b /tmp/composable-devnet/composable-picasso-ibc/config-chain-b.toml --config-core /tmp/composable-devnet/composable-picasso-ibc/config-core.toml --delay-period 10 + ${self'.packages.hyperspace-composable-rococo-picasso-rococo}/bin/hyperspace create-connection --config-a ${devnet-root-directory}/composable-picasso-ibc/config-chain-a.toml --config-b ${devnet-root-directory}/composable-picasso-ibc/config-chain-b.toml --config-core ${devnet-root-directory}/composable-picasso-ibc/config-core.toml --delay-period 10 ''; log_location = - "/tmp/composable-devnet/composable-picasso-ibc-connection-init.log"; + "${devnet-root-directory}/composable-picasso-ibc-connection-init.log"; depends_on = { "composable-picasso-ibc-init".condition = "process_completed_successfully"; @@ -391,14 +404,14 @@ composable-picasso-ibc-channels-init = { command = '' - HOME="/tmp/composable-devnet/composable-picasso-ibc" + HOME="${devnet-root-directory}/composable-picasso-ibc" export HOME RUST_LOG="hyperspace=info,hyperspace_parachain=debug,hyperspace_cosmos=debug" export RUST_LOG - ${self'.packages.hyperspace-composable-rococo-picasso-rococo}/bin/hyperspace create-channel --config-a /tmp/composable-devnet/composable-picasso-ibc/config-chain-a.toml --config-b /tmp/composable-devnet/composable-picasso-ibc/config-chain-b.toml --config-core /tmp/composable-devnet/composable-picasso-ibc/config-core.toml --delay-period 10 --port-id transfer --version ics20-1 --order unordered + ${self'.packages.hyperspace-composable-rococo-picasso-rococo}/bin/hyperspace create-channel --config-a ${devnet-root-directory}/composable-picasso-ibc/config-chain-a.toml --config-b ${devnet-root-directory}/composable-picasso-ibc/config-chain-b.toml --config-core ${devnet-root-directory}/composable-picasso-ibc/config-core.toml --delay-period 10 --port-id transfer --version ics20-1 --order unordered ''; log_location = - "/tmp/composable-devnet/composable-picasso-ibc-channels-init.log"; + "${devnet-root-directory}/composable-picasso-ibc-channels-init.log"; depends_on = { "composable-picasso-ibc-connection-init".condition = "process_completed_successfully"; @@ -408,7 +421,7 @@ composable-picasso-ibc-relay = { command = self'.packages.composable-picasso-ibc-relay; log_location = - "/tmp/composable-devnet/composable-picasso-ibc-relay.log"; + "${devnet-root-directory}/composable-picasso-ibc-relay.log"; depends_on = { "composable-picasso-ibc-channels-init".condition = "process_completed_successfully"; @@ -429,20 +442,21 @@ host = "127.0.0.1"; port = 26657; }; - log_location = "/tmp/composable-devnet/centauri.log"; + log_location = "${devnet-root-directory}/centauri.log"; availability = { restart = "on_failure"; }; }; centauri-init = { command = self'.packages.centaurid-init; depends_on."centauri".condition = "process_healthy"; - log_location = "/tmp/composable-devnet/centauri-init.log"; + log_location = "${devnet-root-directory}/centauri-init.log"; 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"; + log_location = + "${devnet-root-directory}/centauri-xcvm-init.log"; availability = { restart = "on_failure"; }; }; @@ -450,8 +464,21 @@ command = self'.packages.centaurid-xcvm-config; depends_on."centauri-xcvm-init".condition = "process_completed_successfully"; + depends_on."osmosis-xcvm-init".condition = + "process_completed_successfully"; + log_location = + "${devnet-root-directory}/centauri-xcvm-config.log"; + availability = { restart = "on_failure"; }; + }; + + osmosis-xcvm-config = { + command = self'.packages.osmosisd-xcvm-config; + depends_on."centauri-xcvm-init".condition = + "process_completed_successfully"; + depends_on."osmosis-xcvm-init".condition = + "process_completed_successfully"; log_location = - "/tmp/composable-devnet/centauri-xcvm-config.log"; + "${devnet-root-directory}/osmosis-xcvm-config.log"; availability = { restart = "on_failure"; }; }; @@ -461,12 +488,12 @@ host = "127.0.0.1"; port = 36657; }; - log_location = "/tmp/composable-devnet/osmosis.log"; + log_location = "${devnet-root-directory}/osmosis.log"; }; - osmosis-init = { - command = self'.packages.osmosisd-init; + osmosis-xcvm-init = { + command = self'.packages.osmosisd-xcvm-init; depends_on."osmosis".condition = "process_healthy"; - log_location = "/tmp/composable-devnet/osmosis-init.log"; + log_location = "${devnet-root-directory}/osmosis-xcvm-init.log"; availability = { restart = "on_failure"; }; }; @@ -477,7 +504,7 @@ "osmosis".condition = "process_healthy"; }; log_location = - "/tmp/composable-devnet/osmosis-centauri-hermes-init.log"; + "${devnet-root-directory}/osmosis-centauri-hermes-init.log"; availability = { restart = "on_failure"; }; }; @@ -488,7 +515,7 @@ "process_completed_successfully"; }; log_location = - "/tmp/composable-devnet/osmosis-centauri-hermes-relay.log"; + "${devnet-root-directory}/osmosis-centauri-hermes-relay.log"; availability = { restart = relay; }; }; }; diff --git a/inputs/notional-labs/composable-centauri/flake-module.nix b/inputs/notional-labs/composable-centauri/flake-module.nix index cd2ce27cc0e..622de7ccc8b 100644 --- a/inputs/notional-labs/composable-centauri/flake-module.nix +++ b/inputs/notional-labs/composable-centauri/flake-module.nix @@ -103,7 +103,7 @@ 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 "$KEY" --keyring-dir "$KEYRING_TEST" --admin "$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" --amount 1000000000000$FEE 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) @@ -114,7 +114,7 @@ INSTANTIATE=$(cat << EOF { "admin" : "$KEY", - "here_id" : $NETWORK_ID + "network_id" : $NETWORK_ID } EOF ) @@ -129,17 +129,22 @@ ++ [ centaurid pkgs.jq self'.packages.xc-cw-contracts ]; text = '' - CHAIN_DATA="${devnet-root-directory}/.centaurid" + HOME=${devnet-root-directory} + export HOME + KEY=${cosmosTools.xcvm.centauri} + + CHAIN_DATA="$HOME/.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) + OSMOSIS_GATEWAY_CONTRACT_ADDRESS=$(cat "$HOME/.osmosisd/gateway_contract_address") FORCE_NETWORK_OSMOSIS=$(cat << EOF { @@ -151,7 +156,7 @@ }, "gateway": { "cosm_wasm": { - "contract": "$GATEWAY_CONTRACT_ADDRESS", + "contract": "$OSMOSIS_GATEWAY_CONTRACT_ADDRESS", "interpreter_code_id": $INTERPRETER_CODE_ID, "admin": "$KEY" } @@ -222,7 +227,7 @@ "to": 3, "other": { "counterparty_timeout": { - "timestamp": "60" + "seconds" : 120 }, "ics_20": { "source" : "channel-0", @@ -243,7 +248,7 @@ "config": { "force_asset": { "asset_id": "158456325028528675187087900673", - "from_network_id": 2, + "network_id": 2, "local": { "native": { "denom": "ppica" @@ -271,7 +276,7 @@ "config": { "force_asset": { "asset_id": "158456325028528675187087900674", - "from_network_id": 2, + "network_id": 2, "local": { "native": { "denom": "uatom" @@ -299,7 +304,7 @@ "config": { "force_asset": { "asset_id": "237684487542793012780631851009", - "from_network_id": 3, + "network_id": 3, "local": { "native": { "denom": "ppica" @@ -342,7 +347,7 @@ "config": { "force_asset": { "asset_id": "237684487542793012780631851010", - "from_network_id": 2, + "network_id": 2, "local": { "native": { "denom" : "uatom" @@ -375,53 +380,74 @@ TRANSFER_PICA_TO_OSMOSIS=$(cat << EOF { - "execute_program": { "execute_program": { - "salt": "737061776e5f776974685f6173736574", - "program": { - "tag": "737061776e5f776974685f6173736574", - "instructions": [ - { - "spawn": { - "network": 3, - "salt": "737061776e5f776974685f6173736574", - "assets": [ - [ - "158456325028528675187087900673", - { - "amount": { - "intercept": "1000000000", - "slope": "0" + "execute_program": { + "salt": "737061776e5f776974685f6173736574", + "program": { + "tag": "737061776e5f776974685f6173736574", + "instructions": [ + { + "spawn": { + "network": 3, + "salt": "737061776e5f776974685f6173736574", + "assets": [ + [ + "158456325028528675187087900673", + { + "amount": { + "intercept": "1234567890", + "slope": "0" + }, + "is_unit": false + } + ] + ], + "program": { + "tag": "737061776e5f776974685f6173736574", + "instructions": [ + { + "transfer": { + "to": { + "account": "AB9vNpqXOevUvR5+JDnlljDbHhw=" + }, + "assets": [ + [ + "237684487542793012780631851009", + { + "amount": { + + "intercept": "123456789", + "slope": "0" + }, + "is_unit": false + } + ] + ] + } + } + ] + } + } + } + ] }, - "is_unit": false - } - ] - ], - "program": { - "tag": "737061776e5f776974685f6173736574", - "instructions": [] - } - } - } - ] - }, - "assets": [ - [ - "158456325028528675187087900673", - "1000000000" - ] - ] - }, - "tip": "centauri12smx2wdlyttvyzvzg54y2vnqwq2qjatescq89n" - } - } + "assets": [ + [ + "158456325028528675187087900673", + "1234567890" + ] + ] + }, + "tip": "centauri12smx2wdlyttvyzvzg54y2vnqwq2qjatescq89n" + } + } EOF ) # check route "$BINARY" query wasm contract-state smart "$GATEWAY_CONTRACT_ADDRESS" '{ "get_ibc_ics20_route" : { "for_asset" : "158456325028528675187087900673", "to_network": 3 } }' --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --home "$CHAIN_DATA" - "$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 + "$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 1234567890"$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" ''; }; @@ -447,6 +473,8 @@ echo "removing data dir" rm --force --recursive "$CHAIN_DATA" fi + PICA_CHANNEL_ID=''${2-1} + if [[ ! -d "$CHAIN_DATA" ]]; then mkdir --parents "$CHAIN_DATA" mkdir --parents "$CHAIN_DATA/config/gentx" @@ -462,10 +490,13 @@ 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"' + function pica_setup() { + jq-genesis '.app_state.transmiddleware.token_infos[0].ibc_denom |= "ibc/632DBFDB06584976F1351A66E873BF0F7A19FAA083425FEC9890C90993E5F0A4"' + jq-genesis ".app_state.transmiddleware.token_infos[0].channel_id |= \"channel-$PICA_CHANNEL_ID\"" + jq-genesis '.app_state.transmiddleware.token_infos[0].native_denom |= "ppica"' + jq-genesis '.app_state.transmiddleware.token_infos[0].asset_id |= "1"' + } + pica_setup 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" @@ -530,6 +561,21 @@ "$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 ''; }; + centauri-tx = pkgs.writeShellApplication { + name = "centaurid-xcvm-config"; + runtimeInputs = devnetTools.withBaseContainerTools + ++ [ centaurid pkgs.jq ]; + + text = '' + CHAIN_DATA="${devnet-root-directory}/.centaurid" + CHAIN_ID="centauri-dev" + KEYRING_TEST="$CHAIN_DATA/keyring-test" + PORT=26657 + FEE=ppica + BINARY=centaurid + "$BINARY" tx ibc-transfer transfer transfer channel-0 osmo1x99pkz8mk7msmptegg887wy46vrusl7kk0sudvaf2uh2k8qz7spsyy4mg8 9876543210ppica --memo '{ "wasm" : { "contract" : "osmo1x99pkz8mk7msmptegg887wy46vrusl7kk0sudvaf2uh2k8qz7spsyy4mg8" } }' --chain-id="$CHAIN_ID" --node "tcp://localhost:$PORT" --output json --yes --gas 25000000 --fees 920000166"$FEE" --log_level trace --keyring-backend test --home "$CHAIN_DATA" --from ${cosmosTools.xcvm.moniker} --keyring-dir "$KEYRING_TEST" --trace --log_level trace + ''; + }; }; }; }