diff --git a/core-primitives/settings/src/lib.rs b/core-primitives/settings/src/lib.rs index d95dc5d041..aa70e4b74d 100644 --- a/core-primitives/settings/src/lib.rs +++ b/core-primitives/settings/src/lib.rs @@ -101,5 +101,9 @@ pub mod enclave {} pub mod teeracle { use core::time::Duration; // Send extrinsic to update market exchange rate on the parentchain once per day - pub static DEFAULT_MARKET_DATA_UPDATE_INTERVAL: Duration = Duration::from_secs(86400); + pub static DEFAULT_MARKET_DATA_UPDATE_INTERVAL: Duration = ONE_DAY; + + pub static ONE_DAY: Duration = Duration::from_secs(86400); + + pub static THIRTY_MINUTES: Duration = Duration::from_secs(1800); } diff --git a/service/src/cli.yml b/service/src/cli.yml index 800c5647a8..42f22834da 100644 --- a/service/src/cli.yml +++ b/service/src/cli.yml @@ -114,6 +114,11 @@ subcommands: short: i help: Set the teeracle exchange rate update interval. Example of accepted syntax <5 seconds 15 minutes 2 hours 1 days> or short <5s15m2h1d> takes_value: true + - reregister-teeracle-interval: + required: false + long: reregister + 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 args: diff --git a/service/src/config.rs b/service/src/config.rs index a5039e9404..4b441b173c 100644 --- a/service/src/config.rs +++ b/service/src/config.rs @@ -17,7 +17,7 @@ use clap::ArgMatches; use itc_rest_client::rest_client::Url; -use itp_settings::teeracle::DEFAULT_MARKET_DATA_UPDATE_INTERVAL; +use itp_settings::teeracle::{DEFAULT_MARKET_DATA_UPDATE_INTERVAL, ONE_DAY, THIRTY_MINUTES}; use parse_duration::parse; use serde::{Deserialize, Serialize}; use std::{ @@ -225,6 +225,8 @@ pub struct RunConfig { shard: Option, /// Optional teeracle update interval teeracle_update_interval: Option, + /// Optional teeracle reregistration interval + reregister_teeracle_interval: Option, /// Marblerun's Prometheus endpoint base URL marblerun_base_url: Option, } @@ -250,6 +252,15 @@ impl RunConfig { self.teeracle_update_interval.unwrap_or(DEFAULT_MARKET_DATA_UPDATE_INTERVAL) } + /// The periodic registration period of the teeracle. + /// + /// Defaults to 23h30m, as this is slightly below the currently configured automatic + /// deregistration period on the Integritee chains. + pub fn reregister_teeracle_interval(&self) -> Duration { + // Todo: Derive this from chain https://github.com/integritee-network/worker/issues/1351 + self.reregister_teeracle_interval.unwrap_or(ONE_DAY - THIRTY_MINUTES) + } + pub fn marblerun_base_url(&self) -> &str { // This conflicts with the default port of a substrate node, but it is indeed the // default port of marblerun too: @@ -267,13 +278,25 @@ impl From<&ArgMatches<'_>> for RunConfig { let teeracle_update_interval = m.value_of("teeracle-interval").map(|i| { parse(i).unwrap_or_else(|e| panic!("teeracle-interval parsing error {:?}", e)) }); + let reregister_teeracle_interval = m.value_of("reregister-teeracle-interval").map(|i| { + parse(i).unwrap_or_else(|e| panic!("teeracle-interval parsing error {:?}", e)) + }); + let marblerun_base_url = m.value_of("marblerun-url").map(|i| { Url::parse(i) .unwrap_or_else(|e| panic!("marblerun-url parsing error: {:?}", e)) .to_string() }); - Self { skip_ra, dev, request_state, shard, teeracle_update_interval, marblerun_base_url } + Self { + skip_ra, + dev, + request_state, + shard, + teeracle_update_interval, + reregister_teeracle_interval, + marblerun_base_url, + } } } diff --git a/service/src/main.rs b/service/src/main.rs index 8c20b5ffc7..e92ba144e3 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -18,7 +18,7 @@ #![cfg_attr(test, feature(assert_matches))] #[cfg(feature = "teeracle")] -use crate::teeracle::start_interval_market_update; +use crate::teeracle::{schedule_periodic_reregistration_thread, start_periodic_market_update}; #[cfg(not(feature = "dcap"))] use crate::utils::check_files; @@ -445,12 +445,20 @@ fn start_worker( } else { println!("[!] creating remote attestation report and create enclave register extrinsic."); }; + + // clones because of the move + let enclave2 = enclave.clone(); + let node_api2 = node_api.clone(); #[cfg(not(feature = "dcap"))] - let xt = enclave.generate_ias_ra_extrinsic(&trusted_url, skip_ra).unwrap(); + let register_xt = move || enclave2.generate_ias_ra_extrinsic(&trusted_url, skip_ra).unwrap(); #[cfg(feature = "dcap")] - let xt = enclave.generate_dcap_ra_extrinsic(&trusted_url, skip_ra).unwrap(); - let register_enclave_block_hash = - send_extrinsic(xt, &node_api, &tee_accountid, is_development_mode); + let register_xt = move || enclave2.generate_dcap_ra_extrinsic(&trusted_url, skip_ra).unwrap(); + + let send_register_xt = move || { + send_extrinsic(register_xt(), &node_api2, &tee_accountid.clone(), is_development_mode) + }; + + let register_enclave_block_hash = send_register_xt(); let register_enclave_xt_header = node_api.get_header(register_enclave_block_hash).unwrap().unwrap(); @@ -470,7 +478,12 @@ fn start_worker( // initialize teeracle interval #[cfg(feature = "teeracle")] if WorkerModeProvider::worker_mode() == WorkerMode::Teeracle { - start_interval_market_update( + schedule_periodic_reregistration_thread( + send_register_xt, + run_config.reregister_teeracle_interval(), + ); + + start_periodic_market_update( &node_api, run_config.teeracle_update_interval(), enclave.as_ref(), diff --git a/service/src/teeracle/mod.rs b/service/src/teeracle/mod.rs index 3674ecfeb3..7283fa9197 100644 --- a/service/src/teeracle/mod.rs +++ b/service/src/teeracle/mod.rs @@ -15,10 +15,11 @@ */ -use crate::teeracle::interval_scheduling::schedule_on_repeating_intervals; +use crate::{error::ServiceResult, teeracle::schedule_periodic::schedule_periodic}; use codec::{Decode, Encode}; use itp_enclave_api::teeracle_api::TeeracleApi; use itp_node_api::api_client::ParentchainApi; +use itp_types::parentchain::Hash; use itp_utils::hex::hex_encode; use log::*; use sp_runtime::OpaqueExtrinsic; @@ -27,99 +28,88 @@ use substrate_api_client::{SubmitAndWatch, XtStatus}; use teeracle_metrics::{increment_number_of_request_failures, set_extrinsics_inclusion_success}; use tokio::runtime::Handle; -pub(crate) mod interval_scheduling; +pub(crate) mod schedule_periodic; pub(crate) mod teeracle_metrics; -/// Send extrinsic to chain according to the market data update interval in the settings -/// with the current market data (for now only exchange rate). -pub(crate) fn start_interval_market_update( +/// Schedule periodic reregistration of the enclave. +/// +/// The `send_register_xt` needs to create a fresh registration extrinsic every time it is called +/// (updated nonce, fresh IAS-RA or DCAP-Quote). +/// +/// 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, + period: Duration, +) { + println!("Schedule periodic enclave reregistration every: {:?}", period); + + std::thread::Builder::new() + .name("enclave_reregistration_thread".to_owned()) + .spawn(move || { + schedule_periodic( + || { + trace!("Reregistering the enclave."); + if let Some(block_hash) = send_register_xt() { + println!( + "✅ Successfully reregistered the enclave. Block hash: {}.", + block_hash + ) + } else { + error!("❌ Could not reregister the enclave.") + } + }, + period, + ); + }) + .unwrap(); +} + +/// Executes a periodic teeracle data update and sends the new data to the parentchain. +/// +/// Note: Puts the current thread to sleep for `period`. +pub(crate) fn start_periodic_market_update( api: &ParentchainApi, - interval: Duration, + period: Duration, enclave_api: &E, tokio_handle: &Handle, ) { let updates_to_run = || { - execute_market_update(api, enclave_api, tokio_handle); + if let Err(e) = execute_oracle_update(api, tokio_handle, || { + // Get market data for usd (hardcoded) + enclave_api.update_market_data_xt("TEER", "USD") + }) { + error!("Error running market update {:?}", e) + } + // TODO: Refactor and add this back according to ISSUE: https://github.com/integritee-network/worker/issues/1300 - // execute_weather_update(api, enclave_api, tokio_handle); + // if let Err(e) = execute_oracle_update(api, tokio_handle, || { + // enclave_api.update_weather_data_xt("54.32", "15.37") + // }) { + // error!("Error running weather update {:?}", e) + // } }; info!("Teeracle will update now"); updates_to_run(); - info!("Starting teeracle interval for oracle update, interval of {:?}", interval); - schedule_on_repeating_intervals(updates_to_run, interval); + info!("Schedule teeracle updates every {:?}", period); + schedule_periodic(updates_to_run, period); } -#[allow(dead_code)] -fn execute_weather_update( +fn execute_oracle_update( node_api: &ParentchainApi, - enclave: &E, tokio_handle: &Handle, -) { - let updated_extrinsic = match enclave.update_weather_data_xt("54.32", "15.37") { - Err(e) => { - error!("{:?}", e); - increment_number_of_request_failures(); - return - }, - Ok(r) => r, - }; - - let extrinsics = match >::decode(&mut updated_extrinsic.as_slice()) { - Ok(calls) => calls, - Err(e) => { - error!("Failed to decode opaque extrinsics(s): {:?}: ", e); - return - }, - }; - - extrinsics.into_iter().for_each(|call| { - let node_api_clone = node_api.clone(); - tokio_handle.spawn(async move { - let encoded_extrinsic = call.encode(); - debug!("Hex encoded extrinsic to be sent: {}", hex_encode(&encoded_extrinsic)); - println!("[>] Update oracle (send the extrinsic)"); - let extrinsic_hash = match node_api_clone.submit_and_watch_opaque_extrinsic_until( - encoded_extrinsic.into(), - XtStatus::InBlock, - ) { - Err(e) => { - error!("Failed to send extrinsic: {:?}", e); - set_extrinsics_inclusion_success(false); - return - }, - Ok(report) => { - set_extrinsics_inclusion_success(true); - report.extrinsic_hash - }, - }; - println!("[<] Extrinsic got included into a block. Hash: {:?}\n", extrinsic_hash); - }); - }); -} - -fn execute_market_update( - node_api: &ParentchainApi, - enclave: &E, - tokio_handle: &Handle, -) { - // Get market data for usd (hardcoded) - let updated_extrinsic = match enclave.update_market_data_xt("TEER", "USD") { - Err(e) => { - error!("{:?}", e); - increment_number_of_request_failures(); - return - }, - Ok(r) => r, - }; - - let extrinsics: Vec = match Decode::decode(&mut updated_extrinsic.as_slice()) { - Ok(calls) => calls, - Err(e) => { - error!("Failed to decode opaque extrinsic(s): {:?}: ", e); - return - }, - }; + get_oracle_xt: F, +) -> ServiceResult<()> +where + F: Fn() -> Result, itp_enclave_api::error::Error>, +{ + let oracle_xt = get_oracle_xt().map_err(|e| { + increment_number_of_request_failures(); + e + })?; + + let extrinsics = >::decode(&mut oracle_xt.as_slice())?; // Send the extrinsics to the parentchain and wait for InBlock confirmation. for call in extrinsics.into_iter() { @@ -128,7 +118,7 @@ fn execute_market_update( let encoded_extrinsic = call.encode(); debug!("Hex encoded extrinsic to be sent: {}", hex_encode(&encoded_extrinsic)); - println!("[>] Update the exchange rate (send the extrinsic)"); + println!("[>] Update oracle data (send the extrinsic)"); let extrinsic_hash = match node_api_clone.submit_and_watch_opaque_extrinsic_until( encoded_extrinsic.into(), XtStatus::InBlock, @@ -147,4 +137,6 @@ fn execute_market_update( println!("[<] Extrinsic got included into a block. Hash: {:?}\n", extrinsic_hash); }); } + + Ok(()) } diff --git a/service/src/teeracle/interval_scheduling.rs b/service/src/teeracle/schedule_periodic.rs similarity index 83% rename from service/src/teeracle/interval_scheduling.rs rename to service/src/teeracle/schedule_periodic.rs index 5cf60b3cf1..cde09af452 100644 --- a/service/src/teeracle/interval_scheduling.rs +++ b/service/src/teeracle/schedule_periodic.rs @@ -20,12 +20,12 @@ use std::{ time::{Duration, Instant}, }; -/// Schedules a task on perpetually looping intervals. +/// Schedules a periodic task in the current thread. /// /// In case the task takes longer than is scheduled by the interval duration, /// the interval timing will drift. The task is responsible for /// ensuring it does not use up more time than is scheduled. -pub(super) fn schedule_on_repeating_intervals(task: T, interval_duration: Duration) +pub(super) fn schedule_periodic(task: T, period: Duration) where T: Fn(), { @@ -33,13 +33,13 @@ where loop { let elapsed = interval_start.elapsed(); - if elapsed >= interval_duration { + if elapsed >= period { // update interval time interval_start = Instant::now(); task(); } else { // sleep for the rest of the interval - let sleep_time = interval_duration - elapsed; + let sleep_time = period - elapsed; thread::sleep(sleep_time); } }