From 1d159152c7ca3ae320d23368965ec8a684e2faab Mon Sep 17 00:00:00 2001 From: brenzi Date: Wed, 6 Nov 2024 08:31:52 +0100 Subject: [PATCH] more rpc methods (#1636) * implement rpc system_version * started... * SidechainBlockHeader cache crate implemented. tests pass * write to header cache * implement chain_getHeader * implement cli get-sidechain-header. works * system_version rpc and cli * fix tests * clippy --- Cargo.lock | 13 ++ Cargo.toml | 1 + cli/Cargo.toml | 1 + cli/src/lib.rs | 7 + .../trusted_base_cli/commands/get_header.rs | 62 +++++++++ cli/src/trusted_base_cli/commands/mod.rs | 2 + cli/src/trusted_base_cli/commands/version.rs | 63 +++++++++ cli/src/trusted_base_cli/mod.rs | 15 ++- enclave-runtime/Cargo.lock | 12 ++ enclave-runtime/Cargo.toml | 1 + .../src/initialization/global_components.rs | 6 +- enclave-runtime/src/initialization/mod.rs | 15 ++- enclave-runtime/src/rpc/common_api.rs | 25 +++- enclave-runtime/src/test/direct_rpc_tests.rs | 7 + .../src/test/sidechain_aura_tests.rs | 2 + .../src/test/sidechain_event_tests.rs | 2 + sidechain/block-header-cache/Cargo.toml | 28 ++++ .../src/block_header_cache.rs | 125 ++++++++++++++++++ sidechain/block-header-cache/src/error.rs | 32 +++++ sidechain/block-header-cache/src/lib.rs | 63 +++++++++ sidechain/consensus/aura/Cargo.toml | 3 + .../consensus/aura/src/block_importer.rs | 32 ++++- .../aura/src/test/block_importer_tests.rs | 2 + sidechain/consensus/common/Cargo.toml | 3 + .../consensus/common/src/block_import.rs | 12 +- .../src/block_import_confirmation_handler.rs | 1 + .../src/test/mocks/block_importer_mock.rs | 12 +- 27 files changed, 528 insertions(+), 19 deletions(-) create mode 100644 cli/src/trusted_base_cli/commands/get_header.rs create mode 100644 cli/src/trusted_base_cli/commands/version.rs create mode 100644 sidechain/block-header-cache/Cargo.toml create mode 100644 sidechain/block-header-cache/src/block_header_cache.rs create mode 100644 sidechain/block-header-cache/src/error.rs create mode 100644 sidechain/block-header-cache/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2b0d1fe9ed..dcc9233df9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2577,6 +2577,7 @@ dependencies = [ "itp-time-utils", "itp-types", "itp-utils", + "its-primitives", "log 0.4.20", "pallet-balances", "pallet-enclave-bridge", @@ -3710,6 +3711,16 @@ dependencies = [ "thiserror 1.0.9", ] +[[package]] +name = "its-block-header-cache" +version = "0.1.0" +dependencies = [ + "its-primitives", + "sgx_tstd", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + [[package]] name = "its-block-verification" version = "0.9.0" @@ -3754,6 +3765,7 @@ dependencies = [ "itp-types", "itp-utils", "its-block-composer", + "its-block-header-cache", "its-block-verification", "its-consensus-common", "its-consensus-slots", @@ -3788,6 +3800,7 @@ dependencies = [ "itp-test", "itp-types", "itp-utils", + "its-block-header-cache", "its-block-verification", "its-primitives", "its-state", diff --git a/Cargo.toml b/Cargo.toml index dbf8554cd4..30366aa38a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,7 @@ members = [ "core-primitives/utils", "service", "sidechain/block-composer", + "sidechain/block-header-cache", "sidechain/block-verification", "sidechain/consensus/aura", "sidechain/consensus/common", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 5bbfbd334e..bd06b98465 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -58,6 +58,7 @@ itp-stf-primitives = { path = "../core-primitives/stf-primitives" } itp-time-utils = { path = "../core-primitives/time-utils" } itp-types = { path = "../core-primitives/types" } itp-utils = { path = "../core-primitives/utils" } +its-primitives = { path = "../sidechain/primitives" } [features] default = [] diff --git a/cli/src/lib.rs b/cli/src/lib.rs index b02e3baa7e..29fd8cfb05 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -48,6 +48,7 @@ use crate::commands::Commands; use clap::Parser; use ita_stf::{guess_the_number::GuessTheNumberInfo, ParentchainsInfo}; use itp_node_api::api_client::Metadata; +use its_primitives::types::header::SidechainHeader; use sp_application_crypto::KeyTypeId; use sp_core::{H160, H256}; use thiserror::Error; @@ -113,9 +114,15 @@ pub enum CliResultOk { U32 { value: u32, }, + SidechainHeader { + header: SidechainHeader, + }, ParentchainsInfo { info: ParentchainsInfo, }, + String { + value: String, + }, GuessAttempts { value: u8, }, diff --git a/cli/src/trusted_base_cli/commands/get_header.rs b/cli/src/trusted_base_cli/commands/get_header.rs new file mode 100644 index 0000000000..31b838dfeb --- /dev/null +++ b/cli/src/trusted_base_cli/commands/get_header.rs @@ -0,0 +1,62 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::{ + command_utils::get_worker_api_direct, trusted_cli::TrustedCli, Cli, CliError, CliResult, + CliResultOk, +}; +use codec::Decode; +use itc_rpc_client::direct_client::DirectApi; +use itp_rpc::{RpcRequest, RpcResponse, RpcReturnValue}; +use itp_types::DirectRequestStatus; +use itp_utils::FromHexPrefixed; +use its_primitives::types::header::SidechainHeader; +use log::error; + +#[derive(Parser)] +pub struct GetSidechainHeaderCommand {} + +impl GetSidechainHeaderCommand { + pub(crate) fn run(&self, cli: &Cli, _trusted_args: &TrustedCli) -> CliResult { + let direct_api = get_worker_api_direct(cli); + let rpc_method = "chain_getHeader".to_owned(); + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call(rpc_method, vec![]).unwrap(); + let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); + // Decode RPC response. + let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str) + .map_err(|err| CliError::WorkerRpcApi { msg: err.to_string() })?; + let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) + // Replace with `inspect_err` once it's stable. + .map_err(|err| { + error!("Failed to decode RpcReturnValue: {:?}", err); + CliError::WorkerRpcApi { msg: "failed to decode RpcReturnValue".to_string() } + })?; + + if rpc_return_value.status == DirectRequestStatus::Error { + error!("{}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); + return Err(CliError::WorkerRpcApi { msg: "rpc error".to_string() }) + } + + let header = SidechainHeader::decode(&mut rpc_return_value.value.as_slice()) + // Replace with `inspect_err` once it's stable. + .map_err(|err| { + error!("Failed to decode sidechain header: {:?}", err); + CliError::WorkerRpcApi { msg: err.to_string() } + })?; + println!("{:?}", header); + Ok(CliResultOk::SidechainHeader { header }) + } +} diff --git a/cli/src/trusted_base_cli/commands/mod.rs b/cli/src/trusted_base_cli/commands/mod.rs index 39edf5d346..a49999e18c 100644 --- a/cli/src/trusted_base_cli/commands/mod.rs +++ b/cli/src/trusted_base_cli/commands/mod.rs @@ -1,5 +1,6 @@ pub mod balance; pub mod get_fingerprint; +pub mod get_header; pub mod get_parentchains_info; pub mod get_shard; pub mod get_shard_vault; @@ -8,6 +9,7 @@ pub mod get_total_issuance; pub mod nonce; pub mod transfer; pub mod unshield_funds; +pub mod version; #[cfg(feature = "test")] pub mod set_balance; diff --git a/cli/src/trusted_base_cli/commands/version.rs b/cli/src/trusted_base_cli/commands/version.rs new file mode 100644 index 0000000000..cf146b0038 --- /dev/null +++ b/cli/src/trusted_base_cli/commands/version.rs @@ -0,0 +1,63 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::{ + command_utils::get_worker_api_direct, trusted_cli::TrustedCli, Cli, CliError, CliResult, + CliResultOk, +}; +use codec::Decode; +use itc_rpc_client::direct_client::DirectApi; +use itp_rpc::{RpcRequest, RpcResponse, RpcReturnValue}; + +use itp_types::DirectRequestStatus; +use itp_utils::FromHexPrefixed; + +use log::error; + +#[derive(Parser)] +pub struct VersionCommand {} + +impl VersionCommand { + pub(crate) fn run(&self, cli: &Cli, _trusted_args: &TrustedCli) -> CliResult { + let direct_api = get_worker_api_direct(cli); + let rpc_method = "system_version".to_owned(); + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call(rpc_method, vec![]).unwrap(); + let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); + // Decode RPC response. + let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str) + .map_err(|err| CliError::WorkerRpcApi { msg: err.to_string() })?; + let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) + // Replace with `inspect_err` once it's stable. + .map_err(|err| { + error!("Failed to decode RpcReturnValue: {:?}", err); + CliError::WorkerRpcApi { msg: "failed to decode RpcReturnValue".to_string() } + })?; + + if rpc_return_value.status == DirectRequestStatus::Error { + error!("{}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); + return Err(CliError::WorkerRpcApi { msg: "rpc error".to_string() }) + } + + let version = String::from_utf8(rpc_return_value.value) + // Replace with `inspect_err` once it's stable. + .map_err(|err| { + error!("Failed to decode sidechain header: {:?}", err); + CliError::WorkerRpcApi { msg: err.to_string() } + })?; + println!("{}", version); + Ok(CliResultOk::String { value: version }) + } +} diff --git a/cli/src/trusted_base_cli/mod.rs b/cli/src/trusted_base_cli/mod.rs index f971181a08..88b8f6f03a 100644 --- a/cli/src/trusted_base_cli/mod.rs +++ b/cli/src/trusted_base_cli/mod.rs @@ -20,9 +20,10 @@ use crate::trusted_base_cli::commands::set_balance::SetBalanceCommand; use crate::{ trusted_base_cli::commands::{ balance::BalanceCommand, get_fingerprint::GetFingerprintCommand, - get_parentchains_info::GetParentchainsInfoCommand, get_shard::GetShardCommand, - get_shard_vault::GetShardVaultCommand, get_total_issuance::GetTotalIssuanceCommand, - nonce::NonceCommand, transfer::TransferCommand, unshield_funds::UnshieldFundsCommand, + get_header::GetSidechainHeaderCommand, get_parentchains_info::GetParentchainsInfoCommand, + get_shard::GetShardCommand, get_shard_vault::GetShardVaultCommand, + get_total_issuance::GetTotalIssuanceCommand, nonce::NonceCommand, + transfer::TransferCommand, unshield_funds::UnshieldFundsCommand, version::VersionCommand, }, trusted_cli::TrustedCli, trusted_command_utils::get_keystore_path, @@ -69,11 +70,17 @@ pub enum TrustedBaseCommand { /// get shard vault for shielding (if defined for this worker) GetShardVault(GetShardVaultCommand), + /// get sidechain header + GetSidechainHeader(GetSidechainHeaderCommand), + /// get total issuance of this shard's native token GetTotalIssuance(GetTotalIssuanceCommand), /// get info for all parentchains' sync status GetParentchainsInfo(GetParentchainsInfoCommand), + + /// get a version string for the enclave + Version(VersionCommand), } impl TrustedBaseCommand { @@ -91,7 +98,9 @@ impl TrustedBaseCommand { TrustedBaseCommand::GetParentchainsInfo(cmd) => cmd.run(cli, trusted_cli), TrustedBaseCommand::GetShard(cmd) => cmd.run(cli, trusted_cli), TrustedBaseCommand::GetShardVault(cmd) => cmd.run(cli, trusted_cli), + TrustedBaseCommand::GetSidechainHeader(cmd) => cmd.run(cli, trusted_cli), TrustedBaseCommand::GetTotalIssuance(cmd) => cmd.run(cli, trusted_cli), + TrustedBaseCommand::Version(cmd) => cmd.run(cli, trusted_cli), } } } diff --git a/enclave-runtime/Cargo.lock b/enclave-runtime/Cargo.lock index 8b886cb568..8c2eeadc6a 100644 --- a/enclave-runtime/Cargo.lock +++ b/enclave-runtime/Cargo.lock @@ -821,6 +821,7 @@ dependencies = [ "itp-top-pool-author", "itp-types", "itp-utils", + "its-block-header-cache", "its-block-verification", "its-primitives", "its-rpc-handler", @@ -2426,6 +2427,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "its-block-header-cache" +version = "0.1.0" +dependencies = [ + "its-primitives", + "sgx_tstd", + "thiserror", +] + [[package]] name = "its-block-verification" version = "0.9.0" @@ -2462,6 +2472,7 @@ dependencies = [ "itp-types", "itp-utils", "its-block-composer", + "its-block-header-cache", "its-block-verification", "its-consensus-common", "its-consensus-slots", @@ -2491,6 +2502,7 @@ dependencies = [ "itp-sgx-crypto", "itp-types", "itp-utils", + "its-block-header-cache", "its-block-verification", "its-primitives", "its-state", diff --git a/enclave-runtime/Cargo.toml b/enclave-runtime/Cargo.toml index dcd49bcf42..a824159b23 100644 --- a/enclave-runtime/Cargo.toml +++ b/enclave-runtime/Cargo.toml @@ -130,6 +130,7 @@ itp-top-pool = { path = "../core-primitives/top-pool", default-features = false, itp-top-pool-author = { path = "../core-primitives/top-pool-author", default-features = false, features = ["sgx"] } itp-types = { path = "../core-primitives/types", default-features = false } itp-utils = { path = "../core-primitives/utils", default-features = false } +its-block-header-cache = { path = "../sidechain/block-header-cache", default-features = false, features = ["sgx"] } its-block-verification = { path = "../sidechain/block-verification", default-features = false } its-primitives = { path = "../sidechain/primitives", default-features = false } its-rpc-handler = { path = "../sidechain/rpc-handler", default-features = false, features = ["sgx"] } diff --git a/enclave-runtime/src/initialization/global_components.rs b/enclave-runtime/src/initialization/global_components.rs index 5843a955cb..ac378c8597 100644 --- a/enclave-runtime/src/initialization/global_components.rs +++ b/enclave-runtime/src/initialization/global_components.rs @@ -78,9 +78,10 @@ use itp_top_pool_author::{ author::{Author, AuthorTopFilter}, }; use itp_types::{Block as ParentchainBlock, SignedBlock as SignedParentchainBlock}; +use its_block_header_cache::SidechainBlockHeaderCache; use its_primitives::{ traits::{Block as SidechainBlockTrait, SignedBlock as SignedSidechainBlockTrait}, - types::block::SignedBlock as SignedSidechainBlock, + types::{block::SignedBlock as SignedSidechainBlock, header::SidechainHeader}, }; use its_sidechain::{ aura::block_importer::BlockImporter as SidechainBlockImporter, @@ -421,6 +422,9 @@ lazy_static! { /// Global nonce cache for the Target B parentchain.. pub static ref GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE: Arc = Default::default(); + + /// Global sidechain header cache + pub static ref GLOBAL_SIDECHAIN_BLOCK_HEADER_CACHE: Arc> = Default::default(); } /// Solochain Handler. diff --git a/enclave-runtime/src/initialization/mod.rs b/enclave-runtime/src/initialization/mod.rs index dc46086423..4a6dd28507 100644 --- a/enclave-runtime/src/initialization/mod.rs +++ b/enclave-runtime/src/initialization/mod.rs @@ -29,10 +29,11 @@ use crate::{ GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_OCALL_API_COMPONENT, GLOBAL_RPC_WS_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT, - GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, - GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, - GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, - GLOBAL_STATE_OBSERVER_COMPONENT, GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, + GLOBAL_SIDECHAIN_BLOCK_HEADER_CACHE, GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT, + GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT, + GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, + GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_OBSERVER_COMPONENT, + GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TOP_POOL_AUTHOR_COMPONENT, GLOBAL_WEB_SOCKET_SERVER_COMPONENT, }, @@ -80,6 +81,9 @@ use jsonrpc_core::IoHandler; use log::*; use sp_core::crypto::Pair; use std::{collections::HashMap, path::PathBuf, string::String, sync::Arc}; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); + pub(crate) fn init_enclave( mu_ra_url: String, untrusted_worker_url: String, @@ -182,6 +186,8 @@ pub(crate) fn init_enclave( getter_executor, shielding_key_repository, ocall_api.clone(), + VERSION.into(), + GLOBAL_SIDECHAIN_BLOCK_HEADER_CACHE.clone(), ); #[cfg(feature = "sidechain")] @@ -236,6 +242,7 @@ pub(crate) fn init_enclave_sidechain_components() -> EnclaveResult<()> { top_pool_author, parentchain_block_import_dispatcher, ocall_api.clone(), + GLOBAL_SIDECHAIN_BLOCK_HEADER_CACHE.clone(), )); let sidechain_block_import_queue = GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT.get()?; diff --git a/enclave-runtime/src/rpc/common_api.rs b/enclave-runtime/src/rpc/common_api.rs index e9883027bf..133583bac2 100644 --- a/enclave-runtime/src/rpc/common_api.rs +++ b/enclave-runtime/src/rpc/common_api.rs @@ -40,6 +40,8 @@ use itp_stf_executor::{getter_executor::ExecuteGetter, traits::StfShardVaultQuer use itp_top_pool_author::traits::AuthorApi; use itp_types::{DirectRequestStatus, EnclaveFingerprint, Request, ShardIdentifier, H256}; use itp_utils::{FromHexPrefixed, ToHexPrefixed}; +use its_block_header_cache::{GetSidechainBlockHeader, SidechainBlockHeaderCache}; +use its_primitives::types::header::SidechainHeader; use its_rpc_handler::direct_top_pool_api::add_top_pool_direct_rpc_methods; use jsonrpc_core::{serde_json::json, IoHandler, Params, Value}; use log::debug; @@ -57,6 +59,8 @@ pub fn add_common_api( getter_executor: Arc, shielding_key: Arc, ocall_api: Arc, + enclave_version: String, + sidechain_header_cache: Arc>, ) where Author: AuthorApi + Send + Sync + 'static, GetterExecutor: ExecuteGetter + Send + Sync + 'static, @@ -181,13 +185,17 @@ pub fn add_common_api( }); let local_ocall_api = ocall_api.clone(); - io_handler.add_sync_method("chain_subscribeAllHeads", move |_: Params| { - debug!("worker_api_direct rpc was called: chain_subscribeAllHeads"); + io_handler.add_sync_method("chain_getHeader", move |_: Params| { + debug!("worker_api_direct rpc was called: chain_getHeader"); local_ocall_api .update_metrics(vec![EnclaveMetric::RpcRequestsIncrement]) .unwrap_or_else(|e| error!("failed to update prometheus metric: {:?}", e)); - let parsed = "world"; - Ok(Value::String(format!("hello, {}", parsed))) + let json_value = if let Ok(header) = sidechain_header_cache.get_header() { + RpcReturnValue::new(header.0.encode(), false, DirectRequestStatus::Ok) + } else { + RpcReturnValue::new(0u8.encode(), false, DirectRequestStatus::Error) + }; + Ok(json!(json_value.to_hex())) }); let local_ocall_api = ocall_api.clone(); @@ -292,8 +300,13 @@ pub fn add_common_api( local_ocall_api .update_metrics(vec![EnclaveMetric::RpcRequestsIncrement]) .unwrap_or_else(|e| error!("failed to update prometheus metric: {:?}", e)); - let parsed = "world"; - Ok(Value::String(format!("hello, {}", parsed))) + let version = format!("enclave-runtime: {}", enclave_version); + let json_value = RpcReturnValue { + do_watch: false, + value: version.as_bytes().into(), + status: DirectRequestStatus::Ok, + }; + Ok(json!(json_value.to_hex())) }); } diff --git a/enclave-runtime/src/test/direct_rpc_tests.rs b/enclave-runtime/src/test/direct_rpc_tests.rs index 577222356a..ed116fc647 100644 --- a/enclave-runtime/src/test/direct_rpc_tests.rs +++ b/enclave-runtime/src/test/direct_rpc_tests.rs @@ -33,6 +33,8 @@ use itp_test::mock::onchain_mock::OnchainMock; use itp_top_pool_author::mocks::AuthorApiMock; use itp_types::{AccountId, DirectRequestStatus, Request, ShardIdentifier}; use itp_utils::{FromHexPrefixed, ToHexPrefixed}; +use its_block_header_cache::{CachedSidechainBlockHeader, SidechainBlockHeaderCache}; +use its_primitives::types::header::SidechainHeader; use jsonrpc_core::IoHandler; use sp_core::ed25519::Signature; use sp_runtime::MultiSignature; @@ -60,6 +62,11 @@ pub fn get_state_request_works() { getter_executor, Arc::new(rsa_repository), ocall_api, + "0.0.0-test".into(), + SidechainBlockHeaderCache::new( + CachedSidechainBlockHeader(SidechainHeader::default()).into(), + ) + .into(), ); let rpc_handler = Arc::new(RpcWsHandler::new(io_handler, watch_extractor, connection_registry)); diff --git a/enclave-runtime/src/test/sidechain_aura_tests.rs b/enclave-runtime/src/test/sidechain_aura_tests.rs index fad5c2ec9b..08f7bca82b 100644 --- a/enclave-runtime/src/test/sidechain_aura_tests.rs +++ b/enclave-runtime/src/test/sidechain_aura_tests.rs @@ -50,6 +50,7 @@ use itp_test::mock::handle_state_mock::HandleStateMock; use itp_time_utils::duration_now; use itp_top_pool_author::{top_filter::AllowAllTopsFilter, traits::AuthorApi}; use itp_types::{AccountId, Block as ParentchainBlock, ShardIdentifier}; +use its_block_header_cache::SidechainBlockHeaderCache; use its_block_verification::slot::slot_from_timestamp_and_duration; use its_primitives::{traits::Block, types::SignedBlock as SignedSidechainBlock}; use its_sidechain::{aura::proposer_factory::ProposerFactory, slots::SlotInfo}; @@ -112,6 +113,7 @@ pub fn produce_sidechain_block_and_import_it() { top_pool_author.clone(), parentchain_block_import_trigger.clone(), ocall_api.clone(), + SidechainBlockHeaderCache::default().into(), )); let block_composer = Arc::new(TestBlockComposer::new(signer, state_key_repo)); let proposer_environment = diff --git a/enclave-runtime/src/test/sidechain_event_tests.rs b/enclave-runtime/src/test/sidechain_event_tests.rs index f56e34a54a..fcc1e5432a 100644 --- a/enclave-runtime/src/test/sidechain_event_tests.rs +++ b/enclave-runtime/src/test/sidechain_event_tests.rs @@ -41,6 +41,7 @@ use itp_stf_state_handler::handle_state::HandleState; use itp_time_utils::duration_now; use itp_top_pool_author::top_filter::AllowAllTopsFilter; use itp_types::Block as ParentchainBlock; +use its_block_header_cache::SidechainBlockHeaderCache; use its_block_verification::slot::slot_from_timestamp_and_duration; use its_primitives::types::SignedBlock as SignedSidechainBlock; use its_sidechain::{aura::proposer_factory::ProposerFactory, slots::SlotInfo}; @@ -99,6 +100,7 @@ pub fn ensure_events_get_reset_upon_block_proposal() { top_pool_author.clone(), parentchain_block_import_trigger.clone(), ocall_api.clone(), + SidechainBlockHeaderCache::default().into(), )); let block_composer = Arc::new(TestBlockComposer::new(signer, state_key_repo)); let proposer_environment = ProposerFactory::new(top_pool_author, stf_executor, block_composer); diff --git a/sidechain/block-header-cache/Cargo.toml b/sidechain/block-header-cache/Cargo.toml new file mode 100644 index 0000000000..4173d5e2ef --- /dev/null +++ b/sidechain/block-header-cache/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "its-block-header-cache" +version = "0.1.0" +authors = ["Integritee AG "] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# local dependencies +its-primitives = { default-features = false, path = "../primitives" } +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +[features] +default = ["std"] +std = [ + "thiserror", + "its-primitives/std", +] +sgx = [ + "sgx_tstd", + "thiserror_sgx", +] diff --git a/sidechain/block-header-cache/src/block_header_cache.rs b/sidechain/block-header-cache/src/block_header_cache.rs new file mode 100644 index 0000000000..67fa7f3ba4 --- /dev/null +++ b/sidechain/block-header-cache/src/block_header_cache.rs @@ -0,0 +1,125 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLock as RwLock; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLockWriteGuard as RwLockWriteGuard; + +#[cfg(feature = "std")] +use std::sync::RwLock; +#[cfg(feature = "std")] +use std::sync::RwLockWriteGuard; + +use crate::{ + error::{Error, Result}, + CachedSidechainBlockHeader, GetSidechainBlockHeader, MutateSidechainBlockHeader, +}; + +/// Local header cache +/// +/// stores the header internally, protected by a RW lock for concurrent access +#[derive(Default)] +pub struct SidechainBlockHeaderCache
{ + block_header_lock: RwLock>, +} + +impl
SidechainBlockHeaderCache
{ + pub fn new(block_header_lock: RwLock>) -> Self { + SidechainBlockHeaderCache { block_header_lock } + } +} + +impl
MutateSidechainBlockHeader
for SidechainBlockHeaderCache
{ + fn load_for_mutation( + &self, + ) -> Result>> { + self.block_header_lock.write().map_err(|_| Error::LockPoisoning) + } +} + +impl GetSidechainBlockHeader
for SidechainBlockHeaderCache
{ + fn get_header(&self) -> Result> { + let header_lock = self.block_header_lock.read().map_err(|_| Error::LockPoisoning)?; + Ok(*header_lock) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use its_primitives::{traits::Header, types::header::SidechainHeader}; + use std::{sync::Arc, thread}; + + #[test] + pub fn cache_defaults_correctly() { + let cache = SidechainBlockHeaderCache::default(); + assert_eq!( + CachedSidechainBlockHeader(SidechainHeader::default()), + cache.get_header().unwrap() + ); + } + + #[test] + pub fn set_block_header_works() { + let block_header_cache = SidechainBlockHeaderCache::default(); + let mut block_header_lock = block_header_cache.load_for_mutation().unwrap(); + let desired_header = SidechainHeader::new( + 42, + Default::default(), + Default::default(), + Default::default(), + 53, + ); + + *block_header_lock = CachedSidechainBlockHeader(desired_header); + std::mem::drop(block_header_lock); + assert_eq!( + CachedSidechainBlockHeader(desired_header), + block_header_cache.get_header().unwrap() + ); + } + + #[test] + pub fn concurrent_read_access_blocks_until_write_is_done() { + let block_header_cache = Arc::new(SidechainBlockHeaderCache::default()); + + let mut block_header_write_lock = block_header_cache.load_for_mutation().unwrap(); + + let desired_header = SidechainHeader::new( + 42, + Default::default(), + Default::default(), + Default::default(), + 53, + ); + // spawn a new thread that reads the header + // this thread should be blocked until the write lock is released, i.e. until + // the new header is written. We can verify this, by trying to read that header variable + // that will be inserted further down below + let new_thread_block_header_cache = block_header_cache.clone(); + let join_handle = thread::spawn(move || { + let block_header_read = new_thread_block_header_cache.get_header().unwrap(); + assert_eq!(CachedSidechainBlockHeader(desired_header), block_header_read); + }); + + *block_header_write_lock = CachedSidechainBlockHeader(desired_header); + std::mem::drop(block_header_write_lock); + + join_handle.join().unwrap(); + } +} diff --git a/sidechain/block-header-cache/src/error.rs b/sidechain/block-header-cache/src/error.rs new file mode 100644 index 0000000000..c08f1ce39a --- /dev/null +++ b/sidechain/block-header-cache/src/error.rs @@ -0,0 +1,32 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use std::boxed::Box; + +pub type Result = core::result::Result; + +/// header cache error +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("SidechainBlockHeader lock is poisoned")] + LockPoisoning, + #[error(transparent)] + Other(#[from] Box), +} diff --git a/sidechain/block-header-cache/src/lib.rs b/sidechain/block-header-cache/src/lib.rs new file mode 100644 index 0000000000..aa06e4304e --- /dev/null +++ b/sidechain/block-header-cache/src/lib.rs @@ -0,0 +1,63 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(assert_matches)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +#[cfg(feature = "std")] +use std::sync::RwLockWriteGuard; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLockWriteGuard as RwLockWriteGuard; + +use crate::error::Result; + +pub use block_header_cache::SidechainBlockHeaderCache; + +pub mod block_header_cache; +pub mod error; + +/// Header type (newtype wrapper for BlockHeaderValue) +#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] +pub struct CachedSidechainBlockHeader
(pub Header); +/// Trait to mutate a BlockHeader. +/// +/// Used in a combination of loading a lock and then writing the updated +/// value back, returning the lock again. +pub trait MutateSidechainBlockHeader
{ + /// load a BlockHeader with the intention to mutate it. lock is released once it goes out of scope + fn load_for_mutation(&self) + -> Result>>; +} + +/// Trait to get a BlockHeader. +/// +/// +pub trait GetSidechainBlockHeader
{ + fn get_header(&self) -> Result>; +} diff --git a/sidechain/consensus/aura/Cargo.toml b/sidechain/consensus/aura/Cargo.toml index 3e1501bb87..4f05c3edaa 100644 --- a/sidechain/consensus/aura/Cargo.toml +++ b/sidechain/consensus/aura/Cargo.toml @@ -32,6 +32,7 @@ itp-top-pool-author = { path = "../../../core-primitives/top-pool-author", defau itp-types = { path = "../../../core-primitives/types", default-features = false } itp-utils = { path = "../../../core-primitives/utils", default-features = false } its-block-composer = { path = "../../block-composer", default-features = false } +its-block-header-cache = { path = "../../block-header-cache", default-features = false } its-block-verification = { path = "../../block-verification", optional = true, default-features = false } its-consensus-common = { path = "../common", default-features = false } its-consensus-slots = { path = "../slots", default-features = false } @@ -73,6 +74,7 @@ std = [ "itp-types/std", "itp-utils/std", "its-block-composer/std", + "its-block-header-cache/std", "its-block-verification/std", "its-consensus-common/std", "its-consensus-slots/std", @@ -91,6 +93,7 @@ sgx = [ "itp-stf-state-handler/sgx", "itp-time-utils/sgx", "its-block-composer/sgx", + "its-block-header-cache/sgx", "its-consensus-common/sgx", "its-consensus-slots/sgx", "its-state/sgx", diff --git a/sidechain/consensus/aura/src/block_importer.rs b/sidechain/consensus/aura/src/block_importer.rs index 329e42af04..001900631a 100644 --- a/sidechain/consensus/aura/src/block_importer.rs +++ b/sidechain/consensus/aura/src/block_importer.rs @@ -31,10 +31,13 @@ use itp_stf_primitives::{traits::TrustedCallVerification, types::TrustedOperatio use itp_stf_state_handler::handle_state::HandleState; use itp_top_pool_author::traits::{AuthorApi, OnBlockImported}; use itp_types::H256; +use its_block_header_cache::{ + CachedSidechainBlockHeader, MutateSidechainBlockHeader, SidechainBlockHeaderCache, +}; pub use its_consensus_common::BlockImport; use its_consensus_common::Error as ConsensusError; use its_primitives::traits::{ - BlockData, Header as HeaderTrait, ShardIdentifierFor, SignedBlock as SignedBlockTrait, + Block, BlockData, Header as HeaderTrait, ShardIdentifierFor, SignedBlock as SignedBlockTrait, }; use its_validateer_fetch::ValidateerFetch; use log::*; @@ -50,7 +53,7 @@ use std::{marker::PhantomData, sync::Arc}; pub struct BlockImporter< Authority, ParentchainBlock, - SignedSidechainBlock, + SignedSidechainBlock: SignedBlockTrait, OCallApi, StateHandler, StateKeyRepository, @@ -64,6 +67,11 @@ pub struct BlockImporter< top_pool_author: Arc, parentchain_block_importer: Arc, ocall_api: Arc, + header_cache: Arc< + SidechainBlockHeaderCache< + <::Block as Block>::HeaderType, + >, + >, _phantom: PhantomData<(Authority, ParentchainBlock, SignedSidechainBlock, TCS, G)>, } @@ -119,6 +127,11 @@ impl< top_pool_author: Arc, parentchain_block_importer: Arc, ocall_api: Arc, + header_cache: Arc< + SidechainBlockHeaderCache< + <::Block as Block>::HeaderType, + >, + >, ) -> Self { Self { state_handler, @@ -126,6 +139,7 @@ impl< top_pool_author, parentchain_block_importer, ocall_api, + header_cache, _phantom: Default::default(), } } @@ -187,7 +201,7 @@ impl< ParentchainBlock: ParentchainBlockTrait, SignedSidechainBlock: SignedBlockTrait + 'static, <::Block as SidechainBlockTrait>::HeaderType: - HeaderTrait, + HeaderTrait + Copy, OCallApi: EnclaveSidechainOCallApi + ValidateerFetch + EnclaveOnChainOCallApi @@ -219,6 +233,18 @@ impl< ) } + fn update_latest_sidechain_header( + &self, + head: &<::Block as SidechainBlockTrait>::HeaderType, + ) -> Result<(), ConsensusError> { + let mut header_lock = self + .header_cache + .load_for_mutation() + .map_err(|_| ConsensusError::LockPoisoning)?; + *header_lock = CachedSidechainBlockHeader(*head); + Ok(()) + } + fn apply_state_update( &self, shard: &ShardIdentifierFor, diff --git a/sidechain/consensus/aura/src/test/block_importer_tests.rs b/sidechain/consensus/aura/src/test/block_importer_tests.rs index 43a4f7ea77..cb3c979e93 100644 --- a/sidechain/consensus/aura/src/test/block_importer_tests.rs +++ b/sidechain/consensus/aura/src/test/block_importer_tests.rs @@ -31,6 +31,7 @@ use itp_test::mock::{ use itp_time_utils::{duration_now, now_as_millis}; use itp_top_pool_author::mocks::AuthorApiMock; use itp_types::{Block as ParentchainBlock, Header as ParentchainHeader, H256}; +use its_block_header_cache::SidechainBlockHeaderCache; use its_consensus_common::{BlockImport, Error as ConsensusError}; use its_primitives::{ traits::{SignBlock, SignedBlock}, @@ -95,6 +96,7 @@ fn test_fixtures( top_pool_author.clone(), parentchain_block_import_trigger, ocall_api, + SidechainBlockHeaderCache::default().into(), ); (block_importer, state_handler, top_pool_author) diff --git a/sidechain/consensus/common/Cargo.toml b/sidechain/consensus/common/Cargo.toml index 0d9f33ada5..e0ee53b0b5 100644 --- a/sidechain/consensus/common/Cargo.toml +++ b/sidechain/consensus/common/Cargo.toml @@ -22,6 +22,7 @@ itp-settings = { path = "../../../core-primitives/settings" } itp-sgx-crypto = { path = "../../../core-primitives/sgx/crypto", default-features = false } itp-types = { path = "../../../core-primitives/types", default-features = false } itp-utils = { path = "../../../core-primitives/utils", default-features = false } +its-block-header-cache = { path = "../../block-header-cache", default-features = false } its-block-verification = { path = "../../block-verification", optional = true, default-features = false } its-primitives = { path = "../../primitives", default-features = false } its-state = { path = "../../state", default-features = false } @@ -63,6 +64,7 @@ std = [ "itp-types/std", "itp-utils/std", "its-primitives/std", + "its-block-header-cache/std", "its-block-verification/std", "its-state/std", "fork-tree/std", @@ -79,6 +81,7 @@ sgx = [ "itp-node-api-metadata-provider/sgx", "itp-sgx-crypto/sgx", "itp-sgx-externalities/sgx", + "its-block-header-cache/sgx", "its-state/sgx", "fork-tree/sgx", # scs diff --git a/sidechain/consensus/common/src/block_import.rs b/sidechain/consensus/common/src/block_import.rs index 567728bce2..e92ac42267 100644 --- a/sidechain/consensus/common/src/block_import.rs +++ b/sidechain/consensus/common/src/block_import.rs @@ -34,6 +34,7 @@ pub trait BlockImport where ParentchainBlock: ParentchainBlockTrait, SignedSidechainBlock: SignedSidechainBlockTrait, + SignedSidechainBlock::Block: SidechainBlockTrait, { /// The verifier for of the respective consensus instance. type Verifier: Verifier< @@ -67,6 +68,12 @@ where where F: FnOnce(Self::SidechainState) -> Result; + /// update sidechain header cache to last imported block + fn update_latest_sidechain_header( + &self, + head: &<::Block as SidechainBlockTrait>::HeaderType, + ) -> Result<(), Error>; + /// Verify a sidechain block that is to be imported. fn verify_import( &self, @@ -174,7 +181,10 @@ where self.cleanup(&signed_sidechain_block)?; // Store block in storage. - self.get_context().store_sidechain_blocks(vec![signed_sidechain_block])?; + self.get_context() + .store_sidechain_blocks(vec![signed_sidechain_block.clone()])?; + + self.update_latest_sidechain_header(signed_sidechain_block.block().header())?; info!("Importing block {} took {} ms", block_number, start_time.elapsed().as_millis()); diff --git a/sidechain/consensus/common/src/block_import_confirmation_handler.rs b/sidechain/consensus/common/src/block_import_confirmation_handler.rs index ec8a9f29da..bd013617c1 100644 --- a/sidechain/consensus/common/src/block_import_confirmation_handler.rs +++ b/sidechain/consensus/common/src/block_import_confirmation_handler.rs @@ -110,6 +110,7 @@ impl< shard: &ShardIdentifier, maybe_last_sidechain_block_confirmation: &Option, ) -> Result<()> { + // todo: cache last sidechain block header for use with direct rpc api if header.block_number() == header.next_finalization_block_number() { let call = self .metadata_repository diff --git a/sidechain/consensus/common/src/test/mocks/block_importer_mock.rs b/sidechain/consensus/common/src/test/mocks/block_importer_mock.rs index b03d00cfbe..2dcb4659cd 100644 --- a/sidechain/consensus/common/src/test/mocks/block_importer_mock.rs +++ b/sidechain/consensus/common/src/test/mocks/block_importer_mock.rs @@ -21,7 +21,10 @@ use itp_sgx_crypto::aes::Aes; use itp_sgx_externalities::SgxExternalities; use itp_test::mock::onchain_mock::OnchainMock; use itp_types::H256; -use its_primitives::traits::{ShardIdentifierFor, SignedBlock as SignedSidechainBlockTrait}; +use its_primitives::traits::{ + Block, Block as SidechainBlockTrait, ShardIdentifierFor, + SignedBlock as SignedSidechainBlockTrait, +}; use sp_core::Pair; use sp_runtime::traits::Block as ParentchainBlockTrait; use std::{collections::VecDeque, sync::RwLock}; @@ -114,6 +117,13 @@ where todo!() } + fn update_latest_sidechain_header( + &self, + head: &<::Block as SidechainBlockTrait>::HeaderType, + ) -> core::result::Result<(), Error> { + todo!() + } + fn verify_import( &self, _shard: &ShardIdentifierFor,