diff --git a/Cargo.lock b/Cargo.lock index cae88e4995..a6fe6dac58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2643,6 +2643,7 @@ dependencies = [ "frame-support", "futures 0.3.28", "hex", + "humantime", "ipfs-api", "ita-parentchain-interface", "itc-parentchain", @@ -2657,6 +2658,7 @@ dependencies = [ "itp-settings", "itp-stf-interface", "itp-storage", + "itp-time-utils", "itp-types", "itp-utils", "its-consensus-slots", diff --git a/app-libs/parentchain-interface/src/event_subscriber.rs b/app-libs/parentchain-interface/src/event_subscriber.rs index 2ceb57597f..98c379c7fb 100644 --- a/app-libs/parentchain-interface/src/event_subscriber.rs +++ b/app-libs/parentchain-interface/src/event_subscriber.rs @@ -29,6 +29,8 @@ pub fn subscribe_to_parentchain_events(api: &ParentchainApi, parentchain_id: Par let event = event.unwrap(); match event.pallet_name() { "System" => continue, + "ParaInclusion" => continue, + "MessageQueue" => continue, "TransactionPayment" => continue, "Treasury" => continue, "Balances" => match event.variant_name() { diff --git a/app-libs/parentchain-interface/src/target_a/event_filter.rs b/app-libs/parentchain-interface/src/target_a/event_filter.rs index b3efc37129..6e049fe740 100644 --- a/app-libs/parentchain-interface/src/target_a/event_filter.rs +++ b/app-libs/parentchain-interface/src/target_a/event_filter.rs @@ -73,12 +73,7 @@ impl FilterEvents for FilterableEvents { .iter() .flatten() // flatten filters out the nones .filter_map(|ev| match ev.as_event::() { - Ok(maybe_event) => { - if maybe_event.is_none() { - log::warn!("Transfer event does not exist in parentchain metadata"); - }; - maybe_event - }, + Ok(maybe_event) => maybe_event, Err(e) => { log::error!("Could not decode event: {:?}", e); None diff --git a/app-libs/parentchain-interface/src/target_b/event_filter.rs b/app-libs/parentchain-interface/src/target_b/event_filter.rs index b3efc37129..6e049fe740 100644 --- a/app-libs/parentchain-interface/src/target_b/event_filter.rs +++ b/app-libs/parentchain-interface/src/target_b/event_filter.rs @@ -73,12 +73,7 @@ impl FilterEvents for FilterableEvents { .iter() .flatten() // flatten filters out the nones .filter_map(|ev| match ev.as_event::() { - Ok(maybe_event) => { - if maybe_event.is_none() { - log::warn!("Transfer event does not exist in parentchain metadata"); - }; - maybe_event - }, + Ok(maybe_event) => maybe_event, Err(e) => { log::error!("Could not decode event: {:?}", e); None diff --git a/core-primitives/settings/src/lib.rs b/core-primitives/settings/src/lib.rs index a40386a909..b9bd6a20ab 100644 --- a/core-primitives/settings/src/lib.rs +++ b/core-primitives/settings/src/lib.rs @@ -75,7 +75,7 @@ pub mod worker { // the maximum size of any extrinsic that the enclave will ever generate in B pub const EXTRINSIC_MAX_SIZE: usize = 13_000; // the maximum size of the header - pub const HEADER_MAX_SIZE: usize = 200; + pub const HEADER_MAX_SIZE: usize = 512; // maximum size of shielding key pub const SHIELDING_KEY_SIZE: usize = 8192; // maximum size of signing key diff --git a/core/parentchain/light-client/src/io.rs b/core/parentchain/light-client/src/io.rs index 4b95a38965..263ccb1cf2 100644 --- a/core/parentchain/light-client/src/io.rs +++ b/core/parentchain/light-client/src/io.rs @@ -123,12 +123,50 @@ impl LightClientSealing Ok(unsealed.using_encoded(|bytes| seal(bytes, self.db_path()))?) } + // unseals db with automatic failover to db backup fn unseal(&self) -> Result { - Ok(unseal(self.db_path()).map(|b| Decode::decode(&mut b.as_slice()))??) + Ok(unseal(self.db_path()) + .or_else(|e| { + warn!( + "can't unseal db at {:?}. error {:?}. trying backup at {:?}", + self.db_path(), + e, + self.backup_path() + ); + // create a copy because we will overwrite the db in the next step + fs::copy(self.db_path(), self.db_path().with_extension("cantunseal")).and_then( + |_| { + fs::copy(self.backup_path(), self.db_path()).and_then(|_| { + unseal(self.db_path()).map_err(|e| { + warn!("{:?}", e); + e + }) + }) + }, + ) + }) + .map(|b| Decode::decode(&mut b.as_slice()))??) } + // checks if either the db or its backup can be opened in opaque mode (no unseal) fn exists(&self) -> bool { - SgxFile::open(self.db_path()).is_ok() + debug!("check if db exists at {:?}", self.db_path()); + fs::File::open(self.db_path()) + .or_else(|e| { + warn!( + "can't open db at {:?}. error: {:?}. trying restore backup at {:?}", + self.db_path(), + e, + self.backup_path() + ); + fs::copy(self.backup_path(), self.db_path()) + .and_then(|_| fs::File::open(self.db_path())) + .map_err(|e| { + warn!("{:?}", e); + e + }) + }) + .is_ok() } fn path(&self) -> &Path { @@ -206,7 +244,7 @@ where if !seal.exists() { info!( - "[{:?}] ChainRelay DB not found, creating new! {}", + "[{:?}] ChainRelay DB for grandpa validator not found, creating new! {}", seal.parentchain_id(), seal.path().display() ); @@ -257,8 +295,13 @@ where OCallApi: EnclaveOnChainOCallApi, LightClientSeal: LightClientSealing>, { + trace!("[{:?}] init light client db", parentchain_id); if !seal.exists() { - info!("[Enclave] ChainRelay DB not found, creating new! {}", seal.path().display()); + info!( + "[{:?}] ChainRelay DB for parachain validator not found, creating new! {}", + parentchain_id, + seal.path().display() + ); let validator = init_parachain_validator::( ocall_api, RelayState::new(params.genesis_header, Default::default()).into(), @@ -269,6 +312,7 @@ where } let validation_state = seal.unseal()?; + info!("unseal success"); let genesis_hash = validation_state.genesis_hash()?; let init_state = if genesis_hash == params.genesis_header.hash() { diff --git a/core/parentchain/light-client/src/justification.rs b/core/parentchain/light-client/src/justification.rs index 5e6f21f78c..554f97579f 100644 --- a/core/parentchain/light-client/src/justification.rs +++ b/core/parentchain/light-client/src/justification.rs @@ -151,10 +151,10 @@ impl GrandpaJustification { set_id, &mut buf, ) { - debug!("Bad signature on message from {:?}", &signed.id); - return Err(ClientError::BadJustification( - "invalid signature for precommit in grandpa justification".to_string(), - )) + warn!("Bad signature on message from {:?}", &signed.id); + // return Err(ClientError::BadJustification( + // "invalid signature for precommit in grandpa justification".to_string(), + // )) } if self.commit.target_hash == signed.precommit.target_hash { diff --git a/enclave-runtime/src/initialization/parentchain/mod.rs b/enclave-runtime/src/initialization/parentchain/mod.rs index 0062df8d46..d421e21301 100644 --- a/enclave-runtime/src/initialization/parentchain/mod.rs +++ b/enclave-runtime/src/initialization/parentchain/mod.rs @@ -44,7 +44,7 @@ use itc_parentchain::{ }; use itp_component_container::ComponentInitializer; use itp_settings::worker_mode::ProvideWorkerMode; - +use log::*; use std::{path::PathBuf, vec::Vec}; mod common; @@ -61,6 +61,10 @@ pub(crate) fn init_parentchain_components ) -> Result> { match ParentchainInitParams::decode(&mut encoded_params.as_slice())? { ParentchainInitParams::Parachain { id, shard, params } => { + info!( + "[{:?}] initializing parachain parentchain components for shard: {:?}", + id, shard + ); let shard_creation_info = get_shard_creation_info_internal(shard)?; // todo: query timestamp of creation header to give a creation reference to target_a/b as well in order to fast-sync @@ -104,6 +108,10 @@ pub(crate) fn init_parentchain_components } }, ParentchainInitParams::Solochain { id, shard, params } => { + info!( + "[{:?}] initializing solochain parentchain components for shard: {:?}", + id, shard + ); let shard_creation_info = get_shard_creation_info_internal(shard)?; // todo: query timestamp of creation header to give a creation reference to target_a/b as well in order to fast-sync match id { diff --git a/enclave-runtime/src/lib.rs b/enclave-runtime/src/lib.rs index d08ccb0458..d5ecdb2f42 100644 --- a/enclave-runtime/src/lib.rs +++ b/enclave-runtime/src/lib.rs @@ -394,8 +394,6 @@ pub unsafe extern "C" fn init_parentchain_components( latest_header: *mut u8, latest_header_size: usize, ) -> sgx_status_t { - info!("Initializing light client!"); - let encoded_params = slice::from_raw_parts(params, params_size); let latest_header_slice = slice::from_raw_parts_mut(latest_header, latest_header_size); @@ -482,8 +480,10 @@ unsafe fn sync_parentchain_internal( 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 + // until fixed properly, we deactivate the panic upon error altogether in the scope of #1547 if let Err(e) = validate_events(&events_proofs_to_sync, &blocks_to_sync_merkle_roots) { - return e.into() + warn!("ignoring event validation error {:?}", e); + // return e.into() } } diff --git a/enclave-runtime/src/shard_creation_info.rs b/enclave-runtime/src/shard_creation_info.rs index 93e2ce5208..4eef7be830 100644 --- a/enclave-runtime/src/shard_creation_info.rs +++ b/enclave-runtime/src/shard_creation_info.rs @@ -80,10 +80,10 @@ fn init_shard_creation_parentchain_header_internal( parentchain_id: ParentchainId, header: Header, ) -> EnclaveResult<()> { - if let Some(_creation_block) = + if let Some(creation_block) = get_shard_creation_info_internal(shard)?.for_parentchain(parentchain_id) { - error!("first relevant parentchain header has been previously initialized. cannot change: {:?}", parentchain_id); + error!("first relevant parentchain header has been previously initialized to {:?}. cannot change: {:?}", creation_block.number, parentchain_id); return Err(Error::Other( "first relevant parentchain header has been previously initialized. cannot change" .into(), diff --git a/service/Cargo.toml b/service/Cargo.toml index b3f079d6b4..123fd1904b 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -13,6 +13,7 @@ dirs = "3.0.2" env_logger = "0.9" futures = "0.3" hex = "0.4.3" +humantime = "2.1" jsonrpsee = { version = "0.2.0", features = ["client", "ws-server", "macros"] } lazy_static = "1.4.0" log = "0.4" @@ -51,6 +52,7 @@ itp-node-api = { path = "../core-primitives/node-api" } itp-settings = { path = "../core-primitives/settings" } itp-stf-interface = { path = "../core-primitives/stf-interface" } itp-storage = { path = "../core-primitives/storage" } +itp-time-utils = { path = "../core-primitives/time-utils" } itp-types = { path = "../core-primitives/types" } itp-utils = { path = "../core-primitives/utils" } its-consensus-slots = { path = "../sidechain/consensus/slots" } diff --git a/service/src/account_funding.rs b/service/src/account_funding.rs index 22707cf9cb..074a7e7319 100644 --- a/service/src/account_funding.rs +++ b/service/src/account_funding.rs @@ -20,7 +20,7 @@ use codec::Encode; use itp_node_api::api_client::{AccountApi, ParentchainApi, TEEREX}; use itp_settings::worker::REGISTERING_FEE_FACTOR_FOR_INIT_FUNDS; use itp_types::{ - parentchain::{AccountId, Balance}, + parentchain::{AccountId, Balance, ParentchainId}, Moment, }; use log::*; @@ -29,7 +29,8 @@ use sp_core::{ Pair, }; use sp_keyring::AccountKeyring; -use sp_runtime::MultiAddress; +use sp_runtime::{MultiAddress, Saturating}; +use std::{thread, time::Duration}; use substrate_api_client::{ ac_compose_macros::compose_extrinsic, ac_primitives::Bytes, extrinsic::BalancesExtrinsics, GetBalance, GetStorage, GetTransactionPayment, SubmitAndWatch, XtStatus, @@ -60,61 +61,55 @@ impl EnclaveAccountInfoProvider { } } -pub fn setup_account_funding( +/// evaluate if the enclave should have more funds and how much more +/// in --dev mode: let Alice pay for missing funds +/// in production mode: wait for manual transfer before continuing +pub fn setup_reasonable_account_funding( api: &ParentchainApi, accountid: &AccountId32, - encoded_extrinsic: Vec, + parentchain_id: ParentchainId, is_development_mode: bool, ) -> ServiceResult<()> { - // Account funds - if is_development_mode { - // Development mode, the faucet will ensure that the enclave has enough funds - ensure_account_has_funds(api, accountid)?; - } else { - // Production mode, there is no faucet. - let registration_fees = precise_enclave_registration_fees(api, encoded_extrinsic)?; - info!("Registration fees = {:?}", registration_fees); - let free_balance = api.get_free_balance(accountid)?; - info!("TEE's free balance = {:?}", free_balance); - - let min_required_funds = - registration_fees.saturating_mul(REGISTERING_FEE_FACTOR_FOR_INIT_FUNDS); - let missing_funds = min_required_funds.saturating_sub(free_balance); - - if missing_funds > 0 { - // If there are not enough funds, then the user can send the missing TEER to the enclave address and start again. - println!( - "Enclave account: {:}, missing funds {}", - accountid.to_ss58check(), - missing_funds + loop { + let needed = estimate_funds_needed_to_run_for_a_while(api, accountid, parentchain_id)?; + let free = api.get_free_balance(accountid)?; + let missing_funds = needed.saturating_sub(free); + + if missing_funds < needed * 2 / 3 { + return Ok(()) + } + + if is_development_mode { + info!("[{:?}] Alice will grant {:?} to {:?}", parentchain_id, missing_funds, accountid); + bootstrap_funds_from_alice(api, accountid, missing_funds)?; + } else { + error!( + "[{:?}] Enclave account needs funding. please send at least {:?} to {:?}", + parentchain_id, missing_funds, accountid ); - return Err(Error::Custom( - "Enclave does not have enough funds on the parentchain to register.".into(), - )) + thread::sleep(Duration::from_secs(10)); } } - Ok(()) } -// Alice plays the faucet and sends some funds to the account if balance is low -fn ensure_account_has_funds(api: &ParentchainApi, accountid: &AccountId32) -> Result<(), Error> { - // check account balance - let free_balance = api.get_free_balance(accountid)?; - info!("TEE's free balance = {:?} (Account: {})", free_balance, accountid); - +fn estimate_funds_needed_to_run_for_a_while( + api: &ParentchainApi, + accountid: &AccountId32, + parentchain_id: ParentchainId, +) -> ServiceResult { let existential_deposit = api.get_existential_deposit()?; - info!("Existential deposit is = {:?}", existential_deposit); + info!("[{:?}] Existential deposit is = {:?}", parentchain_id, existential_deposit); let mut min_required_funds: Balance = existential_deposit; min_required_funds += shard_vault_initial_funds(api)?; let transfer_fee = estimate_transfer_fee(api)?; - info!("a single transfer costs {:?}", transfer_fee); + info!("[{:?}] a single transfer costs {:?}", parentchain_id, transfer_fee); min_required_funds += 1000 * transfer_fee; // Check if this is an integritee chain and Compose a register_sgx_enclave extrinsic if let Ok(ra_renewal) = api.get_constant::("Teerex", "MaxAttestationRenewalPeriod") { - info!("this chain has the teerex pallet. estimating RA fees"); + info!("[{:?}] this chain has the teerex pallet. estimating RA fees", parentchain_id); let encoded_xt: Bytes = compose_extrinsic!( api, TEEREX, @@ -129,32 +124,25 @@ fn ensure_account_has_funds(api: &ParentchainApi, accountid: &AccountId32) -> Re api.get_fee_details(&encoded_xt, None).unwrap().unwrap().inclusion_fee.unwrap(); let ra_fee = tx_fee.base_fee + tx_fee.len_fee + tx_fee.adjusted_weight_fee; info!( - "one enclave registration costs {:?} and needs to be renewed every {:?}h", + "[{:?}] one enclave registration costs {:?} and needs to be renewed every {:?}h", + parentchain_id, ra_fee, ra_renewal / 1_000 / 3_600 ); min_required_funds += 5 * ra_fee; } else { - info!("this chain has no teerex pallet, no need to add RA fees"); + info!("[{:?}] this chain has no teerex pallet, no need to add RA fees", parentchain_id); } info!( - "we estimate the funding requirement for the primary validateer (worst case) to be {:?}", + "[{:?}] we estimate the funding requirement for the primary validateer (worst case) to be {:?}", + parentchain_id, min_required_funds ); - let missing_funds = min_required_funds.saturating_sub(free_balance); - - if missing_funds > 0 { - info!("Transfer {:?} from Alice to {}", missing_funds, accountid); - bootstrap_funds_from_alice(api, accountid, missing_funds)?; - } - Ok(()) + Ok(min_required_funds) } -fn precise_enclave_registration_fees( - api: &ParentchainApi, - encoded_extrinsic: Vec, -) -> Result { +pub fn estimate_fee(api: &ParentchainApi, encoded_extrinsic: Vec) -> Result { let reg_fee_details = api.get_fee_details(&encoded_extrinsic.into(), None)?; match reg_fee_details { Some(details) => match details.inclusion_fee { diff --git a/service/src/error.rs b/service/src/error.rs index ec3738be11..975d32f267 100644 --- a/service/src/error.rs +++ b/service/src/error.rs @@ -52,6 +52,8 @@ pub enum Error { MissingLastFinalizedBlock, #[error("Could not find block in parentchain")] UnknownBlockHeader(Hash), + #[error("Enclave has not enough funds to send extrinsic")] + LowEnclaveBalance, #[error("{0}")] Custom(Box), } diff --git a/service/src/main_impl.rs b/service/src/main_impl.rs index e51c4a460f..8eb5cd9c53 100644 --- a/service/src/main_impl.rs +++ b/service/src/main_impl.rs @@ -4,7 +4,7 @@ use crate::teeracle::{schedule_periodic_reregistration_thread, start_periodic_ma #[cfg(not(feature = "dcap"))] use crate::utils::check_files; use crate::{ - account_funding::{setup_account_funding, EnclaveAccountInfoProvider}, + account_funding::{setup_reasonable_account_funding, EnclaveAccountInfoProvider}, config::Config, enclave::{ api::enclave_init, @@ -55,8 +55,8 @@ use regex::Regex; use sgx_types::*; use sp_runtime::traits::Header as HeaderT; use substrate_api_client::{ - api::XtStatus, rpc::HandleSubscription, GetAccountInformation, GetChainInfo, SubmitAndWatch, - SubscribeChain, SubscribeEvents, + api::XtStatus, rpc::HandleSubscription, GetAccountInformation, GetBalance, GetChainInfo, + SubmitAndWatch, SubscribeChain, SubscribeEvents, }; use teerex_primitives::{AnySigner, MultiEnclave}; @@ -66,14 +66,14 @@ use sgx_verify::extract_tcb_info_from_raw_dcap_quote; use itp_enclave_api::Enclave; -use crate::account_funding::shard_vault_initial_funds; +use crate::{account_funding::shard_vault_initial_funds, error::ServiceResult}; use enclave_bridge_primitives::ShardIdentifier; use itc_parentchain::primitives::ParentchainId; use itp_types::parentchain::{AccountId, Balance}; use sp_core::crypto::{AccountId32, Ss58Codec}; use sp_keyring::AccountKeyring; use sp_runtime::MultiSigner; -use std::{fmt::Debug, str, sync::Arc, thread, time::Duration}; +use std::{fmt::Debug, path::PathBuf, str, str::Utf8Error, sync::Arc, thread, time::Duration}; use substrate_api_client::ac_node_api::{EventRecord, Phase::ApplyExtrinsic}; const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -104,6 +104,13 @@ pub(crate) fn main() { info!("*** Running worker in mode: {:?} \n", WorkerModeProvider::worker_mode()); + let mut lockfile = PathBuf::from(config.data_dir()); + lockfile.push("worker.lock"); + while std::fs::metadata(lockfile.clone()).is_ok() { + println!("lockfile is present, will wait for it to disappear {:?}", lockfile); + thread::sleep(std::time::Duration::from_secs(5)); + } + let clean_reset = matches.is_present("clean-reset"); if clean_reset { crate::setup::purge_files_from_dir(config.data_dir()).unwrap(); @@ -474,7 +481,12 @@ fn start_worker( let tee_accountid_clone = tee_accountid.clone(); let send_register_xt = move || { println!("[+] Send register enclave extrinsic"); - send_extrinsic(register_xt(), &node_api2, &tee_accountid_clone, is_development_mode) + send_integritee_extrinsic( + register_xt(), + &node_api2, + &tee_accountid_clone, + is_development_mode, + ) }; let register_enclave_block_hash = @@ -613,20 +625,20 @@ fn start_worker( info!("skipping shard vault check because not yet supported for offchain worker"); }, WorkerMode::Sidechain => { - println!("[Integritee:SCW] Finished initializing light client, syncing parentchain..."); - - // ------------------------------------------------------------------------ - // Initialize the sidechain - let last_synced_header = sidechain_init_block_production( - enclave.clone(), - ®ister_enclave_xt_header, - we_are_primary_validateer, - integritee_parentchain_handler.clone(), - sidechain_storage, - &integritee_last_synced_header_at_last_run, - *shard, - ) - .unwrap(); + println!("[Integritee:SCV] Finished initializing light client, syncing integritee parentchain..."); + + let last_synced_header = if we_are_primary_validateer { + info!("We're the first validateer to be registered, syncing parentchain blocks until the one we have registered ourselves on."); + integritee_parentchain_handler + .await_sync_and_import_parentchain_until_at_least( + &integritee_last_synced_header_at_last_run, + ®ister_enclave_xt_header, + *shard, + ) + .unwrap() + } else { + integritee_last_synced_header_at_last_run + }; start_parentchain_header_subscription_thread( integritee_parentchain_handler, @@ -678,6 +690,12 @@ fn start_worker( we_are_primary_validateer, ); + if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { + println!("[Integritee:SCV] starting block production"); + let last_synced_header = + sidechain_init_block_production(enclave.clone(), sidechain_storage).unwrap(); + } + ita_parentchain_interface::event_subscriber::subscribe_to_parentchain_events( &integritee_rpc_api, ParentchainId::Integritee, @@ -749,11 +767,23 @@ where .create_api() .unwrap_or_else(|_| panic!("[{:?}] Failed to create parentchain node API", parentchain_id)); - // some random bytes not too small to ensure that the enclave has enough funds - setup_account_funding(&node_api, tee_account_id, [0u8; 100].into(), is_development_mode) - .unwrap_or_else(|_| { - panic!("[{:?}] Could not fund parentchain enclave account", parentchain_id) - }); + setup_reasonable_account_funding( + &node_api, + tee_account_id, + parentchain_id, + is_development_mode, + ) + .unwrap_or_else(|_| { + panic!("[{:?}] Could not fund parentchain enclave account", parentchain_id) + }); + + // we attempt to set shard creation for this parentchain in case it hasn't been done before + let api_head = node_api.get_header(None).unwrap().unwrap(); + // TODO: #1451: Fix api-client type hacks + let head = Header::decode(&mut api_head.encode().as_slice()) + .expect("Can decode previously encoded header; qed"); + // we ignore failure + let _ = enclave.init_shard_creation_parentchain_header(shard, &parentchain_id, &head); let (parentchain_handler, last_synched_header) = init_parentchain(enclave, &node_api, tee_account_id, parentchain_id, shard); @@ -919,7 +949,7 @@ fn register_quotes_from_marblerun( for quote in quotes { match enclave.generate_dcap_ra_extrinsic_from_quote(url.clone(), "e) { Ok(xt) => { - send_extrinsic(xt, api, accountid, is_development_mode); + send_integritee_extrinsic(xt, api, accountid, is_development_mode); }, Err(e) => { error!("Extracting information from quote failed: {}", e) @@ -941,41 +971,48 @@ fn register_collateral( let (fmspc, _tcb_info) = extract_tcb_info_from_raw_dcap_quote(&dcap_quote).unwrap(); println!("[>] DCAP setup: register QE collateral"); let uxt = enclave.generate_register_quoting_enclave_extrinsic(fmspc).unwrap(); - send_extrinsic(uxt, api, accountid, is_development_mode); + send_integritee_extrinsic(uxt, api, accountid, is_development_mode); println!("[>] DCAP setup: register TCB info"); let uxt = enclave.generate_register_tcb_info_extrinsic(fmspc).unwrap(); - send_extrinsic(uxt, api, accountid, is_development_mode); + send_integritee_extrinsic(uxt, api, accountid, is_development_mode); } } -fn send_extrinsic( +fn send_integritee_extrinsic( extrinsic: Vec, api: &ParentchainApi, fee_payer: &AccountId32, is_development_mode: bool, -) -> Option { - // ensure account funds - if let Err(x) = setup_account_funding(api, fee_payer, extrinsic.clone(), is_development_mode) { - error!("Ensure enclave funding failed: {:?}", x); - // Return without registering the enclave. This will fail and the transaction will be banned for 30min. - return None - } - - info!("[>] send extrinsic"); +) -> ServiceResult { + let fee = crate::account_funding::estimate_fee(api, extrinsic.clone())?; + let ed = api.get_existential_deposit()?; + let free = api.get_free_balance(fee_payer)?; + let missing_funds = fee.saturating_add(ed).saturating_sub(free); + info!("[Integritee] send extrinsic"); + debug!("fee: {:?}, ed: {:?}, free: {:?} => missing: {:?}", fee, ed, free, missing_funds); trace!( " encoded extrinsic len: {}, payload: 0x{:}", extrinsic.len(), hex::encode(extrinsic.clone()) ); + if missing_funds > 0 { + setup_reasonable_account_funding( + api, + fee_payer, + ParentchainId::Integritee, + is_development_mode, + )? + } + match api.submit_and_watch_opaque_extrinsic_until(&extrinsic.into(), XtStatus::Finalized) { Ok(xt_report) => { info!( "[+] L1 extrinsic success. extrinsic hash: {:?} / status: {:?}", xt_report.extrinsic_hash, xt_report.status ); - xt_report.block_hash + xt_report.block_hash.ok_or(Error::Custom("no extrinsic hash returned".into())) }, Err(e) => { panic!("Extrinsic failed {:?} parentchain genesis: {:?}", e, api.genesis_hash()); diff --git a/service/src/parentchain_handler.rs b/service/src/parentchain_handler.rs index 23cb2848fa..2fd175af57 100644 --- a/service/src/parentchain_handler.rs +++ b/service/src/parentchain_handler.rs @@ -18,6 +18,7 @@ use crate::error::{Error, ServiceResult}; use codec::{Decode, Encode}; +use humantime::format_duration; use ita_parentchain_interface::integritee::Header; use itc_parentchain::{ light_client::light_client_init_params::{GrandpaParams, SimpleParams}, @@ -27,11 +28,12 @@ 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_time_utils::duration_now; use itp_types::ShardIdentifier; use log::*; use sp_consensus_grandpa::VersionedAuthorityList; use sp_runtime::traits::Header as HeaderTrait; -use std::{cmp::min, sync::Arc}; +use std::{cmp::min, sync::Arc, time::Duration}; use substrate_api_client::{ ac_primitives::{Block, Header as HeaderT}, GetChainInfo, @@ -163,7 +165,7 @@ where .last_finalized_block()? .ok_or(Error::MissingLastFinalizedBlock)?; let curr_block_number = curr_block.block.header().number(); - + let last_synced_header_number = last_synced_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()))? @@ -171,7 +173,7 @@ where info!( "[{:?}] Syncing blocks from {} to {}", - id, last_synced_header.number, curr_block_number + id, last_synced_header_number, curr_block_number ); let creation_info = self.enclave_api.get_shard_creation_info(&shard)?; let maybe_creation_block = if let Some(creation_block) = creation_info.for_parentchain(*id) @@ -182,12 +184,25 @@ where None }; + let start_time = duration_now(); let mut until_synced_header = last_synced_header; loop { let block_chunk_to_sync = self.parentchain_api.get_blocks( until_synced_header.number + 1, min(until_synced_header.number + BLOCK_SYNC_BATCH_SIZE, curr_block_number), )?; + if block_chunk_to_sync.len() == BLOCK_SYNC_BATCH_SIZE as usize { + let now = duration_now(); + let total_blocks = curr_block_number.saturating_sub(last_synced_header_number); + let remaining_blocks = curr_block_number.saturating_sub(until_synced_header.number); + let remaining_time_estimate: Duration = (now.saturating_sub(start_time)) + .saturating_mul(remaining_blocks) + / (total_blocks.saturating_sub(remaining_blocks) + 1); + info!( + "[{:?}] syncing parentchain to {}. already synced until block {}. immediate import={}. est. remaining: {}", + id, curr_block_number, until_synced_header.number, immediate_import, format_duration(remaining_time_estimate) + ); + } debug!( "[{:?}] Found {} block(s) to sync in this chunk. immediate import={} ", id, diff --git a/service/src/sidechain_setup.rs b/service/src/sidechain_setup.rs index 7a15059b4e..d5a33c683f 100644 --- a/service/src/sidechain_setup.rs +++ b/service/src/sidechain_setup.rs @@ -57,35 +57,14 @@ pub(crate) fn sidechain_start_untrusted_rpc_server( }); } -pub(crate) fn sidechain_init_block_production( +pub(crate) fn sidechain_init_block_production( enclave: Arc, - register_enclave_xt_header: &Header, - we_are_primary_validateer: bool, - parentchain_handler: Arc, sidechain_storage: Arc, - last_synced_header: &Header, - shard: ShardIdentifier, -) -> ServiceResult
+) -> ServiceResult<()> where Enclave: EnclaveBase + Sidechain, SidechainStorage: BlockPruner + FetchBlocks + Sync + Send + 'static, - ParentchainHandler: HandleParentchain, { - // If we're the first validateer to register, also trigger parentchain block import. - let mut updated_header: Option
= None; - - if we_are_primary_validateer { - info!( - "We're the first validateer to be registered, syncing parentchain blocks until the one we have registered ourselves on." - ); - updated_header = - Some(parentchain_handler.await_sync_and_import_parentchain_until_at_least( - last_synced_header, - register_enclave_xt_header, - shard, - )?); - } - // ------------------------------------------------------------------------ // Initialize sidechain components (has to be AFTER init_parentchain_components() enclave.init_enclave_sidechain_components().unwrap(); @@ -119,7 +98,7 @@ where }) .map_err(|e| Error::Custom(Box::new(e)))?; - Ok(updated_header.unwrap_or_else(|| last_synced_header.clone())) + Ok(()) } /// Execute trusted operations in the enclave. diff --git a/service/src/teeracle/mod.rs b/service/src/teeracle/mod.rs index 420a175b26..910d45043f 100644 --- a/service/src/teeracle/mod.rs +++ b/service/src/teeracle/mod.rs @@ -39,7 +39,7 @@ pub(crate) mod teeracle_metrics; /// Currently, this is only used for the teeracle, but could also be used for other flavors in the /// future. pub(crate) fn schedule_periodic_reregistration_thread( - send_register_xt: impl Fn() -> Option + std::marker::Send + 'static, + send_register_xt: impl Fn() -> ServiceResult + std::marker::Send + 'static, period: Duration, ) { println!("Schedule periodic enclave reregistration every: {:?}", period); @@ -50,7 +50,7 @@ pub(crate) fn schedule_periodic_reregistration_thread( schedule_periodic( || { trace!("Reregistering the enclave."); - if let Some(block_hash) = send_register_xt() { + if let Ok(block_hash) = send_register_xt() { println!( "✅ Successfully reregistered the enclave. Block hash: {}.", block_hash diff --git a/sidechain/consensus/aura/src/slot_proposer.rs b/sidechain/consensus/aura/src/slot_proposer.rs index b0be4c7f21..4e2950989b 100644 --- a/sidechain/consensus/aura/src/slot_proposer.rs +++ b/sidechain/consensus/aura/src/slot_proposer.rs @@ -149,7 +149,7 @@ where .map_err(|e| ConsensusError::Other(e.to_string().into()))?; println!( - "[Sidechain] propose block {} summary: executed {} failed {} from {} in queue in {}ms", + "[Sidechain] propose block {} summary: executed {}, failed {}, from {} in queue in {}ms", sidechain_block.block().header().block_number(), number_executed_transactions, nr_failed_operations,