diff --git a/Cargo.lock b/Cargo.lock index b9f8ddaa5a..5d207756d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2704,7 +2704,7 @@ dependencies = [ [[package]] name = "integritee-cli" -version = "0.12.1" +version = "0.12.2" dependencies = [ "array-bytes 6.1.0", "base58", @@ -2796,7 +2796,7 @@ dependencies = [ [[package]] name = "integritee-service" -version = "0.12.1" +version = "0.12.2" dependencies = [ "anyhow", "async-trait", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b9528730c0..bbbbc3a216 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "integritee-cli" -version = "0.12.1" +version = "0.12.2" authors = ["Integritee AG "] edition = "2021" diff --git a/core-primitives/enclave-api/ffi/src/lib.rs b/core-primitives/enclave-api/ffi/src/lib.rs index 841d503d4d..520bfb3d00 100644 --- a/core-primitives/enclave-api/ffi/src/lib.rs +++ b/core-primitives/enclave-api/ffi/src/lib.rs @@ -67,6 +67,26 @@ extern "C" { parentchain_id_size: u32, ) -> sgx_status_t; + pub fn init_shard_creation_parentchain_header( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + shard: *const u8, + shard_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + header: *const u8, + header_size: u32, + ) -> sgx_status_t; + + pub fn get_shard_creation_header( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + shard: *const u8, + shard_size: u32, + creation: *mut u8, + creation_size: u32, + ) -> sgx_status_t; + pub fn execute_trusted_calls(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; pub fn sync_parentchain( @@ -80,7 +100,7 @@ extern "C" { events_proofs_size: usize, parentchain_id: *const u8, parentchain_id_size: u32, - is_syncing: c_int, + immediate_import: c_int, ) -> sgx_status_t; pub fn set_nonce( diff --git a/core-primitives/enclave-api/src/enclave_base.rs b/core-primitives/enclave-api/src/enclave_base.rs index 4e2f6e3791..4ebbfca6b6 100644 --- a/core-primitives/enclave-api/src/enclave_base.rs +++ b/core-primitives/enclave-api/src/enclave_base.rs @@ -20,7 +20,7 @@ use crate::EnclaveResult; use codec::Decode; use core::fmt::Debug; use itc_parentchain::primitives::{ParentchainId, ParentchainInitParams}; -use itp_types::ShardIdentifier; +use itp_types::{parentchain::Header, ShardIdentifier}; use sgx_crypto_helper::rsa3072::Rsa3072PubKey; use sp_core::ed25519; use teerex_primitives::EnclaveFingerprint; @@ -57,6 +57,19 @@ pub trait EnclaveBase: Send + Sync + 'static { parentchain_id: &ParentchainId, ) -> EnclaveResult<()>; + /// Initialize parentchain checkpoint after which invocations will be processed + fn init_shard_creation_parentchain_header( + &self, + shard: &ShardIdentifier, + parentchain_id: &ParentchainId, + header: &Header, + ) -> EnclaveResult<()>; + + fn get_shard_creation_header( + &self, + shard: &ShardIdentifier, + ) -> EnclaveResult<(ParentchainId, Header)>; + fn set_nonce(&self, nonce: u32, parentchain_id: ParentchainId) -> EnclaveResult<()>; fn set_node_metadata( @@ -88,7 +101,7 @@ mod impl_ffi { use itp_settings::worker::{ HEADER_MAX_SIZE, MR_ENCLAVE_SIZE, SHIELDING_KEY_SIZE, SIGNING_KEY_SIZE, }; - use itp_types::ShardIdentifier; + use itp_types::{parentchain::Header, ShardIdentifier}; use log::*; use sgx_crypto_helper::rsa3072::Rsa3072PubKey; use sgx_types::*; @@ -208,6 +221,61 @@ mod impl_ffi { Ok(()) } + fn init_shard_creation_parentchain_header( + &self, + shard: &ShardIdentifier, + parentchain_id: &ParentchainId, + header: &Header, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let parentchain_id_enc = parentchain_id.encode(); + let header_bytes = header.encode(); + let shard_bytes = shard.encode(); + let result = unsafe { + ffi::init_shard_creation_parentchain_header( + self.eid, + &mut retval, + shard_bytes.as_ptr(), + shard_bytes.len() as u32, + parentchain_id_enc.as_ptr(), + parentchain_id_enc.len() as u32, + header_bytes.as_ptr(), + header_bytes.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn get_shard_creation_header( + &self, + shard: &ShardIdentifier, + ) -> EnclaveResult<(ParentchainId, Header)> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut creation = [0u8; HEADER_MAX_SIZE + std::mem::size_of::()]; + let shard_bytes = shard.encode(); + + let result = unsafe { + ffi::get_shard_creation_header( + self.eid, + &mut retval, + shard_bytes.as_ptr(), + shard_bytes.len() as u32, + creation.as_mut_ptr(), + creation.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + let (creation_parentchain_id, creation_header): (ParentchainId, Header) = + Decode::decode(&mut creation.as_slice())?; + Ok((creation_parentchain_id, creation_header)) + } + fn set_nonce(&self, nonce: u32, parentchain_id: ParentchainId) -> EnclaveResult<()> { let mut retval = sgx_status_t::SGX_SUCCESS; diff --git a/core-primitives/enclave-api/src/sidechain.rs b/core-primitives/enclave-api/src/sidechain.rs index 364262a99c..d00b177684 100644 --- a/core-primitives/enclave-api/src/sidechain.rs +++ b/core-primitives/enclave-api/src/sidechain.rs @@ -32,7 +32,7 @@ pub trait Sidechain: Send + Sync + 'static { events: &[Vec], events_proofs: &[StorageProof], parentchain_id: &ParentchainId, - is_syncing: bool, + immediate_import: bool, ) -> EnclaveResult<()>; fn execute_trusted_calls(&self) -> EnclaveResult<()>; @@ -57,7 +57,7 @@ mod impl_ffi { events: &[Vec], events_proofs: &[StorageProof], parentchain_id: &ParentchainId, - is_syncing: bool, + immediate_import: bool, ) -> EnclaveResult<()> { let mut retval = sgx_status_t::SGX_SUCCESS; let blocks_enc = blocks.encode(); @@ -77,7 +77,7 @@ mod impl_ffi { events_proofs_enc.len(), parentchain_id_enc.as_ptr(), parentchain_id_enc.len() as u32, - is_syncing.into(), + immediate_import.into(), ) }; diff --git a/core-primitives/node-api/api-client-extensions/src/pallet_teerex.rs b/core-primitives/node-api/api-client-extensions/src/pallet_teerex.rs index 513a09f861..e8007729be 100644 --- a/core-primitives/node-api/api-client-extensions/src/pallet_teerex.rs +++ b/core-primitives/node-api/api-client-extensions/src/pallet_teerex.rs @@ -17,7 +17,9 @@ use crate::ApiResult; use itp_api_client_types::{traits::GetStorage, Api, Config, Request}; -use itp_types::{AccountId, IpfsHash, MultiEnclave, ShardIdentifier, ShardStatus}; +use itp_types::{ + AccountId, IpfsHash, MultiEnclave, ShardIdentifier, ShardSignerStatus, ShardStatus, +}; use log::error; pub const TEEREX: &str = "Teerex"; @@ -40,6 +42,11 @@ pub trait PalletTeerexApi { shard: &ShardIdentifier, at_block: Option, ) -> ApiResult>>>; + fn shard_status( + &self, + shard: &ShardIdentifier, + at_block: Option, + ) -> ApiResult>>; fn latest_ipfs_hash( &self, shard: &ShardIdentifier, @@ -100,6 +107,14 @@ where ) } + fn shard_status( + &self, + shard: &ShardIdentifier, + at_block: Option, + ) -> ApiResult>> { + self.get_storage_map(ENCLAVE_BRIDGE, "ShardStatus", shard, at_block) + } + fn latest_ipfs_hash( &self, shard: &ShardIdentifier, diff --git a/core-primitives/stf-interface/src/lib.rs b/core-primitives/stf-interface/src/lib.rs index 39fb373e49..308019bb22 100644 --- a/core-primitives/stf-interface/src/lib.rs +++ b/core-primitives/stf-interface/src/lib.rs @@ -37,6 +37,7 @@ pub mod sudo_pallet; pub mod system_pallet; pub const SHARD_VAULT_KEY: &str = "ShardVaultPubKey"; +pub const SHARD_CREATION_HEADER_KEY: &str = "ShardCreationHeaderKey"; /// Interface to initialize a new state. pub trait InitState { diff --git a/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs b/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs index a58383bf05..080f158144 100644 --- a/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs +++ b/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs @@ -51,9 +51,9 @@ where &self, blocks: Vec, events: Vec>, - _is_syncing: bool, + _immediate_import: bool, ) -> Result<()> { - // _is_syncing does not matter for the immediate dispatcher, behavoiur is the same. Immediate block import. + // _immediate_import does not matter for the immediate dispatcher, behavoiur is the same. Immediate block import. debug!("Importing {} parentchain blocks", blocks.len()); self.block_importer.import_parentchain_blocks(blocks, events)?; diff --git a/core/parentchain/block-import-dispatcher/src/lib.rs b/core/parentchain/block-import-dispatcher/src/lib.rs index 2385075644..34e94523d0 100644 --- a/core/parentchain/block-import-dispatcher/src/lib.rs +++ b/core/parentchain/block-import-dispatcher/src/lib.rs @@ -49,7 +49,7 @@ pub trait DispatchBlockImport { &self, blocks: Vec, events: Vec>, - is_syncing: bool, + immediate_import: bool, ) -> Result<()>; } @@ -105,16 +105,16 @@ where &self, blocks: Vec, events: Vec>, - is_syncing: bool, + immediate_import: bool, ) -> Result<()> { match self { BlockImportDispatcher::TriggeredDispatcher(dispatcher) => { log::trace!("TRIGGERED DISPATCHER MATCH"); - dispatcher.dispatch_import(blocks, events, is_syncing) + dispatcher.dispatch_import(blocks, events, immediate_import) }, BlockImportDispatcher::ImmediateDispatcher(dispatcher) => { log::trace!("IMMEDIATE DISPATCHER MATCH"); - dispatcher.dispatch_import(blocks, events, is_syncing) + dispatcher.dispatch_import(blocks, events, immediate_import) }, BlockImportDispatcher::EmptyDispatcher => { log::trace!("EMPTY DISPATCHER DISPATCHER MATCH"); diff --git a/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs b/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs index 77812331b8..712fcc724d 100644 --- a/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs +++ b/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs @@ -100,7 +100,7 @@ where &self, blocks: Vec, events: Vec, - is_syncing: bool, + immediate_import: bool, ) -> Result<()> { let parentchain_id = self.block_importer.parentchain_id(); trace!( @@ -109,7 +109,7 @@ where blocks.len(), events.len() ); - if is_syncing { + if immediate_import { trace!( "[{:?}] Triggered is in sync mode, immediately importing blocks and events", parentchain_id diff --git a/core/parentchain/block-importer/src/block_importer.rs b/core/parentchain/block-importer/src/block_importer.rs index 83118c216a..d93c19f38e 100644 --- a/core/parentchain/block-importer/src/block_importer.rs +++ b/core/parentchain/block-importer/src/block_importer.rs @@ -18,6 +18,7 @@ //! Imports parentchain blocks and executes any indirect calls found in the extrinsics. use crate::{error::Result, ImportParentchainBlocks}; + use ita_stf::ParentchainHeader; use itc_parentchain_indirect_calls_executor::ExecuteIndirectCalls; use itc_parentchain_light_client::{ @@ -26,7 +27,7 @@ use itc_parentchain_light_client::{ use itp_extrinsics_factory::CreateExtrinsics; use itp_stf_executor::traits::StfUpdateState; use itp_types::{ - parentchain::{IdentifyParentchain, ParentchainId}, + parentchain::{Header, IdentifyParentchain, ParentchainId}, OpaqueCall, H256, }; use log::*; @@ -48,6 +49,7 @@ pub struct ParentchainBlockImporter< stf_executor: Arc, extrinsics_factory: Arc, pub indirect_calls_executor: Arc, + maybe_creation_header: Option
, _phantom: PhantomData, } @@ -71,12 +73,14 @@ impl< stf_executor: Arc, extrinsics_factory: Arc, indirect_calls_executor: Arc, + maybe_creation_header: Option
, ) -> Self { ParentchainBlockImporter { validator_accessor, stf_executor, extrinsics_factory, indirect_calls_executor, + maybe_creation_header, _phantom: Default::default(), } } @@ -122,9 +126,23 @@ impl< .execute_mut_on_validator(|v| v.submit_block(&signed_block)) { error!("[{:?}] Header submission to light client failed: {:?}", id, e); + return Err(e.into()) } + // check if we can fast-sync + if id == ParentchainId::Integritee { + if let Some(ref creation_header) = self.maybe_creation_header { + if signed_block.block.header().number < creation_header.number { + trace!( + "fast-syncing block import, ignoring any invocations before block {:}", + creation_header.number + ); + continue + } + } + } + let block = signed_block.block; // Perform state updates. if let Err(e) = self diff --git a/core/parentchain/parentchain-crate/src/primitives.rs b/core/parentchain/parentchain-crate/src/primitives.rs index 055672ea6e..7c63ae5455 100644 --- a/core/parentchain/parentchain-crate/src/primitives.rs +++ b/core/parentchain/parentchain-crate/src/primitives.rs @@ -22,7 +22,9 @@ use codec::{Decode, Encode}; use sp_runtime::traits::Block; +use itp_types::ShardIdentifier; pub use itp_types::{parentchain::ParentchainId, Block as ParachainBlock, Block as SolochainBlock}; + pub type HeaderFor = ::Header; pub type SolochainHeader = HeaderFor; pub type ParachainHeader = HeaderFor; @@ -33,8 +35,8 @@ pub type ParachainParams = SimpleParams; /// Allows to use a single E-call for the initialization of different parentchain types. #[derive(Encode, Decode, Clone)] pub enum ParentchainInitParams { - Solochain { id: ParentchainId, params: SolochainParams }, - Parachain { id: ParentchainId, params: ParachainParams }, + Solochain { id: ParentchainId, shard: ShardIdentifier, params: SolochainParams }, + Parachain { id: ParentchainId, shard: ShardIdentifier, params: ParachainParams }, } impl ParentchainInitParams { @@ -46,14 +48,14 @@ impl ParentchainInitParams { } } -impl From<(ParentchainId, SolochainParams)> for ParentchainInitParams { - fn from(value: (ParentchainId, SolochainParams)) -> Self { - Self::Solochain { id: value.0, params: value.1 } +impl From<(ParentchainId, ShardIdentifier, SolochainParams)> for ParentchainInitParams { + fn from(value: (ParentchainId, ShardIdentifier, SolochainParams)) -> Self { + Self::Solochain { id: value.0, shard: value.1, params: value.2 } } } -impl From<(ParentchainId, ParachainParams)> for ParentchainInitParams { - fn from(value: (ParentchainId, ParachainParams)) -> Self { - Self::Parachain { id: value.0, params: value.1 } +impl From<(ParentchainId, ShardIdentifier, ParachainParams)> for ParentchainInitParams { + fn from(value: (ParentchainId, ShardIdentifier, ParachainParams)) -> Self { + Self::Parachain { id: value.0, shard: value.1, params: value.2 } } } diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 86234e8e4a..3cb273b776 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -72,7 +72,7 @@ services: interval: 10s timeout: 10s retries: 25 - command: "--clean-reset --data-dir /tmp/worker2 --ws-external -M integritee-worker-2 -T wss://integritee-worker-2 -u ws://integritee-node -U ws://integritee-worker-2 -P 2012 -w 2102 -p 9912 -h 4646 run --dev --request-state ${ADDITIONAL_RUNTIME_FLAGS}" + command: "--clean-reset --data-dir /tmp/worker2 --ws-external -M integritee-worker-2 -T wss://integritee-worker-2 -u ws://integritee-node -U ws://integritee-worker-2 -P 2012 -w 2102 -p 9912 -h 4646 run --dev ${ADDITIONAL_RUNTIME_FLAGS}" restart: "no" networks: integritee-test-network: diff --git a/enclave-runtime/Cargo.lock b/enclave-runtime/Cargo.lock index e24303f22d..3e8a023ea9 100644 --- a/enclave-runtime/Cargo.lock +++ b/enclave-runtime/Cargo.lock @@ -771,7 +771,7 @@ dependencies = [ [[package]] name = "enclave-runtime" -version = "0.12.1" +version = "0.12.2" dependencies = [ "array-bytes 6.1.0", "cid", @@ -789,6 +789,7 @@ dependencies = [ "itc-offchain-worker-executor", "itc-parentchain", "itc-parentchain-block-import-dispatcher", + "itc-parentchain-block-importer", "itc-parentchain-test", "itc-tls-websocket-server", "itp-attestation-handler", diff --git a/enclave-runtime/Cargo.toml b/enclave-runtime/Cargo.toml index 3596e44168..ab93bbd646 100644 --- a/enclave-runtime/Cargo.toml +++ b/enclave-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "enclave-runtime" -version = "0.12.1" +version = "0.12.2" authors = ["Integritee AG "] edition = "2021" @@ -98,6 +98,7 @@ itc-direct-rpc-server = { path = "../core/direct-rpc-server", default-features = itc-offchain-worker-executor = { path = "../core/offchain-worker-executor", default-features = false, features = ["sgx"] } itc-parentchain = { path = "../core/parentchain/parentchain-crate", default-features = false, features = ["sgx"] } itc-parentchain-block-import-dispatcher = { path = "../core/parentchain/block-import-dispatcher", default-features = false, features = ["sgx"] } +itc-parentchain-block-importer = { path = "../core/parentchain/block-importer", default-features = false, features = ["sgx"] } itc-parentchain-test = { path = "../core/parentchain/test", default-features = false } itc-tls-websocket-server = { path = "../core/tls-websocket-server", default-features = false, features = ["sgx"] } itp-attestation-handler = { path = "../core-primitives/attestation-handler", default-features = false, features = ["sgx"] } diff --git a/enclave-runtime/Enclave.edl b/enclave-runtime/Enclave.edl index ddb26d312a..0c80a379e0 100644 --- a/enclave-runtime/Enclave.edl +++ b/enclave-runtime/Enclave.edl @@ -63,6 +63,16 @@ enclave { [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size ); + public sgx_status_t init_shard_creation_parentchain_header( + [in, size=shard_size] uint8_t* shard, uint32_t shard_size, + [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size, + [in, size=header_size] uint8_t* header, uint32_t header_size + ); + + public sgx_status_t get_shard_creation_header( + [in, size=shard_size] uint8_t* shard, uint32_t shard_size, + [out, size=creation_size] uint8_t* creation, uint32_t creation_size); + public sgx_status_t execute_trusted_calls(); public sgx_status_t sync_parentchain( @@ -70,7 +80,7 @@ enclave { [in, size=events_size] uint8_t* events, size_t events_size, [in, size=events_proofs_size] uint8_t* events_proofs, size_t events_proofs_size, [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size, - int is_syncing + int immediate_import ); public sgx_status_t set_nonce( diff --git a/enclave-runtime/src/initialization/parentchain/common.rs b/enclave-runtime/src/initialization/parentchain/common.rs index 8e19c24d2f..a1f44edc7c 100644 --- a/enclave-runtime/src/initialization/parentchain/common.rs +++ b/enclave-runtime/src/initialization/parentchain/common.rs @@ -46,6 +46,7 @@ use crate::{ use itp_component_container::ComponentGetter; use itp_nonce_cache::NonceCache; use itp_sgx_crypto::key_repository::AccessKey; +use itp_types::parentchain::Header; use log::*; use sp_core::H256; use std::sync::Arc; @@ -55,6 +56,7 @@ pub(crate) fn create_integritee_parentchain_block_importer( stf_executor: Arc, extrinsics_factory: Arc, node_metadata_repository: Arc, + maybe_creation_header: Option
, ) -> Result { let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; @@ -78,6 +80,7 @@ pub(crate) fn create_integritee_parentchain_block_importer( stf_executor, extrinsics_factory, indirect_calls_executor, + maybe_creation_header, )) } @@ -109,6 +112,7 @@ pub(crate) fn create_target_a_parentchain_block_importer( stf_executor, extrinsics_factory, indirect_calls_executor, + None, )) } @@ -140,6 +144,7 @@ pub(crate) fn create_target_b_parentchain_block_importer( stf_executor, extrinsics_factory, indirect_calls_executor, + None, )) } diff --git a/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs b/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs index f13961aa8a..db20703229 100644 --- a/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs +++ b/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs @@ -40,7 +40,7 @@ use itp_types::parentchain::ParentchainId; use std::{path::PathBuf, sync::Arc}; pub use itc_parentchain::primitives::{ParachainBlock, ParachainHeader, ParachainParams}; - +use itp_types::parentchain::Header; #[derive(Clone)] pub struct IntegriteeParachainHandler { pub genesis_header: ParachainHeader, @@ -55,6 +55,7 @@ impl IntegriteeParachainHandler { pub fn init( _base_path: PathBuf, params: ParachainParams, + maybe_creation_header: Option
, ) -> Result { let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; @@ -91,6 +92,7 @@ impl IntegriteeParachainHandler { stf_executor.clone(), extrinsics_factory.clone(), node_metadata_repository.clone(), + maybe_creation_header, )?; let import_dispatcher = match WorkerModeProvider::worker_mode() { diff --git a/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs b/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs index d84624f0e6..77fe5cec54 100644 --- a/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs +++ b/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs @@ -36,7 +36,7 @@ use crate::{ use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; use itp_component_container::ComponentGetter; use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; -use itp_types::parentchain::ParentchainId; +use itp_types::parentchain::{Header, ParentchainId}; use std::{path::PathBuf, sync::Arc}; pub use itc_parentchain::primitives::{SolochainBlock, SolochainHeader, SolochainParams}; @@ -54,6 +54,7 @@ impl IntegriteeSolochainHandler { pub fn init( _base_path: PathBuf, params: SolochainParams, + maybe_creation_header: Option
, ) -> Result { let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; @@ -90,6 +91,7 @@ impl IntegriteeSolochainHandler { stf_executor.clone(), extrinsics_factory.clone(), node_metadata_repository.clone(), + maybe_creation_header, )?; let import_dispatcher = match WorkerModeProvider::worker_mode() { diff --git a/enclave-runtime/src/initialization/parentchain/mod.rs b/enclave-runtime/src/initialization/parentchain/mod.rs index a2338009ad..f2a730b2cb 100644 --- a/enclave-runtime/src/initialization/parentchain/mod.rs +++ b/enclave-runtime/src/initialization/parentchain/mod.rs @@ -17,6 +17,7 @@ use crate::{ error::Result, + get_shard_creation_parentchain_header_internal, initialization::{ global_components::{ GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT, @@ -43,6 +44,7 @@ use itc_parentchain::{ }; use itp_component_container::ComponentInitializer; use itp_settings::worker_mode::ProvideWorkerMode; +use itp_types::parentchain::Header; use std::{path::PathBuf, vec::Vec}; mod common; @@ -58,63 +60,100 @@ pub(crate) fn init_parentchain_components encoded_params: Vec, ) -> Result> { match ParentchainInitParams::decode(&mut encoded_params.as_slice())? { - ParentchainInitParams::Parachain { id, params } => match id { - ParentchainId::Integritee => { - let handler = - IntegriteeParachainHandler::init::(base_path, params)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, - ParentchainId::TargetA => { - let handler = - TargetAParachainHandler::init::(base_path, params)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, - ParentchainId::TargetB => { - let handler = - TargetBParachainHandler::init::(base_path, params)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, + ParentchainInitParams::Parachain { id, shard, params } => { + let maybe_creation: Option<(ParentchainId, Header)> = + get_shard_creation_parentchain_header_internal(shard).ok(); + let maybe_creation_header = if let Some((creation_parachain_id, creation_header)) = + maybe_creation + { + if creation_parachain_id != ParentchainId::Integritee { + unimplemented!("only Integritee parentchain is supported for shard creation"); + } + Some(creation_header) + } else { + None + }; + + // todo: query timestamp of creation header to give a creation reference to target_a/b as well in order to fast-sync + match id { + ParentchainId::Integritee => { + let handler = IntegriteeParachainHandler::init::( + base_path, + params, + maybe_creation_header, + )?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + ParentchainId::TargetA => { + let handler = + TargetAParachainHandler::init::(base_path, params)?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + ParentchainId::TargetB => { + let handler = + TargetBParachainHandler::init::(base_path, params)?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + } }, - ParentchainInitParams::Solochain { id, params } => match id { - ParentchainId::Integritee => { - let handler = - IntegriteeSolochainHandler::init::(base_path, params)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, - ParentchainId::TargetA => { - let handler = - TargetASolochainHandler::init::(base_path, params)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, - ParentchainId::TargetB => { - let handler = - TargetBSolochainHandler::init::(base_path, params)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, + ParentchainInitParams::Solochain { id, shard, params } => { + let maybe_creation: Option<(ParentchainId, Header)> = + get_shard_creation_parentchain_header_internal(shard).ok(); + let maybe_creation_header = if let Some((creation_parachain_id, creation_header)) = + maybe_creation + { + if creation_parachain_id != ParentchainId::Integritee { + unimplemented!("only Integritee parentchain is supported for shard creation"); + } + Some(creation_header) + } else { + None + }; + // todo: query timestamp of creation header to give a creation reference to target_a/b as well in order to fast-sync + match id { + ParentchainId::Integritee => { + let handler = IntegriteeSolochainHandler::init::( + base_path, + params, + maybe_creation_header, + )?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + ParentchainId::TargetA => { + let handler = + TargetASolochainHandler::init::(base_path, params)?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + ParentchainId::TargetB => { + let handler = + TargetBSolochainHandler::init::(base_path, params)?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + } }, } } diff --git a/enclave-runtime/src/lib.rs b/enclave-runtime/src/lib.rs index b71362e930..4a4c6f2d16 100644 --- a/enclave-runtime/src/lib.rs +++ b/enclave-runtime/src/lib.rs @@ -47,7 +47,7 @@ use crate::{ get_node_metadata_repository_from_target_b_solo_or_parachain, utf8_str_from_raw, DecodeRaw, }, }; -use codec::Decode; +use codec::{Decode, Encode}; use core::ffi::c_int; use itc_parentchain::{block_import_dispatcher::DispatchBlockImport, primitives::ParentchainId}; use itp_component_container::ComponentGetter; @@ -56,8 +56,10 @@ use itp_node_api::metadata::NodeMetadata; use itp_nonce_cache::{MutateNonce, Nonce}; use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}; use itp_sgx_crypto::key_repository::AccessPubkey; +use itp_stf_interface::SHARD_CREATION_HEADER_KEY; +use itp_stf_state_handler::{handle_state::HandleState, query_shard_state::QueryShardState}; use itp_storage::{StorageProof, StorageProofChecker}; -use itp_types::{ShardIdentifier, SignedBlock}; +use itp_types::{parentchain::Header, ShardIdentifier, SignedBlock}; use itp_utils::write_slice_and_whitespace_pad; use log::*; use once_cell::sync::OnceCell; @@ -423,6 +425,121 @@ pub unsafe extern "C" fn init_shard(shard: *const u8, shard_size: u32) -> sgx_st sgx_status_t::SGX_SUCCESS } +#[no_mangle] +pub unsafe extern "C" fn init_shard_creation_parentchain_header( + shard: *const u8, + shard_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + header: *const u8, + header_size: u32, +) -> sgx_status_t { + let shard_identifier = + ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize)); + let header = match Header::decode(&mut slice::from_raw_parts(header, header_size as usize)) { + Ok(hdr) => hdr, + Err(e) => { + error!("Could not decode header: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + let parentchain_id = + match ParentchainId::decode_raw(parentchain_id, parentchain_id_size as usize) { + Ok(id) => id, + Err(e) => { + error!("Could not decode parentchain id: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + if let Err(e) = + init_shard_creation_parentchain_header_internal(shard_identifier, parentchain_id, header) + { + error!( + "Failed to initialize first relevant parentchain header [{:?}]: {:?}", + parentchain_id, e + ); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + sgx_status_t::SGX_SUCCESS +} + +fn init_shard_creation_parentchain_header_internal( + shard: ShardIdentifier, + parentchain_id: ParentchainId, + header: Header, +) -> Result<()> { + if let Ok((id, _hdr)) = get_shard_creation_parentchain_header_internal(shard) { + error!("first relevant parentchain header has been previously initialized. cannot change: {:?}", id); + return Err(Error::Other( + "first relevant parentchain header has been previously initialized. cannot change" + .into(), + )) + } + debug!("initializing shard creation header: {:?}", parentchain_id); + + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + if !state_handler + .shard_exists(&shard) + .map_err(|_| Error::Other("get shard_exists failed".into()))? + { + return Err(Error::Other("shard not initialized".into())) + }; + + let (state_lock, mut state) = state_handler.load_for_mutation(&shard)?; + let value = (parentchain_id, header); + state.state.insert(SHARD_CREATION_HEADER_KEY.into(), value.encode()); + state_handler.write_after_mutation(state, state_lock, &shard)?; + Ok(()) +} + +/// reads the shard vault account id form state if it has been initialized previously +pub(crate) fn get_shard_creation_parentchain_header_internal( + shard: ShardIdentifier, +) -> Result<(ParentchainId, Header)> { + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + + state_handler + .execute_on_current(&shard, |state, _| { + state + .state + .get::>(&SHARD_CREATION_HEADER_KEY.into()) + .and_then(|v| Decode::decode(&mut v.clone().as_slice()).ok()) + })? + .ok_or_else(|| { + Error::Other( + "failed to fetch shard creation parentchain header. has it been initialized?" + .into(), + ) + }) +} + +/// reads the shard vault account id form state if it has been initialized previously +#[no_mangle] +pub unsafe extern "C" fn get_shard_creation_header( + shard: *const u8, + shard_size: u32, + creation: *mut u8, + creation_size: u32, +) -> sgx_status_t { + let shard = ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize)); + + let shard_creation = match get_shard_creation_parentchain_header_internal(shard) { + Ok(creation) => creation, + Err(e) => { + warn!("Failed to fetch creation header: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + trace!("fetched shard creation header from state: {:?}", shard_creation); + + let creation_slice = slice::from_raw_parts_mut(creation, creation_size as usize); + if let Err(e) = write_slice_and_whitespace_pad(creation_slice, shard_creation.encode()) { + return Error::BufferError(e).into() + }; + sgx_status_t::SGX_SUCCESS +} + #[no_mangle] pub unsafe extern "C" fn sync_parentchain( blocks_to_sync: *const u8, @@ -433,7 +550,7 @@ pub unsafe extern "C" fn sync_parentchain( events_proofs_to_sync_size: usize, parentchain_id: *const u8, parentchain_id_size: u32, - is_syncing: c_int, + immediate_import: c_int, ) -> sgx_status_t { if let Err(e) = sync_parentchain_internal( blocks_to_sync, @@ -444,7 +561,7 @@ pub unsafe extern "C" fn sync_parentchain( events_proofs_to_sync_size, parentchain_id, parentchain_id_size, - is_syncing == 1, + immediate_import == 1, ) { error!("Error synching parentchain: {:?}", e); } @@ -462,27 +579,28 @@ unsafe fn sync_parentchain_internal( events_proofs_to_sync_size: usize, parentchain_id: *const u8, parentchain_id_size: u32, - is_syncing: bool, + immediate_import: bool, ) -> Result<()> { let blocks_to_sync = Vec::::decode_raw(blocks_to_sync, blocks_to_sync_size)?; + let events_to_sync = Vec::>::decode_raw(events_to_sync, events_to_sync_size)?; let events_proofs_to_sync = Vec::::decode_raw(events_proofs_to_sync, events_proofs_to_sync_size)?; let parentchain_id = ParentchainId::decode_raw(parentchain_id, parentchain_id_size as usize)?; - let blocks_to_sync_merkle_roots: Vec = - blocks_to_sync.iter().map(|block| block.block.header.state_root).collect(); - - if let Err(e) = validate_events(&events_proofs_to_sync, &blocks_to_sync_merkle_roots) { - return e.into() + if !events_proofs_to_sync.is_empty() { + let blocks_to_sync_merkle_roots: Vec = + blocks_to_sync.iter().map(|block| block.block.header.state_root).collect(); + // fixme: vulnerability! https://github.com/integritee-network/worker/issues/1518 + if let Err(e) = validate_events(&events_proofs_to_sync, &blocks_to_sync_merkle_roots) { + return e.into() + } } - let events_to_sync = Vec::>::decode_raw(events_to_sync, events_to_sync_size)?; - dispatch_parentchain_blocks_for_import::( blocks_to_sync, events_to_sync, &parentchain_id, - is_syncing, + immediate_import, ) } @@ -498,7 +616,7 @@ fn dispatch_parentchain_blocks_for_import blocks_to_sync: Vec, events_to_sync: Vec>, id: &ParentchainId, - is_syncing: bool, + immediate_import: bool, ) -> Result<()> { if WorkerModeProvider::worker_mode() == WorkerMode::Teeracle { trace!("Not importing any parentchain blocks"); @@ -516,13 +634,13 @@ fn dispatch_parentchain_blocks_for_import handler.import_dispatcher.dispatch_import( blocks_to_sync, events_to_sync, - is_syncing, + immediate_import, )?; } else if let Ok(handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { handler.import_dispatcher.dispatch_import( blocks_to_sync, events_to_sync, - is_syncing, + immediate_import, )?; } else { return Err(Error::NoIntegriteeParentchainAssigned) @@ -533,13 +651,13 @@ fn dispatch_parentchain_blocks_for_import handler.import_dispatcher.dispatch_import( blocks_to_sync, events_to_sync, - is_syncing, + immediate_import, )?; } else if let Ok(handler) = GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.get() { handler.import_dispatcher.dispatch_import( blocks_to_sync, events_to_sync, - is_syncing, + immediate_import, )?; } else { return Err(Error::NoTargetAParentchainAssigned) @@ -550,13 +668,13 @@ fn dispatch_parentchain_blocks_for_import handler.import_dispatcher.dispatch_import( blocks_to_sync, events_to_sync, - is_syncing, + immediate_import, )?; } else if let Ok(handler) = GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.get() { handler.import_dispatcher.dispatch_import( blocks_to_sync, events_to_sync, - is_syncing, + immediate_import, )?; } else { return Err(Error::NoTargetBParentchainAssigned) diff --git a/enclave-runtime/src/tls_ra/tests.rs b/enclave-runtime/src/tls_ra/tests.rs index 5cdbd2a184..17870f412d 100644 --- a/enclave-runtime/src/tls_ra/tests.rs +++ b/enclave-runtime/src/tls_ra/tests.rs @@ -128,13 +128,14 @@ pub fn test_tls_ra_server_client_networking() { assert_eq!(*client_shielding_key.read().unwrap(), shielding_key_encoded); assert_eq!(*client_light_client_state.read().unwrap(), light_client_state_encoded); - // State and state-key are provisioned only in sidechain mode - if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { - assert_eq!(*client_state.read().unwrap(), state_encoded); - assert_eq!(*client_state_key.read().unwrap(), state_key_encoded); - } else { + // State and state-key are provisioned only in sidechain or OCW mode + if WorkerModeProvider::worker_mode() == WorkerMode::Teeracle { assert_eq!(*client_state.read().unwrap(), initial_client_state); assert_eq!(*client_state_key.read().unwrap(), initial_client_state_key); + } else { + // Sidechain or OffchainWorker + assert_eq!(*client_state.read().unwrap(), state_encoded); + assert_eq!(*client_state_key.read().unwrap(), state_key_encoded); } } diff --git a/enclave-runtime/src/tls_ra/tls_ra_server.rs b/enclave-runtime/src/tls_ra/tls_ra_server.rs index 33f72e9095..07d26df5b5 100644 --- a/enclave-runtime/src/tls_ra/tls_ra_server.rs +++ b/enclave-runtime/src/tls_ra/tls_ra_server.rs @@ -55,8 +55,8 @@ enum ProvisioningPayload { impl From for ProvisioningPayload { fn from(m: WorkerMode) -> Self { match m { - WorkerMode::OffChainWorker | WorkerMode::Teeracle => - ProvisioningPayload::ShieldingKeyAndLightClient, + WorkerMode::Teeracle => ProvisioningPayload::ShieldingKeyAndLightClient, + WorkerMode::OffChainWorker => ProvisioningPayload::Everything, WorkerMode::Sidechain => ProvisioningPayload::Everything, } } diff --git a/service/Cargo.toml b/service/Cargo.toml index b54bb5bba0..4597a31f91 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "integritee-service" -version = "0.12.1" +version = "0.12.2" authors = ["Integritee AG "] build = "build.rs" edition = "2021" diff --git a/service/src/cli.yml b/service/src/cli.yml index c4847ca27c..d820776f3a 100644 --- a/service/src/cli.yml +++ b/service/src/cli.yml @@ -124,10 +124,6 @@ subcommands: long: dev short: d help: Set this flag if running in development mode to bootstrap enclave account on parentchain via //Alice. - - request-state: - long: request-state - short: r - help: Run the worker and request key and state provisioning from another worker. - teeracle-interval: required: false long: teeracle-interval @@ -140,7 +136,7 @@ subcommands: help: Set the teeracle reregistration interval. Example of accepted syntax <5 seconds 15 minutes 2 hours 1 days> or short <5s15m2h1d> takes_value: true - request-state: - about: join a shard by requesting key provisioning from another worker + about: (DEPRECATED) join a shard by requesting key provisioning from another worker args: - shard: long: shard diff --git a/service/src/config.rs b/service/src/config.rs index 3d42180a07..2b298afdab 100644 --- a/service/src/config.rs +++ b/service/src/config.rs @@ -265,8 +265,6 @@ pub struct RunConfig { skip_ra: bool, /// Set this flag if running in development mode to bootstrap enclave account on parentchain via //Alice. dev: bool, - /// Request key and state provisioning from a peer worker. - request_state: bool, /// Shard identifier base58 encoded. Defines the shard that this worker operates on. Default is mrenclave. shard: Option, /// Optional teeracle update interval @@ -286,10 +284,6 @@ impl RunConfig { self.dev } - pub fn request_state(&self) -> bool { - self.request_state - } - pub fn shard(&self) -> Option<&str> { self.shard.as_deref() } @@ -319,7 +313,6 @@ impl From<&ArgMatches<'_>> for RunConfig { fn from(m: &ArgMatches<'_>) -> Self { let skip_ra = m.is_present("skip-ra"); let dev = m.is_present("dev"); - let request_state = m.is_present("request-state"); let shard = m.value_of("shard").map(|s| s.to_string()); let teeracle_update_interval = m.value_of("teeracle-interval").map(|i| { parse(i).unwrap_or_else(|e| panic!("teeracle-interval parsing error {:?}", e)) @@ -337,7 +330,6 @@ impl From<&ArgMatches<'_>> for RunConfig { Self { skip_ra, dev, - request_state, shard, teeracle_update_interval, reregister_teeracle_interval, @@ -464,7 +456,6 @@ mod test { let empty_args = ArgMatches::default(); let run_config = RunConfig::from(&empty_args); - assert_eq!(run_config.request_state, false); assert_eq!(run_config.dev, false); assert_eq!(run_config.skip_ra, false); assert!(run_config.shard.is_none()); @@ -477,7 +468,6 @@ mod test { let mut args = ArgMatches::default(); args.args = HashMap::from([ - ("request-state", Default::default()), ("dev", Default::default()), ("skip-ra", Default::default()), ("shard", Default::default()), @@ -489,7 +479,6 @@ mod test { let run_config = RunConfig::from(&args); - assert_eq!(run_config.request_state, true); assert_eq!(run_config.dev, true); assert_eq!(run_config.skip_ra, true); assert_eq!(run_config.shard.unwrap(), shard_identifier.to_string()); diff --git a/service/src/error.rs b/service/src/error.rs index c99f51e6fc..ec3738be11 100644 --- a/service/src/error.rs +++ b/service/src/error.rs @@ -16,7 +16,7 @@ use codec::Error as CodecError; use itp_node_api::api_client::ApiClientError; -use itp_types::ShardIdentifier; +use itp_types::{parentchain::Hash, ShardIdentifier}; pub type ServiceResult = Result; @@ -50,6 +50,8 @@ pub enum Error { MissingGenesisHeader, #[error("Could not find last finalized block of the parentchain")] MissingLastFinalizedBlock, + #[error("Could not find block in parentchain")] + UnknownBlockHeader(Hash), #[error("{0}")] Custom(Box), } diff --git a/service/src/main_impl.rs b/service/src/main_impl.rs index 37842603a3..1436576d7f 100644 --- a/service/src/main_impl.rs +++ b/service/src/main_impl.rs @@ -59,7 +59,7 @@ use substrate_api_client::{ SubscribeEvents, }; -use teerex_primitives::AnySigner; +use teerex_primitives::{AnySigner, MultiEnclave}; #[cfg(feature = "dcap")] use sgx_verify::extract_tcb_info_from_raw_dcap_quote; @@ -184,15 +184,6 @@ pub(crate) fn main() { let node_api = node_api_factory.create_api().expect("Failed to create parentchain node API"); - if run_config.request_state() { - sync_state::sync_state::<_, _, WorkerModeProvider>( - &node_api, - &shard, - enclave.as_ref(), - run_config.skip_ra(), - ); - } - start_worker::<_, _, _, _, WorkerModeProvider>( config, &shard, @@ -424,10 +415,15 @@ fn start_worker( } // ------------------------------------------------------------------------ - // Init parentchain specific stuff. Needed for parentchain communication. - - let (parentchain_handler, last_synced_header) = - init_parentchain(&enclave, &integritee_rpc_api, &tee_accountid, ParentchainId::Integritee); + // Init parentchain specific stuff. Needed early for parentchain communication. + let (integritee_parentchain_handler, integritee_last_synced_header_at_last_run) = + init_parentchain( + &enclave, + &integritee_rpc_api, + &tee_accountid, + ParentchainId::Integritee, + shard, + ); #[cfg(feature = "dcap")] register_collateral( @@ -496,18 +492,71 @@ fn start_worker( register_enclave_xt_header.number(), register_enclave_xt_header.hash() ); + // double-check + let my_enclave = integritee_rpc_api + .enclave(&tee_accountid, None) + .unwrap() + .expect("our enclave should be registered at this point"); + trace!("verified that our enclave is registered: {:?}", my_enclave); let we_are_primary_validateer = - we_are_primary_worker(&integritee_rpc_api, shard, &tee_accountid).unwrap(); - - if we_are_primary_validateer { - println!("[+] We are the primary worker"); - } else { - println!("[+] We are NOT the primary worker"); - } - + match integritee_rpc_api.primary_worker_for_shard(shard, None).unwrap() { + Some(primary_enclave) => match primary_enclave.instance_signer() { + AnySigner::Known(MultiSigner::Ed25519(primary)) => + if primary.encode() == tee_accountid.encode() { + println!("We are primary worker on this shard and we have been previously running."); + true + } else { + println!( + "We are NOT primary worker. The primary worker is {}.", + primary.to_ss58check(), + ); + info!("The primary worker enclave is {:?}", primary_enclave); + if enclave.get_shard_creation_header(shard).is_err() { + //obtain provisioning from last active worker + info!("my state doesn't know the creation header of the shard. will request provisioning"); + sync_state::sync_state::<_, _, WorkerModeProvider>( + &integritee_rpc_api, + &shard, + enclave.as_ref(), + skip_ra, + ); + } + false + }, + _ => { + panic!( + "the primary worker for shard {:?} has unknown signer type: {:?}", + shard, primary_enclave + ); + }, + }, + None => { + println!("We are the primary worker on this shard and the shard is untouched. Will initialize it"); + enclave + .init_shard_creation_parentchain_header( + shard, + &ParentchainId::Integritee, + ®ister_enclave_xt_header, + ) + .unwrap(); + true + }, + }; + debug!("getting shard creation: {:?}", enclave.get_shard_creation_header(shard)); initialization_handler.registered_on_parentchain(); + // re-initialize integritee parentchain to make sure to use creation_header for fast-sync + // todo: this should only be necessary if we are the primary validateer running for the first time + let (integritee_parentchain_handler, integritee_last_synced_header_at_last_run) = + init_parentchain( + &enclave, + &integritee_rpc_api, + &tee_accountid, + ParentchainId::Integritee, + shard, + ); + match WorkerModeProvider::worker_mode() { WorkerMode::Teeracle => { // ------------------------------------------------------------------------ @@ -530,10 +579,19 @@ fn start_worker( println!("*** [+] Finished initializing light client, syncing parentchain..."); // Syncing all parentchain blocks, this might take a while.. - let last_synced_header = - parentchain_handler.sync_parentchain(last_synced_header, true).unwrap(); + let last_synced_header = integritee_parentchain_handler + .sync_parentchain_until_latest_finalized( + integritee_last_synced_header_at_last_run, + *shard, + true, + ) + .unwrap(); - start_parentchain_header_subscription_thread(parentchain_handler, last_synced_header); + start_parentchain_header_subscription_thread( + integritee_parentchain_handler, + last_synced_header, + *shard, + ); info!("skipping shard vault check because not yet supported for offchain worker"); }, @@ -546,13 +604,18 @@ fn start_worker( enclave.clone(), ®ister_enclave_xt_header, we_are_primary_validateer, - parentchain_handler.clone(), + integritee_parentchain_handler.clone(), sidechain_storage, - &last_synced_header, + &integritee_last_synced_header_at_last_run, + *shard, ) .unwrap(); - start_parentchain_header_subscription_thread(parentchain_handler, last_synced_header); + start_parentchain_header_subscription_thread( + integritee_parentchain_handler, + last_synced_header, + *shard, + ); init_provided_shard_vault(shard, &enclave, we_are_primary_validateer); @@ -644,7 +707,7 @@ fn init_target_parentchain( }); let (parentchain_handler, last_synched_header) = - init_parentchain(enclave, &node_api, tee_account_id, parentchain_id); + init_parentchain(enclave, &node_api, tee_account_id, parentchain_id, shard); if WorkerModeProvider::worker_mode() != WorkerMode::Teeracle { println!( @@ -653,10 +716,15 @@ fn init_target_parentchain( ); // Syncing all parentchain blocks, this might take a while.. - let last_synched_header = - parentchain_handler.sync_parentchain(last_synched_header, true).unwrap(); + let last_synched_header = parentchain_handler + .sync_parentchain_until_latest_finalized(last_synched_header, *shard, true) + .unwrap(); - start_parentchain_header_subscription_thread(parentchain_handler, last_synched_header) + start_parentchain_header_subscription_thread( + parentchain_handler, + last_synched_header, + *shard, + ) } println!("[{:?}] initializing proxied shard vault account now", parentchain_id); enclave.init_proxied_shard_vault(shard, &parentchain_id).unwrap(); @@ -681,6 +749,7 @@ fn init_parentchain( node_api: &ParentchainApi, tee_account_id: &AccountId32, parentchain_id: ParentchainId, + shard: &ShardIdentifier, ) -> (Arc>, Header) where E: EnclaveBase + Sidechain, @@ -690,6 +759,7 @@ where node_api.clone(), enclave.clone(), parentchain_id, + *shard, ) .unwrap(), ); @@ -887,13 +957,14 @@ fn send_extrinsic( fn start_parentchain_header_subscription_thread( parentchain_handler: Arc>, last_synced_header: Header, + shard: ShardIdentifier, ) { let parentchain_id = *parentchain_handler.parentchain_id(); thread::Builder::new() .name(format!("{:?}_parentchain_sync_loop", parentchain_id)) .spawn(move || { if let Err(e) = - subscribe_to_parentchain_new_headers(parentchain_handler, last_synced_header) + subscribe_to_parentchain_new_headers(parentchain_handler, last_synced_header, shard) { error!( "[{:?}] parentchain block syncing terminated with a failure: {:?}", @@ -910,6 +981,7 @@ fn start_parentchain_header_subscription_thread( fn subscribe_to_parentchain_new_headers( parentchain_handler: Arc>, mut last_synced_header: Header, + shard: ShardIdentifier, ) -> Result<(), Error> { // TODO: this should be implemented by parentchain_handler directly, and not via // exposed parentchain_api @@ -929,7 +1001,11 @@ fn subscribe_to_parentchain_new_headers( parentchain_id, new_header.number ); - last_synced_header = parentchain_handler.sync_parentchain(last_synced_header, false)?; + last_synced_header = parentchain_handler.sync_parentchain_until_latest_finalized( + last_synced_header, + shard, + false, + )?; } } @@ -939,37 +1015,3 @@ fn enclave_account(enclave_api: &E) -> AccountId32 { trace!("[+] Got ed25519 account of TEE = {}", tee_public.to_ss58check()); AccountId32::from(*tee_public.as_array_ref()) } - -/// Checks if we are the first validateer to register on the parentchain. -fn we_are_primary_worker( - node_api: &ParentchainApi, - shard: &ShardIdentifier, - enclave_account: &AccountId32, -) -> Result { - // are we registered? else fail. - node_api - .enclave(enclave_account, None)? - .expect("our enclave should be registered at this point"); - trace!("our enclave is registered"); - match node_api.primary_worker_for_shard(shard, None).unwrap() { - Some(enclave) => - match enclave.instance_signer() { - AnySigner::Known(MultiSigner::Ed25519(primary)) => - if primary.encode() == enclave_account.encode() { - debug!("We are primary worker on this shard adn we have been previously running."); - Ok(true) - } else { - debug!("The primary worker is {}", primary.to_ss58check()); - Ok(false) - }, - _ => { - warn!("the primary worker is of unknown type"); - Ok(false) - }, - }, - None => { - debug!("We are the primary worker on this shard and the shard is untouched"); - Ok(true) - }, - } -} diff --git a/service/src/parentchain_handler.rs b/service/src/parentchain_handler.rs index 41f843fe0e..dc1841f9d1 100644 --- a/service/src/parentchain_handler.rs +++ b/service/src/parentchain_handler.rs @@ -26,12 +26,16 @@ use itp_api_client_types::ParentchainApi; use itp_enclave_api::{enclave_base::EnclaveBase, sidechain::Sidechain}; use itp_node_api::api_client::ChainApi; use itp_storage::StorageProof; +use itp_types::ShardIdentifier; use log::*; use my_node_runtime::Header; use sp_consensus_grandpa::VersionedAuthorityList; use sp_runtime::traits::Header as HeaderTrait; use std::{cmp::min, sync::Arc}; -use substrate_api_client::ac_primitives::{Block, Header as HeaderT}; +use substrate_api_client::{ + ac_primitives::{Block, Header as HeaderT}, + GetChainInfo, +}; const BLOCK_SYNC_BATCH_SIZE: u32 = 1000; @@ -42,18 +46,20 @@ pub trait HandleParentchain { /// Fetches the parentchain blocks to sync from the parentchain and feeds them to the enclave. /// Returns the latest synced block header. - fn sync_parentchain( + fn sync_parentchain_until_latest_finalized( &self, last_synced_header: Header, - is_syncing: bool, + shard: ShardIdentifier, + immediate_import: bool, ) -> ServiceResult
; /// Syncs and directly imports parentchain blocks from the latest synced header - /// until the specified until_header. - fn sync_and_import_parentchain_until( + /// until at least the specified until_header. + fn await_sync_and_import_parentchain_until_at_least( &self, last_synced_header: &Header, until_header: &Header, + shard: ShardIdentifier, ) -> ServiceResult
; } @@ -83,6 +89,7 @@ where parentchain_api: ParentchainApi, enclave_api: Arc, id: ParentchainId, + shard: ShardIdentifier, ) -> ServiceResult { let genesis_hash = parentchain_api.get_genesis_hash()?; let genesis_header = @@ -100,6 +107,7 @@ where ( id, + shard, GrandpaParams::new( // #TODO: #1451: clean up type hacks Header::decode(&mut genesis_header.encode().as_slice())?, @@ -111,6 +119,7 @@ where } else { ( id, + shard, SimpleParams::new( // #TODO: #1451: clean up type hacks Header::decode(&mut genesis_header.encode().as_slice())?, @@ -141,10 +150,11 @@ where .init_parentchain_components(self.parentchain_init_params.clone())?) } - fn sync_parentchain( + fn sync_parentchain_until_latest_finalized( &self, last_synced_header: Header, - is_syncing: bool, + shard: ShardIdentifier, + immediate_import: bool, ) -> ServiceResult
{ let id = self.parentchain_id(); trace!("[{:?}] Getting current head", id); @@ -154,10 +164,31 @@ where .ok_or(Error::MissingLastFinalizedBlock)?; let curr_block_number = curr_block.block.header().number(); + // verify that the last_synced_header is indeed a block from this chain + self.parentchain_api + .get_block(Some(last_synced_header.hash()))? + .ok_or_else(|| Error::UnknownBlockHeader(last_synced_header.hash()))?; + info!( "[{:?}] Syncing blocks from {} to {}", id, last_synced_header.number, curr_block_number ); + let maybe_creation = self.enclave_api.get_shard_creation_header(&shard).ok(); + let maybe_creation_header = + if let Some((creation_parentchain_id, creation_header)) = maybe_creation { + trace!( + "shard_creation header is from {:?}: {:?}", + creation_parentchain_id, + creation_header + ); + if *id == creation_parentchain_id { + Some(creation_header) + } else { + None + } + } else { + None + }; let mut until_synced_header = last_synced_header; loop { @@ -165,33 +196,54 @@ where until_synced_header.number + 1, min(until_synced_header.number + BLOCK_SYNC_BATCH_SIZE, curr_block_number), )?; - info!("[{:?}] Found {} block(s) to sync", id, block_chunk_to_sync.len()); + info!("[{:?}] Found {} block(s) to sync in this chunk", id, block_chunk_to_sync.len()); if block_chunk_to_sync.is_empty() { return Ok(until_synced_header) } - let events_chunk_to_sync: Vec> = block_chunk_to_sync - .iter() - .map(|block| { - self.parentchain_api.get_events_for_block(Some(block.block.header.hash())) - }) - .collect::, _>>()?; - - info!("[{:?}] Found {} event vector(s) to sync", id, events_chunk_to_sync.len()); - - let events_proofs_chunk_to_sync: Vec = block_chunk_to_sync - .iter() - .map(|block| { - self.parentchain_api.get_events_value_proof(Some(block.block.header.hash())) - }) - .collect::, _>>()?; + let skip_invocations = if let Some(creation_header) = maybe_creation_header.clone() { + let max_blocknumber_in_chunk = + block_chunk_to_sync.last().map_or_else(|| 0, |b| b.block.header.number()); + if max_blocknumber_in_chunk < *creation_header.number() { + trace!("skipping invocations for fast-sync for blocks older than shard creation: {} < {}", max_blocknumber_in_chunk, creation_header.number()); + true + } else { + false + } + } else { + false + }; + + let events_chunk_to_sync: Vec> = if skip_invocations { + vec![] + } else { + let evs = block_chunk_to_sync + .iter() + .map(|block| { + self.parentchain_api.get_events_for_block(Some(block.block.header.hash())) + }) + .collect::, _>>()?; + info!("[{:?}] Found {} event vector(s) to sync in this chunk", id, evs.len()); + evs + }; + + let events_proofs_chunk_to_sync: Vec = if skip_invocations { + vec![] + } else { + block_chunk_to_sync + .iter() + .map(|block| { + self.parentchain_api.get_events_value_proof(Some(block.block.header.hash())) + }) + .collect::, _>>()? + }; self.enclave_api.sync_parentchain( block_chunk_to_sync.as_slice(), events_chunk_to_sync.as_slice(), events_proofs_chunk_to_sync.as_slice(), self.parentchain_id(), - is_syncing, + immediate_import, )?; let api_client_until_synced_header = block_chunk_to_sync @@ -210,10 +262,11 @@ where } } - fn sync_and_import_parentchain_until( + fn await_sync_and_import_parentchain_until_at_least( &self, last_synced_header: &Header, until_header: &Header, + shard: ShardIdentifier, ) -> ServiceResult
{ let id = self.parentchain_id(); @@ -226,7 +279,8 @@ where let mut last_synced_header = last_synced_header.clone(); while last_synced_header.number() < until_header.number() { - last_synced_header = self.sync_parentchain(last_synced_header, true)?; + last_synced_header = + self.sync_parentchain_until_latest_finalized(last_synced_header, shard, true)?; println!("[{:?}] synced block number: #{}", id, last_synced_header.number); std::thread::sleep(std::time::Duration::from_secs(1)); } diff --git a/service/src/setup.rs b/service/src/setup.rs index 15250642bb..f45bab61c2 100644 --- a/service/src/setup.rs +++ b/service/src/setup.rs @@ -50,10 +50,10 @@ mod needs_enclave { println!("[+] Initialize the shard"); init_shard(enclave, shard_identifier); - println!("[+] Generate key files"); - generate_signing_key_file(enclave); - generate_shielding_key_file(enclave); - + let pubkey = enclave.get_ecc_signing_pubkey().unwrap(); + debug!("Enclave signing key (public) raw: {:?}", pubkey); + let pubkey = enclave.get_rsa_shielding_pubkey().unwrap(); + debug!("Enclave shielding key (public) raw (may be overwritten later): {:?}", pubkey); Ok(()) } diff --git a/service/src/sidechain_setup.rs b/service/src/sidechain_setup.rs index d13b4b5fb0..665c78bbe9 100644 --- a/service/src/sidechain_setup.rs +++ b/service/src/sidechain_setup.rs @@ -28,7 +28,7 @@ use itp_settings::{ files::{SIDECHAIN_PURGE_INTERVAL, SIDECHAIN_PURGE_LIMIT}, sidechain::SLOT_DURATION, }; -use itp_types::Header; +use itp_types::{Header, ShardIdentifier}; use its_consensus_slots::start_slot_worker; use its_primitives::types::block::SignedBlock as SignedSidechainBlock; use its_storage::{interface::FetchBlocks, start_sidechain_pruning_loop, BlockPruner}; @@ -61,6 +61,7 @@ pub(crate) fn sidechain_init_block_production, sidechain_storage: Arc, last_synced_header: &Header, + shard: ShardIdentifier, ) -> ServiceResult
where Enclave: EnclaveBase + Sidechain, @@ -75,9 +76,10 @@ where "We're the first validateer to be registered, syncing parentchain blocks until the one we have registered ourselves on." ); updated_header = - Some(parentchain_handler.sync_and_import_parentchain_until( + Some(parentchain_handler.await_sync_and_import_parentchain_until_at_least( last_synced_header, register_enclave_xt_header, + shard, )?); } diff --git a/service/src/sync_state.rs b/service/src/sync_state.rs index 6c70a5379e..ef53b2ec06 100644 --- a/service/src/sync_state.rs +++ b/service/src/sync_state.rs @@ -28,9 +28,12 @@ use itp_enclave_api::{ }; use itp_node_api::api_client::PalletTeerexApi; use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; -use itp_types::ShardIdentifier; +use itp_types::{parentchain::AccountId, ShardIdentifier}; +use log::info; use sgx_types::sgx_quote_sign_type_t; +use sp_runtime::MultiSigner; use std::string::String; +use teerex_primitives::AnySigner; pub(crate) fn sync_state< E: TlsRemoteAttestation + EnclaveBase + RemoteAttestation, @@ -42,13 +45,13 @@ pub(crate) fn sync_state< enclave_api: &E, skip_ra: bool, ) { - // FIXME: we now assume that keys are equal for all shards. let provider_url = match WorkerModeProvider::worker_mode() { - WorkerMode::Sidechain => - executor::block_on(get_author_url_of_last_finalized_sidechain_block(node_api, shard)) + WorkerMode::Sidechain | WorkerMode::OffChainWorker => + executor::block_on(get_enclave_url_of_last_active(node_api, enclave_api, shard)) + .expect("author of most recent shard update not found"), + WorkerMode::Teeracle => + executor::block_on(get_enclave_url_of_first_registered(node_api, enclave_api)) .expect("Author of last finalized sidechain block could not be found"), - _ => executor::block_on(get_enclave_url_of_first_registered(node_api, enclave_api)) - .expect("Author of last finalized sidechain block could not be found"), }; println!("Requesting state provisioning from worker at {}", &provider_url); @@ -82,20 +85,51 @@ async fn get_author_url_of_last_finalized_sidechain_block( node_api: &NodeApi, enclave_api: &EnclaveApi, ) -> Result { let self_mr_enclave = enclave_api.get_fingerprint()?; + let self_account = enclave_api.get_ecc_signing_pubkey()?; let first_enclave = node_api .all_enclaves(None)? .into_iter() + .filter(|e| e.instance_signer() != AnySigner::Known(MultiSigner::Ed25519(self_account))) .find(|e| e.fingerprint() == self_mr_enclave) .ok_or(Error::NoPeerWorkerFound)?; let worker_api_direct = DirectWorkerApi::new(String::from_utf8(first_enclave.instance_url().unwrap()).unwrap()); Ok(worker_api_direct.get_mu_ra_url()?) } + +/// Returns the url of the last active worker on our shard +async fn get_enclave_url_of_last_active( + node_api: &NodeApi, + enclave_api: &EnclaveApi, + shard: &ShardIdentifier, +) -> Result { + let self_account = enclave_api.get_ecc_signing_pubkey()?; + let shard_status = node_api + .shard_status(shard, None) + .expect("must be able to fetch shard status") + .expect("can only sync state for active shards"); + info!("fetching active peer. shard status: {:?}", shard_status); + let last_active_signer_status = shard_status + .iter() + .filter(|&s| s.signer != AccountId::from(self_account)) + .max_by_key(|&signer_status| signer_status.last_activity) + .expect("there has to be a most recently active peer") + .clone(); + info!("most recently active signer on this shard: {:?}", last_active_signer_status); + let provider_enclave = node_api + .enclave(&last_active_signer_status.signer, None) + .expect("must be able to fetch enclaves") + .expect("active peer must exist in registry"); + let worker_api_direct = DirectWorkerApi::new( + String::from_utf8(provider_enclave.instance_url().expect("provider must specify url")) + .unwrap(), + ); + Ok(worker_api_direct.get_mu_ra_url()?) +} diff --git a/service/src/tests/mock.rs b/service/src/tests/mock.rs index 6379e7cf05..7c7528fa6c 100644 --- a/service/src/tests/mock.rs +++ b/service/src/tests/mock.rs @@ -16,10 +16,11 @@ */ use codec::Encode; +use enclave_bridge_primitives::ShardSignerStatus; use itp_node_api::api_client::{ApiResult, PalletTeerexApi}; use itp_types::{ - AccountId, MultiEnclave, SgxBuildMode, SgxEnclave, SgxReportData, SgxStatus, ShardIdentifier, - H256 as Hash, + parentchain::BlockNumber, AccountId, MultiEnclave, SgxBuildMode, SgxEnclave, SgxReportData, + SgxStatus, ShardIdentifier, H256 as Hash, }; pub struct TestNodeApi; @@ -79,6 +80,15 @@ impl PalletTeerexApi for TestNodeApi { ) -> ApiResult>>> { unreachable!() } + + fn shard_status( + &self, + _: &ShardIdentifier, + _at_block: Option, + ) -> ApiResult>>> { + unreachable!() + } + fn latest_ipfs_hash( &self, _: &ShardIdentifier, diff --git a/service/src/tests/mocks/enclave_api_mock.rs b/service/src/tests/mocks/enclave_api_mock.rs index 115a27ea56..8bb0abc71a 100644 --- a/service/src/tests/mocks/enclave_api_mock.rs +++ b/service/src/tests/mocks/enclave_api_mock.rs @@ -25,7 +25,7 @@ use itc_parentchain::primitives::{ use itp_enclave_api::{enclave_base::EnclaveBase, sidechain::Sidechain, EnclaveResult}; use itp_settings::worker::MR_ENCLAVE_SIZE; use itp_storage::StorageProof; -use itp_types::ShardIdentifier; +use itp_types::{parentchain::Header, ShardIdentifier}; use sgx_crypto_helper::rsa3072::Rsa3072PubKey; use sp_core::ed25519; @@ -69,6 +69,22 @@ impl EnclaveBase for EnclaveMock { unimplemented!() } + fn init_shard_creation_parentchain_header( + &self, + shard: &ShardIdentifier, + parentchain_id: &ParentchainId, + header: &Header, + ) -> EnclaveResult<()> { + unimplemented!() + } + + fn get_shard_creation_header( + &self, + shard: &ShardIdentifier, + ) -> EnclaveResult<(ParentchainId, Header)> { + unimplemented!() + } + fn set_nonce(&self, _: u32, _: ParentchainId) -> EnclaveResult<()> { unimplemented!() }