diff --git a/solo-machine-core/src/event.rs b/solo-machine-core/src/event.rs index 56e5bfd..5b5944b 100644 --- a/solo-machine-core/src/event.rs +++ b/solo-machine-core/src/event.rs @@ -95,6 +95,13 @@ pub enum Event { /// Channel ID of solo machine client on IBC enabled chain channel_id: ChannelId, }, + /// Close channel on IBC enabled chain + CloseChannelInitOnSoloMachine { + /// Chain ID of IBC enabled chain + chain_id: String, + /// Channel ID of IBC enabled chain on solo machine + channel_id: ChannelId, + }, /// Initialized channel on solo machine InitializedChannelOnSoloMachine { /// Channel ID of IBC enabled chain on solo machine diff --git a/solo-machine-core/src/ibc/core/ics04_channel.rs b/solo-machine-core/src/ibc/core/ics04_channel.rs index 28061fe..cb7ee4b 100644 --- a/solo-machine-core/src/ibc/core/ics04_channel.rs +++ b/solo-machine-core/src/ibc/core/ics04_channel.rs @@ -1,4 +1,5 @@ pub mod msg_acknowledgement; +pub mod msg_channel_close_init; pub mod msg_channel_open_ack; pub mod msg_channel_open_init; pub mod msg_recv_packet; diff --git a/solo-machine-core/src/ibc/core/ics04_channel/msg_channel_close_init.rs b/solo-machine-core/src/ibc/core/ics04_channel/msg_channel_close_init.rs new file mode 100644 index 0000000..cec2c36 --- /dev/null +++ b/solo-machine-core/src/ibc/core/ics04_channel/msg_channel_close_init.rs @@ -0,0 +1,5 @@ +use cosmos_sdk_proto::ibc::core::channel::v1::MsgChannelCloseInit; + +const TYPE_URL: &str = "/ibc.core.channel.v1.MsgChannelCloseInit"; + +impl_any_conversion!(MsgChannelCloseInit, TYPE_URL); diff --git a/solo-machine-core/src/model/chain/chain.rs b/solo-machine-core/src/model/chain/chain.rs index 80fdd23..8bac168 100644 --- a/solo-machine-core/src/model/chain/chain.rs +++ b/solo-machine-core/src/model/chain/chain.rs @@ -49,18 +49,27 @@ pub struct Chain { impl Chain { /// Returns the IBC denom of given denomination based on connection details. Returns `None` if connection details /// are not present. - pub fn get_ibc_denom(&self, denom: &Identifier) -> Option { - let connection_details = self.connection_details.as_ref()?; + pub fn get_ibc_denom(&self, denom: &Identifier) -> Result { + let connection_details = self.connection_details.as_ref(); + ensure!( + connection_details.is_some(), + "connection is not established with given chain" + ); + let connection_details = connection_details.unwrap(); + ensure!( + connection_details.solo_machine_channel_id.is_some(), + "can't find solo machine channel, channel is already closed" + ); let denom_trace = DenomTrace::new( &self.config.port_id, - &connection_details.solo_machine_channel_id, + connection_details.solo_machine_channel_id.as_ref().unwrap(), denom, ); let hash = Sha256::digest(denom_trace.to_string().as_bytes()); - Some(format!("ibc/{}", hex::encode_upper(hash))) + Ok(format!("ibc/{}", hex::encode_upper(hash))) } /// Fetches on-chain balance of given denom @@ -76,9 +85,7 @@ impl Chain { self.config.grpc_addr ))?; - let denom = self - .get_ibc_denom(denom) - .ok_or_else(|| anyhow!("connection details not found when fetching balance"))?; + let denom = self.get_ibc_denom(denom)?; let request = QueryBalanceRequest { address: signer.to_account_address()?, @@ -170,9 +177,9 @@ pub struct ConnectionDetails { /// Connection ID of IBC enabled chain on solo machine pub tendermint_connection_id: ConnectionId, /// Channel ID of solo machine client on IBC enabled chain - pub solo_machine_channel_id: ChannelId, + pub solo_machine_channel_id: Option, /// Channel ID of IBC enabled chain on solo machine - pub tendermint_channel_id: ChannelId, + pub tendermint_channel_id: Option, } impl From for RawChain { diff --git a/solo-machine-core/src/service/chain_service.rs b/solo-machine-core/src/service/chain_service.rs index e3c9ee0..cda068c 100644 --- a/solo-machine-core/src/service/chain_service.rs +++ b/solo-machine-core/src/service/chain_service.rs @@ -76,9 +76,7 @@ impl ChainService { .get(chain_id) .await? .ok_or_else(|| anyhow!("chain details not found when computing ibc denom"))?; - chain - .get_ibc_denom(denom) - .ok_or_else(|| anyhow!("connection details not found when computing ibc denom")) + chain.get_ibc_denom(denom) } /// Fetches details of a chain diff --git a/solo-machine-core/src/service/ibc_service.rs b/solo-machine-core/src/service/ibc_service.rs index f429cd2..9a49697 100644 --- a/solo-machine-core/src/service/ibc_service.rs +++ b/solo-machine-core/src/service/ibc_service.rs @@ -31,6 +31,8 @@ use tendermint_rpc::{ }; use tokio::sync::mpsc::UnboundedSender; +use crate::model::ConnectionDetails; +use crate::transaction_builder::msg_channel_close_init; use crate::{ cosmos::crypto::PublicKey, event::{notify_event, Event}, @@ -90,26 +92,119 @@ impl IbcService { .await? .ok_or_else(|| anyhow!("chain details for {} not found", chain_id))?; - if !force { - ensure!( - chain.connection_details.is_none(), - "connection is already established with given chain" - ); + let (already_established, channel_closed) = { + match chain.connection_details { + None => (false, true), + Some(ref detail) => { + let port_id = &chain.config.port_id; + match &detail.tendermint_channel_id { + Some(channel_id) => { + let channel = + ibc_handler::get_channel(&mut *transaction, port_id, channel_id) + .await? + .ok_or_else(|| { + anyhow!( + "channel for channel id ({}) and port id ({}) not found", + channel_id, + port_id + ) + })?; + (true, channel.state == ChannelState::Closed as i32) + } + None => (true, true), + } + } + } + }; + if already_established && !channel_closed && !force { + anyhow::bail!("connection is already established with given chain"); } + // if connection is not not established or connection is established, but need to force reconnect + // create new client and connection first let rpc_client = HttpClient::new(chain.config.rpc_addr.as_str()) .context("unable to connect to rpc client")?; + let (client_id, connection_id) = if !already_established { + let client_id = self + .create_new_client( + &signer, + memo.clone(), + request_id.as_deref(), + &chain, + rpc_client.clone(), + &mut transaction, + ) + .await?; + let connection_id = self + .create_connection( + &mut chain, + &signer, + &rpc_client, + &mut transaction, + &client_id.0, + &client_id.1, + memo.clone(), + request_id.as_deref(), + ) + .await?; + (client_id, connection_id) + } else { + if !channel_closed && !force { + anyhow::bail!("connection is already established with given chain"); + } + todo!("get solo_chaine_client_id and tendermint_client_id") + }; + let (solo_machine_channel_id, tendermint_channel_id) = self + .open_channel( + &signer, + memo, + request_id.as_deref(), + &mut chain, + &rpc_client, + &connection_id.0, + &connection_id.1, + &mut transaction, + ) + .await?; + let connection_details = ChainConnectionDetails { + solo_machine_client_id: client_id.0.clone(), + tendermint_client_id: client_id.1, + solo_machine_connection_id: connection_id.0.clone(), + tendermint_connection_id: connection_id.1, + solo_machine_channel_id: Some(solo_machine_channel_id), + tendermint_channel_id: Some(tendermint_channel_id), + }; + + chain::add_connection_details(&mut *transaction, &chain.id, &connection_details).await?; + + notify_event( + &self.notifier, + Event::ConnectionEstablished { + chain_id, + connection_details, + }, + )?; + + transaction + .commit() + .await + .context("unable to commit transaction for creating ibc connection") + } + + async fn create_new_client<'e>( + &self, + signer: impl Signer, + memo: String, + request_id: Option<&str>, + chain: &Chain, + rpc_client: HttpClient, + transaction: &mut Transaction<'_, Db>, + ) -> Result<(ClientId, ClientId)> { let mut instance = - prepare_light_client(&chain, rpc_client.clone(), Box::new(MemoryStore::new()))?; + prepare_light_client(chain, rpc_client.clone(), Box::new(MemoryStore::new()))?; - let solo_machine_client_id = create_solo_machine_client( - &signer, - &rpc_client, - &chain, - memo.clone(), - request_id.as_deref(), - ) - .await?; + let solo_machine_client_id = + create_solo_machine_client(signer, &rpc_client, chain, memo, request_id).await?; notify_event( &self.notifier, @@ -119,7 +214,7 @@ impl IbcService { )?; let tendermint_client_id = - create_tendermint_client(&mut transaction, &mut instance, &chain).await?; + create_tendermint_client(transaction, &mut instance, chain).await?; notify_event( &self.notifier, @@ -127,15 +222,29 @@ impl IbcService { client_id: tendermint_client_id.clone(), }, )?; + Ok((solo_machine_client_id, tendermint_client_id)) + } + #[allow(clippy::too_many_arguments)] + async fn create_connection<'e>( + &self, + chain: &mut Chain, + signer: impl Signer, + rpc_client: &HttpClient, + transaction: &mut Transaction<'_, Db>, + solo_machine_client_id: &ClientId, + tendermint_client_id: &ClientId, + memo: String, + request_id: Option<&str>, + ) -> Result<(ConnectionId, ConnectionId)> { let solo_machine_connection_id = connection_open_init( &signer, - &rpc_client, - &chain, - &solo_machine_client_id, - &tendermint_client_id, + rpc_client, + chain, + solo_machine_client_id, + tendermint_client_id, memo.clone(), - request_id.as_deref(), + request_id, ) .await?; @@ -147,9 +256,9 @@ impl IbcService { )?; let tendermint_connection_id = connection_open_try( - &mut transaction, - &tendermint_client_id, - &solo_machine_client_id, + &mut *transaction, + tendermint_client_id, + solo_machine_client_id, &solo_machine_connection_id, ) .await?; @@ -162,15 +271,15 @@ impl IbcService { )?; connection_open_ack( - &mut transaction, + &mut *transaction, &signer, - &rpc_client, - &mut chain, + rpc_client, + chain, &solo_machine_connection_id, - &tendermint_client_id, + tendermint_client_id, &tendermint_connection_id, memo.clone(), - request_id.as_deref(), + request_id, ) .await?; @@ -181,7 +290,7 @@ impl IbcService { }, )?; - connection_open_confirm(&mut transaction, &tendermint_connection_id).await?; + connection_open_confirm(&mut *transaction, &tendermint_connection_id).await?; notify_event( &self.notifier, @@ -189,14 +298,28 @@ impl IbcService { connection_id: tendermint_connection_id.clone(), }, )?; + Ok((solo_machine_connection_id, tendermint_connection_id)) + } + #[allow(clippy::too_many_arguments)] + async fn open_channel<'e>( + &self, + signer: &impl Signer, + memo: String, + request_id: Option<&str>, + chain: &mut Chain, + rpc_client: &HttpClient, + solo_machine_connection_id: &ConnectionId, + tendermint_connection_id: &ConnectionId, + transaction: &mut Transaction<'_, Db>, + ) -> Result<(ChannelId, ChannelId)> { let solo_machine_channel_id = channel_open_init( - &signer, - &rpc_client, - &chain, - &solo_machine_connection_id, + signer, + rpc_client, + chain, + solo_machine_connection_id, memo.clone(), - request_id.as_deref(), + request_id, ) .await?; @@ -208,10 +331,10 @@ impl IbcService { )?; let tendermint_channel_id = channel_open_try( - &mut transaction, + &mut *transaction, &chain.config.port_id, &solo_machine_channel_id, - &tendermint_connection_id, + tendermint_connection_id, ) .await?; @@ -223,14 +346,14 @@ impl IbcService { )?; channel_open_ack( - &mut transaction, + &mut *transaction, signer, - &rpc_client, - &mut chain, + rpc_client, + chain, &solo_machine_channel_id, &tendermint_channel_id, memo, - request_id.as_deref(), + request_id, ) .await?; @@ -241,12 +364,7 @@ impl IbcService { }, )?; - channel_open_confirm( - &mut transaction, - &chain.config.port_id, - &tendermint_channel_id, - ) - .await?; + channel_open_confirm(transaction, &chain.config.port_id, &tendermint_channel_id).await?; notify_event( &self.notifier, @@ -254,30 +372,81 @@ impl IbcService { channel_id: tendermint_channel_id.clone(), }, )?; + Ok((solo_machine_channel_id, tendermint_channel_id)) + } - let connection_details = ChainConnectionDetails { - solo_machine_client_id, - tendermint_client_id, - solo_machine_connection_id, - tendermint_connection_id, - solo_machine_channel_id, + /// close the solomachine channel + pub async fn close_channel( + &self, + signer: impl Signer, + chain_id: &ChainId, + request_id: Option, + memo: String, + ) -> Result<()> { + let mut transaction = self + .db_pool + .begin() + .await + .context("unable to begin database transaction")?; + let chain = chain::get_chain(&mut transaction, chain_id) + .await? + .ok_or_else(|| anyhow!("chain details for {} not found", chain_id))?; + ensure!( + chain.connection_details.is_some(), + "chain connection details is empty" + ); + let connection_details = chain.connection_details.as_ref().unwrap(); + ensure!( + connection_details.solo_machine_channel_id.is_some(), + "can't find solo machine channel, channel is already closed", + ); + let mut new_connection_details = connection_details.clone(); + new_connection_details.solo_machine_channel_id = None; + new_connection_details.tendermint_channel_id = None; + + let solo_machine_channel_id = connection_details.solo_machine_channel_id.clone().unwrap(); + let port_id = &chain.config.port_id; + let tendermint_channel_id = connection_details.tendermint_channel_id.as_ref().unwrap(); + close_channel_confirm( + &mut transaction, + port_id, tendermint_channel_id, - }; - - chain::add_connection_details(&mut transaction, &chain.id, &connection_details).await?; - + &chain.id, + &new_connection_details, + ) + .await?; + let rpc_client = HttpClient::new(chain.config.rpc_addr.as_str()) + .context("unable to connect to rpc client")?; + let closed_solo_machine_channel_id = channel_close_init( + &mut transaction, + &rpc_client, + &signer, + chain_id.clone(), + request_id.as_deref(), + memo.clone(), + ) + .await?; + ensure!( + solo_machine_channel_id.to_string() == closed_solo_machine_channel_id, + format!( + "closed wrong solo machine channel, expect {}, get {}", + solo_machine_channel_id, closed_solo_machine_channel_id + ), + ); notify_event( &self.notifier, - Event::ConnectionEstablished { - chain_id, - connection_details, + Event::CloseChannelInitOnSoloMachine { + chain_id: chain_id.to_string(), + channel_id: solo_machine_channel_id, }, )?; transaction .commit() .await - .context("unable to commit transaction for creating ibc connection") + .context("unable to commit transaction for creating ibc connection")?; + + Ok(()) } /// Mint some tokens on IBC enabled chain @@ -578,6 +747,16 @@ impl IbcService { chain.id ) })?; + ensure!( + connection_details.tendermint_channel_id.is_some(), + "can't find tendermint channel, channel is already closed" + ); + ensure!( + connection_details.solo_machine_channel_id.is_some(), + "can't find solo machine channel, channel is already closed" + ); + let solo_machine_channel_id = connection_details.solo_machine_channel_id.as_ref().unwrap(); + let tendermint_channel_id = connection_details.tendermint_channel_id.as_ref().unwrap(); for packet in packets { ensure!( @@ -585,7 +764,7 @@ impl IbcService { "invalid source port id" ); ensure!( - connection_details.solo_machine_channel_id.to_string() == packet.source_channel, + solo_machine_channel_id.to_string() == packet.source_channel, "invalid source channel id" ); ensure!( @@ -593,7 +772,7 @@ impl IbcService { "invalid destination port id" ); ensure!( - connection_details.tendermint_channel_id.to_string() == packet.destination_channel, + tendermint_channel_id.to_string() == packet.destination_channel, "invalid destination channel id" ); @@ -651,7 +830,7 @@ where extract_attribute(&response.deliver_tx.events, "create_client", "client_id")?.parse() } -async fn create_tendermint_client( +async fn create_tendermint_client<'e>( transaction: &mut Transaction<'_, Db>, instance: &mut Instance, chain: &Chain, @@ -744,20 +923,17 @@ async fn connection_open_try<'e>( } #[allow(clippy::too_many_arguments)] -async fn connection_open_ack( +async fn connection_open_ack( transaction: &mut Transaction<'_, Db>, signer: impl Signer, - rpc_client: &C, + rpc_client: &HttpClient, chain: &mut Chain, solo_machine_connection_id: &ConnectionId, tendermint_client_id: &ClientId, tendermint_connection_id: &ConnectionId, memo: String, request_id: Option<&str>, -) -> Result<()> -where - C: Client + Send + Sync, -{ +) -> Result<()> { let msg = transaction_builder::msg_connection_open_ack( transaction, signer, @@ -899,7 +1075,68 @@ async fn channel_open_confirm( })?; channel.set_state(ChannelState::Open); - ibc_handler::update_channel(&mut *transaction, port_id, channel_id, &channel).await + ibc_handler::update_channel(transaction, port_id, channel_id, &channel).await +} + +/// Close solomachine channel, return the solo-machine channel id +pub async fn channel_close_init( + transaction: &mut Transaction<'_, Db>, + rpc_client: &C, + signer: impl Signer, + chain_id: ChainId, + request_id: Option<&str>, + memo: String, +) -> Result +where + C: Client + Send + Sync, +{ + let chain = chain::get_chain(transaction, &chain_id) + .await? + .ok_or_else(|| anyhow!("chain details for {} not found", chain_id))?; + ensure!( + chain.connection_details.is_some(), + "connection is not established with given chain" + ); + + let msg = msg_channel_close_init(signer, &chain, memo, request_id).await?; + + let response = rpc_client + .broadcast_tx_commit(proto_encode(&msg)?.into()) + .await?; + + ensure_response_success(&response)?; + + let channel_id: String = extract_attribute( + &response.deliver_tx.events, + "channel_close_init", + "channel_id", + )? + .parse()?; + Ok(channel_id) +} + +async fn close_channel_confirm( + transaction: &mut Transaction<'_, Db>, + port_id: &PortId, + channel_id: &ChannelId, + chain_id: &ChainId, + new_connection_details: &ConnectionDetails, +) -> Result<()> { + // set the channel status to close + let mut channel = ibc_handler::get_channel(&mut *transaction, port_id, channel_id) + .await? + .ok_or_else(|| { + anyhow!( + "channel for channel id ({}) and port id ({}) not found", + channel_id, + port_id + ) + })?; + channel.set_state(ChannelState::Closed); + ibc_handler::update_channel(&mut *transaction, port_id, channel_id, &channel).await?; + + chain::add_connection_details(&mut *transaction, chain_id, new_connection_details).await?; + Ok(()) } fn prepare_light_client( diff --git a/solo-machine-core/src/transaction_builder.rs b/solo-machine-core/src/transaction_builder.rs index bde691f..4cfa884 100644 --- a/solo-machine-core/src/transaction_builder.rs +++ b/solo-machine-core/src/transaction_builder.rs @@ -40,8 +40,8 @@ use cosmos_sdk_proto::{ core::{ channel::v1::{ Channel, Counterparty as ChannelCounterparty, MsgAcknowledgement, - MsgChannelOpenAck, MsgChannelOpenInit, MsgRecvPacket, Order as ChannelOrder, - Packet, State as ChannelState, + MsgChannelCloseInit, MsgChannelOpenAck, MsgChannelOpenInit, MsgRecvPacket, + Order as ChannelOrder, Packet, State as ChannelState, }, client::v1::{Height, MsgCreateClient, MsgUpdateClient}, commitment::v1::MerklePrefix, @@ -338,6 +338,31 @@ pub async fn msg_channel_open_init( build(signer, chain, &[message], memo, request_id).await } +pub async fn msg_channel_close_init( + signer: impl Signer, + chain: &Chain, + memo: String, + request_id: Option<&str>, +) -> Result { + let port_id = chain.config.port_id.to_string(); + let solo_machine_channel_id = chain + .connection_details + .clone() + .unwrap() + .solo_machine_channel_id + .as_ref() + .map(|s| s.to_string()); + if solo_machine_channel_id.is_none() { + bail!("channel already closed"); + } + let message = MsgChannelCloseInit { + port_id, + channel_id: solo_machine_channel_id.unwrap(), + signer: signer.to_account_address()?, + }; + build(signer, chain, &[message], memo, request_id).await +} + pub async fn msg_channel_open_ack( transaction: &mut Transaction<'_, Db>, signer: impl Signer, @@ -393,6 +418,14 @@ where chain.id ) })?; + ensure!( + connection_details.solo_machine_channel_id.is_some(), + "can't find solo machine channel, channel is already closed" + ); + ensure!( + connection_details.tendermint_channel_id.is_some(), + "can't find tendermint channel, channel is already closed" + ); let sender = signer.to_account_address()?; @@ -406,9 +439,17 @@ where let packet = Packet { sequence: chain.packet_sequence.into(), source_port: chain.config.port_id.to_string(), - source_channel: connection_details.tendermint_channel_id.to_string(), + source_channel: connection_details + .tendermint_channel_id + .as_ref() + .unwrap() + .to_string(), destination_port: chain.config.port_id.to_string(), - destination_channel: connection_details.solo_machine_channel_id.to_string(), + destination_channel: connection_details + .solo_machine_channel_id + .as_ref() + .unwrap() + .to_string(), data: serde_json::to_vec(&packet_data)?, timeout_height: Some( get_latest_height(chain, rpc_client) @@ -451,19 +492,22 @@ pub async fn msg_token_receive( chain.id ) })?; + ensure!( + connection_details.solo_machine_channel_id.is_some(), + "can't find solo machine channel, channel is ready closed" + ); - let denom = chain.get_ibc_denom(denom).ok_or_else(|| { - anyhow!( - "connection is not established with chain with id {}", - chain.id - ) - })?; + let denom = chain.get_ibc_denom(denom)?; let sender = signer.to_account_address()?; let message = MsgTransfer { source_port: chain.config.port_id.to_string(), - source_channel: connection_details.solo_machine_channel_id.to_string(), + source_channel: connection_details + .solo_machine_channel_id + .as_ref() + .unwrap() + .to_string(), token: Some(Coin { amount: amount.to_string(), denom, @@ -714,18 +758,19 @@ async fn get_packet_acknowledgement_proof( packet_sequence: u64, request_id: Option<&str>, ) -> Result> { + let connection_details = chain.connection_details.as_ref().ok_or_else(|| { + anyhow!( + "connection details for chain with id {} not found", + chain.id + ) + })?; + ensure!( + connection_details.tendermint_channel_id.is_some(), + "can't find tendermint channel, channel is already closed" + ); let mut acknowledgement_path = PacketAcknowledgementPath::new( &chain.config.port_id, - &chain - .connection_details - .as_ref() - .ok_or_else(|| { - anyhow!( - "connection details for chain with id {} not found", - chain.id - ) - })? - .tendermint_channel_id, + connection_details.tendermint_channel_id.as_ref().unwrap(), packet_sequence, ); acknowledgement_path.apply_prefix(&"ibc".parse().unwrap()); @@ -756,18 +801,19 @@ async fn get_packet_commitment_proof( ) -> Result> { let commitment_bytes = packet.commitment_bytes()?; + let connection_details = chain.connection_details.as_ref().ok_or_else(|| { + anyhow!( + "connection details for chain with id {} not found", + chain.id + ) + })?; + ensure!( + connection_details.tendermint_channel_id.is_some(), + "can't find tendermint channel id, channel is already closed" + ); let mut commitment_path = PacketCommitmentPath::new( &chain.config.port_id, - &chain - .connection_details - .as_ref() - .ok_or_else(|| { - anyhow!( - "connection details for chain with id {} not found", - chain.id - ) - })? - .tendermint_channel_id, + connection_details.tendermint_channel_id.as_ref().unwrap(), chain.packet_sequence.into(), ); commitment_path.apply_prefix(&"ibc".parse().unwrap()); diff --git a/solo-machine/proto/chain.proto b/solo-machine/proto/chain.proto index 70f968f..7d32e50 100644 --- a/solo-machine/proto/chain.proto +++ b/solo-machine/proto/chain.proto @@ -123,7 +123,7 @@ message ConnectionDetails { // Connection ID of IBC enabled chain on solo machine string tendermint_connection_id = 4; // Channel ID of solo machine on IBC enabled chain - string solo_machine_channel_id = 5; + optional string solo_machine_channel_id = 5; // Channel ID of IBC enabled chain on solo machine - string tendermint_channel_id = 6; + optional string tendermint_channel_id = 6; } \ No newline at end of file diff --git a/solo-machine/src/command/chain.rs b/solo-machine/src/command/chain.rs index 6753347..190d1c7 100644 --- a/solo-machine/src/command/chain.rs +++ b/solo-machine/src/command/chain.rs @@ -280,7 +280,10 @@ impl ChainCommand { add_row( &mut table, "Solo machine client ID", - &connection_details.solo_machine_channel_id, + connection_details + .solo_machine_channel_id + .as_ref() + .unwrap(), ); add_row( &mut table, @@ -300,12 +303,15 @@ impl ChainCommand { add_row( &mut table, "Solo machine channel ID", - &connection_details.solo_machine_channel_id, + connection_details + .solo_machine_channel_id + .as_ref() + .unwrap(), ); add_row( &mut table, "Tendermint channel ID", - &connection_details.tendermint_channel_id, + connection_details.tendermint_channel_id.as_ref().unwrap(), ); } } diff --git a/solo-machine/src/command/ibc.rs b/solo-machine/src/command/ibc.rs index 7ed5d13..e48c7f5 100644 --- a/solo-machine/src/command/ibc.rs +++ b/solo-machine/src/command/ibc.rs @@ -41,6 +41,22 @@ pub enum IbcCommand { #[structopt(long)] force: bool, }, + /// Close solomachine channel + CloseChannel { + /// Chain ID of IBC enabled chain + chain_id: ChainId, + /// Optional memo to include in transactions + #[structopt( + long, + default_value = "solo-machine-memo", + env = "SOLO_MEMO", + hide_env_values = true + )] + memo: String, + /// Optional request ID (for tracking purposes) + #[structopt(long)] + request_id: Option, + }, /// Mint some tokens on IBC enabled chain Mint { /// Chain ID of IBC enabled chain @@ -136,6 +152,15 @@ impl IbcCommand { .connect(signer, chain_id, request_id, memo, force) .await } + Self::CloseChannel { + chain_id, + memo, + request_id, + } => { + ibc_service + .close_channel(signer, &chain_id, request_id, memo) + .await + } Self::Mint { chain_id, amount, diff --git a/solo-machine/src/event/cli_event_handler.rs b/solo-machine/src/event/cli_event_handler.rs index 6d93a5b..bfe47fe 100644 --- a/solo-machine/src/event/cli_event_handler.rs +++ b/solo-machine/src/event/cli_event_handler.rs @@ -53,6 +53,23 @@ impl CliEventHandler { print_stdout(table).context("unable to print table to stdout")?; } + Event::CloseChannelInitOnSoloMachine { + chain_id, + channel_id, + } => { + print_stream( + &mut stdout, + ColorSpec::new().set_bold(true), + "Chain channel init closed!", + )?; + writeln!(stdout)?; + let mut table = Vec::new(); + + add_row(&mut table, "Chain ID", chain_id); + add_row(&mut table, "Solo Machine Channel Id", channel_id); + print_stdout(table.table().color_choice(self.color_choice)) + .context("unable to print table to stdout")?; + } Event::TokensMinted { chain_id, request_id, @@ -267,12 +284,12 @@ impl CliEventHandler { add_row( &mut table, "Solo machine channel ID", - connection_details.solo_machine_channel_id, + connection_details.solo_machine_channel_id.as_ref().unwrap(), ); add_row( &mut table, "Tendermint channel ID", - connection_details.tendermint_channel_id, + connection_details.tendermint_channel_id.as_ref().unwrap(), ); print_stdout(table.table().color_choice(self.color_choice)) diff --git a/solo-machine/src/event/env_logger.rs b/solo-machine/src/event/env_logger.rs index 4b1e70f..5167100 100644 --- a/solo-machine/src/event/env_logger.rs +++ b/solo-machine/src/event/env_logger.rs @@ -75,6 +75,11 @@ impl EventHandler for EnvLogger { "Initialized connection on solo machine [Connection ID = {}]", connection_id ), + Event::CloseChannelInitOnSoloMachine { chain_id, channel_id } => log::info!( + "Close channel initialized on solo machine [Chain ID = {}] [Channel ID = {}]", + chain_id, + channel_id, + ), Event::ConfirmedConnectionOnTendermint { connection_id } => log::info!( "Confirmed connection on IBC enabled chain [Connection ID = {}]", connection_id diff --git a/solo-machine/src/server/chain.rs b/solo-machine/src/server/chain.rs index d74071f..4917de5 100644 --- a/solo-machine/src/server/chain.rs +++ b/solo-machine/src/server/chain.rs @@ -239,8 +239,12 @@ where tendermint_connection_id: connection_details .tendermint_connection_id .to_string(), - solo_machine_channel_id: connection_details.solo_machine_channel_id.to_string(), - tendermint_channel_id: connection_details.tendermint_channel_id.to_string(), + solo_machine_channel_id: connection_details + .solo_machine_channel_id + .map(|s| s.to_string()), + tendermint_channel_id: connection_details + .tendermint_channel_id + .map(|s| s.to_string()), } }), created_at: Some(SystemTime::from(chain.created_at).into()),