From 1eb7f1404c567cd51f38646088ceb12d8a229ed8 Mon Sep 17 00:00:00 2001 From: Juhyung Park Date: Thu, 7 Nov 2019 18:22:44 +0900 Subject: [PATCH] Implement IBC client RPCs --- codechain/rpc_apis.rs | 1 + core/src/ibc/client_02/mod.rs | 4 +- core/src/ibc/context.rs | 2 +- core/src/ibc/mod.rs | 7 +- core/src/lib.rs | 2 +- rpc/src/v1/errors.rs | 18 ++++++ rpc/src/v1/impls/ibc.rs | 118 ++++++++++++++++++++++++++++++++++ rpc/src/v1/impls/mod.rs | 2 + rpc/src/v1/traits/ibc.rs | 43 +++++++++++++ rpc/src/v1/traits/mod.rs | 2 + rpc/src/v1/types/ibc.rs | 23 +++++++ rpc/src/v1/types/mod.rs | 2 + 12 files changed, 216 insertions(+), 8 deletions(-) create mode 100644 rpc/src/v1/impls/ibc.rs create mode 100644 rpc/src/v1/traits/ibc.rs create mode 100644 rpc/src/v1/types/ibc.rs diff --git a/codechain/rpc_apis.rs b/codechain/rpc_apis.rs index 3f4d3c016a..b416d94d70 100644 --- a/codechain/rpc_apis.rs +++ b/codechain/rpc_apis.rs @@ -48,6 +48,7 @@ impl ApiDependencies { AccountClient::new(Arc::clone(&self.account_provider), Arc::clone(&self.client), Arc::clone(&self.miner)) .to_delegate(), ); + handler.extend_with(IBCClient::new(Arc::clone(&self.client)).to_delegate()) } } diff --git a/core/src/ibc/client_02/mod.rs b/core/src/ibc/client_02/mod.rs index 1100230a9a..d9fedbe619 100644 --- a/core/src/ibc/client_02/mod.rs +++ b/core/src/ibc/client_02/mod.rs @@ -23,7 +23,7 @@ use ibc; pub use self::manager::Manager; pub use self::types::{ConsensusState, Header, Kind, State, KIND_CODECHAIN}; -pub fn new_state(id: &str, ctx: &mut dyn ibc::Context, client_type: Kind) -> Box { +fn new_state(id: &str, ctx: &mut dyn ibc::Context, client_type: Kind) -> Box { if client_type == KIND_CODECHAIN { Box::new(codechain::State::new(id, ctx)) } else { @@ -31,7 +31,7 @@ pub fn new_state(id: &str, ctx: &mut dyn ibc::Context, client_type: Kind) -> Box } } -pub fn get_state(id: &str, ctx: &mut dyn ibc::Context) -> Result, String> { +fn get_state(id: &str, ctx: &mut dyn ibc::Context) -> Result, String> { let s = codechain::State::find(id); if s.exists(ctx) { Ok(Box::new(s)) diff --git a/core/src/ibc/context.rs b/core/src/ibc/context.rs index f9682041dc..350b5e8162 100644 --- a/core/src/ibc/context.rs +++ b/core/src/ibc/context.rs @@ -44,7 +44,7 @@ impl<'a> Context for TopLevelContext<'a> { } } -pub struct TopLevelKVStore<'a> { +struct TopLevelKVStore<'a> { state: &'a mut TopLevelState, } diff --git a/core/src/ibc/mod.rs b/core/src/ibc/mod.rs index 03f132f1f2..726b95267d 100644 --- a/core/src/ibc/mod.rs +++ b/core/src/ibc/mod.rs @@ -14,17 +14,16 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -mod client_02; +pub mod client_02; #[allow(dead_code)] #[allow(unused_variables)] mod commitment_23; -#[allow(dead_code)] -#[allow(unused_variables)] -mod context; +pub mod context; #[allow(dead_code)] #[allow(unused_variables)] pub mod custom_action_handler; mod kv_store; +pub use self::client_02 as client; pub use self::context::Context; pub use self::kv_store::KVStore; diff --git a/core/src/lib.rs b/core/src/lib.rs index 08633ce543..e08ad73220 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -70,7 +70,7 @@ mod db; mod db_version; pub mod encoded; mod error; -mod ibc; +pub mod ibc; mod invoice; mod miner; mod scheme; diff --git a/rpc/src/v1/errors.rs b/rpc/src/v1/errors.rs index 6af198ee29..c57d2c10f6 100644 --- a/rpc/src/v1/errors.rs +++ b/rpc/src/v1/errors.rs @@ -74,6 +74,8 @@ mod codes { pub const ASSET_TRANSACTION_ONLY_IN_EXECUTE_TRANSACITON: i64 = -32047; pub const STATE_NOT_EXIST: i64 = -32048; pub const ACTION_DATA_HANDLER_NOT_FOUND: i64 = -32049; + pub const IBC_CLIENT_NOT_EXIST: i64 = -32050; + pub const IBC_CLIENT_ROOT_NOT_EXIST: i64 = -32051; pub const UNKNOWN_ERROR: i64 = -32099; } @@ -304,6 +306,22 @@ pub fn invalid_custom_action(err: String) -> Error { } } +pub fn ibc_client_not_exist() -> Error { + Error { + code: ErrorCode::ServerError(codes::IBC_CLIENT_NOT_EXIST), + message: "IBC client does not exist".to_string(), + data: None, + } +} + +pub fn ibc_client_root_not_exist() -> Error { + Error { + code: ErrorCode::ServerError(codes::IBC_CLIENT_ROOT_NOT_EXIST), + message: "IBC client root does not exist".to_string(), + data: None, + } +} + /// Internal error signifying a logic error in code. /// Should not be used when function can just fail /// because of invalid parameters or incomplete node state. diff --git a/rpc/src/v1/impls/ibc.rs b/rpc/src/v1/impls/ibc.rs new file mode 100644 index 0000000000..8cfe5060b2 --- /dev/null +++ b/rpc/src/v1/impls/ibc.rs @@ -0,0 +1,118 @@ +// Copyright 2019 Kodebox, Inc. +// This file is part of CodeChain. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use std::sync::Arc; + +use ccore::ibc; +use ccore::{BlockChainClient, BlockId, StateInfo}; +use jsonrpc_core::Result; +use primitives::Bytes; +use rustc_serialize::hex::ToHex; + +use super::super::errors; +use super::super::traits::IBC; +use super::super::types::IBCQueryResult; + +pub struct IBCClient +where + C: StateInfo + BlockChainClient, { + client: Arc, +} + +impl IBCClient +where + C: StateInfo + BlockChainClient, +{ + pub fn new(client: Arc) -> Self { + Self { + client, + } + } +} + +impl IBC for IBCClient +where + C: StateInfo + 'static + Send + Sync + BlockChainClient, +{ + fn query_client_consensus_state( + &self, + client_id: 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 mut context = ibc::context::TopLevelContext::new(&mut state); + let client_manager = ibc::client::Manager::new(); + let client_state = + client_manager.query(&mut context, &client_id).map_err(|_| errors::ibc_client_not_exist())?; + + let consensus_state = client_state.get_consensus_state(&mut context); + + let rlp_encoded_consensus_state = consensus_state.encode(); + + Ok(Some(IBCQueryResult { + block_number, + raw: rlp_encoded_consensus_state.to_hex(), + // FIXME + proof: "".to_string(), + })) + } + + fn query_header(&self, block_number: Option) -> Result> { + let block_id = block_number.map(BlockId::Number).unwrap_or(BlockId::Latest); + let header = match self.client.block_header(&block_id) { + None => return Ok(None), + Some(header) => header, + }; + + Ok(Some(header.into_inner().to_hex())) + } + + fn query_client_root( + &self, + client_id: String, + other_block_number: u64, + this_block_number: Option, + ) -> Result> { + let block_id = this_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 mut context = ibc::context::TopLevelContext::new(&mut state); + let client_manager = ibc::client::Manager::new(); + let client_state = + client_manager.query(&mut context, &client_id).map_err(|_| errors::ibc_client_not_exist())?; + + let root = + client_state.get_root(&mut context, other_block_number).map_err(|_| errors::ibc_client_root_not_exist())?; + let rlp_encoded_root = root.encode(); + + Ok(Some(IBCQueryResult { + block_number, + raw: rlp_encoded_root.to_hex(), + // FIXME + proof: "".to_string(), + })) + } +} diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 45e7678459..9b4758e3cc 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -18,6 +18,7 @@ mod account; mod chain; mod devel; mod engine; +mod ibc; mod mempool; mod miner; mod net; @@ -26,6 +27,7 @@ pub use self::account::AccountClient; pub use self::chain::ChainClient; pub use self::devel::DevelClient; pub use self::engine::EngineClient; +pub use self::ibc::IBCClient; pub use self::mempool::MempoolClient; pub use self::miner::MinerClient; pub use self::net::NetClient; diff --git a/rpc/src/v1/traits/ibc.rs b/rpc/src/v1/traits/ibc.rs new file mode 100644 index 0000000000..439c63ded2 --- /dev/null +++ b/rpc/src/v1/traits/ibc.rs @@ -0,0 +1,43 @@ +// Copyright 2019 Kodebox, Inc. +// This file is part of CodeChain. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use jsonrpc_core::Result; +use primitives::Bytes; + +use super::super::types::IBCQueryResult; + + +#[rpc(server)] +pub trait IBC { + #[rpc(name = "ibc_query_client_consensus_state")] + fn query_client_consensus_state( + &self, + client_id: String, + block_number: Option, + ) -> Result>; + + #[rpc(name = "ibc_query_header")] + fn query_header(&self, block_number: Option) -> Result>; + + /// Gets the other chain's root saved in the light client + #[rpc(name = "ibc_query_client_root")] + fn query_client_root( + &self, + client_id: String, + other_block_number: u64, + this_block_number: Option, + ) -> Result>; +} diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index 719f186e49..f731da61a9 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -18,6 +18,7 @@ mod account; mod chain; mod devel; mod engine; +mod ibc; mod mempool; mod miner; mod net; @@ -26,6 +27,7 @@ pub use self::account::Account; pub use self::chain::Chain; pub use self::devel::Devel; pub use self::engine::Engine; +pub use self::ibc::IBC; pub use self::mempool::Mempool; pub use self::miner::Miner; pub use self::net::Net; diff --git a/rpc/src/v1/types/ibc.rs b/rpc/src/v1/types/ibc.rs new file mode 100644 index 0000000000..30c4ace95d --- /dev/null +++ b/rpc/src/v1/types/ibc.rs @@ -0,0 +1,23 @@ +// Copyright 2019 Kodebox, Inc. +// This file is part of CodeChain. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct IBCQueryResult { + pub block_number: u64, + pub raw: String, + pub proof: String, +} diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index 66d9b30ba5..d110baa2f7 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -20,6 +20,7 @@ mod asset_input; mod asset_output; mod asset_scheme; mod block; +mod ibc; mod text; mod transaction; mod unsigned_transaction; @@ -34,6 +35,7 @@ pub use self::asset::OwnedAsset; pub use self::asset_scheme::AssetScheme; pub use self::block::Block; pub use self::block::BlockNumberAndHash; +pub use self::ibc::IBCQueryResult; pub use self::text::Text; pub use self::transaction::{PendingTransactions, Transaction}; pub use self::unsigned_transaction::UnsignedTransaction;