diff --git a/core/src/ibc/connection_03/manager.rs b/core/src/ibc/connection_03/manager.rs index d59c9066fb..bb94459549 100644 --- a/core/src/ibc/connection_03/manager.rs +++ b/core/src/ibc/connection_03/manager.rs @@ -22,7 +22,7 @@ use crate::ibc::connection_03::types::{ConnectionEnd, ConnectionIdentifiersInCli use crate::ibc::{Identifier, IdentifierSlice}; use ibc::client_02::Manager as ClientManager; use primitives::Bytes; -use rlp::{Encodable, Rlp}; +use rlp::Encodable; pub struct Manager<'a> { ctx: &'a mut dyn ibc::Context, @@ -221,16 +221,30 @@ impl<'a> Manager<'a> { Some(connection_end) } + fn query_client_connections(&self, identifier: &str) -> ConnectionIdentifiersInClient { + let kv_store = self.ctx.get_kv_store(); + let path = client_connections_path(identifier); + kv_store.get(&path).map(|bytes| rlp::decode(&bytes).expect("data from DB")).unwrap_or_default() + } + fn add_connection_to_client( &mut self, client_identifier: Identifier, connection_identifier: Identifier, ) -> Result<(), String> { let kv_store = self.ctx.get_kv_store_mut(); - let bytes = - kv_store.get(&client_connections_path(&client_identifier)).ok_or_else(|| "Connection exist".to_owned())?; - let rlp = Rlp::new(&bytes); - let mut conns: ConnectionIdentifiersInClient = rlp.as_val().expect("data from DB"); + if kv_store.contains_key(&connection_path(&connection_identifier)) { + return Err("Connection exist".to_owned()) + } + + let path = client_connections_path(&client_identifier); + let mut conns: ConnectionIdentifiersInClient = kv_store + .get(&path) + .map(|bytes| { + rlp::decode::(&bytes) + .expect("Only the connection code can save the code") + }) + .unwrap_or_default(); conns.add(connection_identifier); diff --git a/core/src/ibc/connection_03/mod.rs b/core/src/ibc/connection_03/mod.rs index b290ed5431..aa88131286 100644 --- a/core/src/ibc/connection_03/mod.rs +++ b/core/src/ibc/connection_03/mod.rs @@ -26,3 +26,6 @@ pub fn client_connections_path(client_id: &str) -> String { } pub use manager::Manager; +pub use types::ConnectionEnd; +pub use types::ConnectionIdentifiersInClient; +pub use types::ConnectionState; diff --git a/core/src/ibc/connection_03/types.rs b/core/src/ibc/connection_03/types.rs index c243099bc8..e5663d8506 100644 --- a/core/src/ibc/connection_03/types.rs +++ b/core/src/ibc/connection_03/types.rs @@ -55,13 +55,21 @@ pub struct ConnectionEnd { // FIXME: implement version } -#[derive(RlpEncodableWrapper, RlpDecodableWrapper, PartialEq, Debug)] +#[derive(RlpEncodableWrapper, RlpDecodableWrapper, PartialEq, Debug, Default)] pub struct ConnectionIdentifiersInClient(Vec); impl ConnectionIdentifiersInClient { + pub fn new() -> Self { + Self(Vec::new()) + } + pub fn add(&mut self, identifier: Identifier) { self.0.push(identifier); } + + pub fn into_vec(self) -> Vec { + self.0 + } } #[cfg(test)] diff --git a/core/src/ibc/querier/mod.rs b/core/src/ibc/querier/mod.rs index 9f3b093297..23d82de249 100644 --- a/core/src/ibc/querier/mod.rs +++ b/core/src/ibc/querier/mod.rs @@ -45,6 +45,12 @@ impl DebugName for ibc::client_02::types::ConsensusState { } } +impl DebugName for ibc::connection_03::types::ConnectionIdentifiersInClient { + fn debug_name() -> &'static str { + "ConnectionIdentifiersInClient" + } +} + /// Queries the path and returns the result in decoded struct pub fn query(ctx: &dyn ibc::Context, path: &CommitmentPath) -> Option where @@ -89,3 +95,9 @@ pub fn path_connection_end(id: IdentifierSlice) -> CommitmentPath { raw: ibc::connection_03::path(id), } } + +pub fn path_connection_identifiers(client_id: IdentifierSlice) -> CommitmentPath { + CommitmentPath { + raw: ibc::connection_03::client_connections_path(client_id), + } +} diff --git a/rpc/src/v1/impls/ibc.rs b/rpc/src/v1/impls/ibc.rs index 2639b22738..a54f0104a0 100644 --- a/rpc/src/v1/impls/ibc.rs +++ b/rpc/src/v1/impls/ibc.rs @@ -16,7 +16,7 @@ use super::super::errors; use super::super::traits::IBC; -use super::super::types::{ClientState, ConsensusState, IBCQuery}; +use super::super::types::{ClientState, ConnectionEnd, ConnectionIdentifiersInClient, ConsensusState, IBCQuery}; use ccore::ibc; use ccore::ibc::querier; use ccore::{BlockChainClient, BlockId, StateInfo}; @@ -126,4 +126,54 @@ where }; Ok(Some(rlp::encode(&header))) } + + fn query_connection( + &self, + identifier: String, + block_number: Option, + ) -> Result>> { + let block_id = block_number.map(BlockId::Number).unwrap_or(BlockId::Latest); + let mut state = self.client.state_at(block_id).ok_or_else(errors::state_not_exist)?; + let block_number = match self.client.block_number(&block_id) { + None => return Ok(None), + Some(block_number) => block_number, + }; + + let context = ibc::context::TopLevelContext::new(&mut state, block_number); + + let path = querier::path_connection_end(&identifier); + let connection_end: Option = querier::query(&context, &path); + + let response = IBCQuery { + number: block_number, + data: connection_end.map(ConnectionEnd::from_core), + proof: querier::make_proof(&context, &path), + }; + Ok(Some(response)) + } + + fn query_client_connections( + &self, + client_identifier: String, + block_number: Option, + ) -> Result>> { + let block_id = block_number.map(BlockId::Number).unwrap_or(BlockId::Latest); + let mut state = self.client.state_at(block_id).ok_or_else(errors::state_not_exist)?; + let block_number = match self.client.block_number(&block_id) { + None => return Ok(None), + Some(block_number) => block_number, + }; + + let context = ibc::context::TopLevelContext::new(&mut state, block_number); + let path = querier::path_connection_identifiers(&client_identifier); + let connections_in_client: Option = + querier::query(&context, &path); + + let response = IBCQuery { + number: block_number, + data: connections_in_client.map(|from_core| from_core.into_vec()), + proof: querier::make_proof(&context, &path), + }; + Ok(Some(response)) + } } diff --git a/rpc/src/v1/traits/ibc.rs b/rpc/src/v1/traits/ibc.rs index 71199123fa..82bf55fd0f 100644 --- a/rpc/src/v1/traits/ibc.rs +++ b/rpc/src/v1/traits/ibc.rs @@ -15,7 +15,7 @@ // along with this program. If not, see . use super::super::types::IBCQuery; -use super::super::types::{ClientState, ConsensusState}; +use super::super::types::{ClientState, ConnectionEnd, ConnectionIdentifiersInClient, ConsensusState}; use jsonrpc_core::Result; use primitives::Bytes; @@ -39,4 +39,18 @@ pub trait IBC { /// from block_number-1 to block_number. It will stay opaque until it gets finally delieverd to Foundry light client. #[rpc(name = "ibc_compose_header")] fn compose_header(&self, block_number: u64) -> Result>; + + #[rpc(name = "ibc_query_connection")] + fn query_connection( + &self, + identifier: String, + block_number: Option, + ) -> Result>>; + + #[rpc(name = "ibc_query_client_connections")] + fn query_client_connections( + &self, + client_identifier: String, + block_number: Option, + ) -> Result>>; } diff --git a/rpc/src/v1/types/ibc.rs b/rpc/src/v1/types/ibc.rs index b0eca59693..8a6ee191d1 100644 --- a/rpc/src/v1/types/ibc.rs +++ b/rpc/src/v1/types/ibc.rs @@ -15,9 +15,15 @@ // along with this program. If not, see . use codechain_core::ibc::client_02::types::{ClientState as CoreClientState, ConsensusState as CoreConsensusState}; +use codechain_core::ibc::connection_03::types::{ + ConnectionEnd as CoreConnectionEnd, ConnectionState as CoreConnectionState, +}; use primitives::{Bytes, H256}; use serde::Serialize; +type Identifier = String; +type CommitmentPrefix = String; + /// Many of RPC responses will be expressed with this /// Because of the nature of IBC, they commonly /// 1. Requires a block number for which proof stands @@ -68,3 +74,44 @@ impl ConsensusState { } } } + +#[derive(Debug, Serialize)] +pub enum ConnectionState { + INIT, + TRYOPEN, + OPEN, +} + +impl ConnectionState { + pub fn from_core(core_connection_state: CoreConnectionState) -> Self { + match core_connection_state { + CoreConnectionState::INIT => ConnectionState::INIT, + CoreConnectionState::TRYOPEN => ConnectionState::TRYOPEN, + CoreConnectionState::OPEN => ConnectionState::OPEN, + } + } +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ConnectionEnd { + pub state: ConnectionState, + pub counterparty_connection_identifier: Identifier, + pub counterparty_prefix: CommitmentPrefix, + pub client_identifier: Identifier, + pub counterparty_client_identifier: Identifier, +} + +impl ConnectionEnd { + pub fn from_core(core_connection_end: CoreConnectionEnd) -> Self { + ConnectionEnd { + state: ConnectionState::from_core(core_connection_end.state), + counterparty_connection_identifier: core_connection_end.counterparty_connection_identifier, + counterparty_prefix: core_connection_end.counterparty_prefix.raw, + client_identifier: core_connection_end.client_identifier, + counterparty_client_identifier: core_connection_end.counterparty_client_identifier, + } + } +} + +pub type ConnectionIdentifiersInClient = Vec; diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index d6080306cf..5248a92cfa 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -25,7 +25,7 @@ mod work; pub use self::action::{Action, ActionWithTracker}; pub use self::block::Block; pub use self::block::BlockNumberAndHash; -pub use self::ibc::{ClientState, ConsensusState, IBCQuery}; +pub use self::ibc::{ClientState, ConnectionEnd, ConnectionIdentifiersInClient, ConsensusState, IBCQuery}; pub use self::mem_pool::MemPoolMinFees; pub use self::transaction::{PendingTransactions, Transaction}; pub use self::unsigned_transaction::UnsignedTransaction;