Skip to content

Commit

Permalink
Provision light-client in mutual remote attestation (#1399)
Browse files Browse the repository at this point in the history
* [block-import] add `ImportType` to distinguish sync and block-production import type.

* Revert "[block-import] add `ImportType` to distinguish sync and block-production import type."

This reverts commit c3c46fd.

* [light-validation] add genesis_hash field and introduce header prunging

* [light-client] add `LightClientStateSealSync` which has some mechanisms such that it can be shared across threads in an arc.

[light-client] add constructor to `LightClientStateSealSync`

fix light-client adjustments

remove pending wip of sealing adjustements

* provision light-client state wip

* [tls-ra] provision light-client state too

* fix clippy

* fix cargo test compilation
  • Loading branch information
clangenb authored Aug 1, 2023
1 parent 3443f15 commit f5e059f
Show file tree
Hide file tree
Showing 17 changed files with 247 additions and 92 deletions.
10 changes: 5 additions & 5 deletions core/parentchain/light-client/src/concurrent_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::{
};
use finality_grandpa::BlockNumberOps;
use sp_runtime::traits::{Block as ParentchainBlockTrait, NumberFor};
use std::marker::PhantomData;
use std::{marker::PhantomData, sync::Arc};

/// Retrieve an exclusive lock on a validator for either read or write access.
///
Expand Down Expand Up @@ -62,15 +62,15 @@ where
/// Implementation of a validator access based on a global lock and corresponding file.
#[derive(Debug)]
pub struct ValidatorAccessor<Validator, ParentchainBlock, LightClientSeal> {
seal: LightClientSeal,
seal: Arc<LightClientSeal>,
light_validation: RwLock<Validator>,
_phantom: PhantomData<(LightClientSeal, Validator, ParentchainBlock)>,
}

impl<Validator, ParentchainBlock, LightClientSeal>
ValidatorAccessor<Validator, ParentchainBlock, LightClientSeal>
{
pub fn new(validator: Validator, seal: LightClientSeal) -> Self {
pub fn new(validator: Validator, seal: Arc<LightClientSeal>) -> Self {
ValidatorAccessor {
light_validation: RwLock::new(validator),
seal,
Expand All @@ -85,7 +85,7 @@ where
Validator: ValidatorTrait<ParentchainBlock>
+ LightClientState<ParentchainBlock>
+ ExtrinsicSenderTrait,
Seal: LightClientSealing<LightValidationState<ParentchainBlock>>,
Seal: LightClientSealing<LightClientState = LightValidationState<ParentchainBlock>>,
ParentchainBlock: ParentchainBlockTrait,
NumberFor<ParentchainBlock>: BlockNumberOps,
{
Expand Down Expand Up @@ -126,7 +126,7 @@ mod tests {
fn execute_with_and_without_mut_in_single_thread_works() {
let validator_mock = ValidatorMock::default();
let seal = LightValidationStateSealMock::new();
let accessor = TestAccessor::new(validator_mock, seal);
let accessor = TestAccessor::new(validator_mock, seal.into());

let _read_result = accessor.execute_on_validator(|_v| Ok(())).unwrap();
let _write_result = accessor.execute_mut_on_validator(|_v| Ok(())).unwrap();
Expand Down
54 changes: 50 additions & 4 deletions core/parentchain/light-client/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

use crate::{
error::Result,
error::{Error, Result},
finality::{Finality, GrandpaFinality, ParachainFinality},
light_client_init_params::{GrandpaParams, SimpleParams},
light_validation::{check_validator_set_proof, LightValidation},
Expand All @@ -37,6 +37,12 @@ use std::{
sync::Arc,
};

#[cfg(feature = "sgx")]
use std::sync::SgxRwLock as RwLock;

#[cfg(feature = "std")]
use std::sync::RwLock;

pub const DB_FILE: &str = "db.bin";
pub const BACKUP_FILE: &str = "db.bin.backup";

Expand Down Expand Up @@ -81,9 +87,11 @@ impl<B, L> LightClientStateSeal<B, L> {
}
}

impl<B: Block, LightClientState: Decode + Encode + Debug> LightClientSealing<LightClientState>
impl<B: Block, LightClientState: Decode + Encode + Debug> LightClientSealing
for LightClientStateSeal<B, LightClientState>
{
type LightClientState = LightClientState;

fn seal(&self, unsealed: &LightClientState) -> Result<()> {
trace!("Backup light client state");

Expand All @@ -108,6 +116,44 @@ impl<B: Block, LightClientState: Decode + Encode + Debug> LightClientSealing<Lig
}
}

/// Same as [LightClientStateSeal], but it ensures that no concurrent write operations are done
/// accross different threads.
#[derive(Debug)]
pub struct LightClientStateSealSync<B, LightClientState> {
seal: LightClientStateSeal<B, LightClientState>,
_rw_lock: RwLock<()>,
}

impl<B, LightClientState> LightClientStateSealSync<B, LightClientState> {
pub fn new(base_path: PathBuf) -> Result<Self> {
Ok(Self { seal: LightClientStateSeal::new(base_path)?, _rw_lock: RwLock::new(()) })
}
}

impl<B: Block, LightClientState: Decode + Encode + Debug> LightClientSealing
for LightClientStateSealSync<B, LightClientState>
{
type LightClientState = LightClientState;

fn seal(&self, unsealed: &LightClientState) -> Result<()> {
let _lock = self._rw_lock.write().map_err(|_| Error::PoisonedLock)?;
self.seal.seal(unsealed)
}

fn unseal(&self) -> Result<LightClientState> {
let _lock = self._rw_lock.read().map_err(|_| Error::PoisonedLock)?;
self.seal.unseal()
}

fn exists(&self) -> bool {
self.seal.exists()
}

fn path(&self) -> &Path {
self.seal.path()
}
}

// FIXME: This is a lot of duplicate code for the initialization of two
// different but sameish light clients. Should be tackled with #1081
pub fn read_or_init_grandpa_validator<B, OCallApi, LightClientSeal>(
Expand All @@ -119,7 +165,7 @@ where
B: Block,
NumberFor<B>: finality_grandpa::BlockNumberOps,
OCallApi: EnclaveOnChainOCallApi,
LightClientSeal: LightClientSealing<LightValidationState<B>>,
LightClientSeal: LightClientSealing<LightClientState = LightValidationState<B>>,
{
check_validator_set_proof::<B>(
params.genesis_header.state_root(),
Expand Down Expand Up @@ -168,7 +214,7 @@ where
B: Block,
NumberFor<B>: finality_grandpa::BlockNumberOps,
OCallApi: EnclaveOnChainOCallApi,
LightClientSeal: LightClientSealing<LightValidationState<B>>,
LightClientSeal: LightClientSealing<LightClientState = LightValidationState<B>>,
{
if !seal.exists() {
info!("[Enclave] ChainRelay DB not found, creating new! {}", seal.path().display());
Expand Down
8 changes: 5 additions & 3 deletions core/parentchain/light-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,11 @@ pub trait LightClientState<Block: ParentchainBlockTrait> {
fn penultimate_finalized_block_header(&self) -> Result<Block::Header, Error>;
}

pub trait LightClientSealing<LightClientState> {
fn seal(&self, state: &LightClientState) -> Result<(), Error>;
fn unseal(&self) -> Result<LightClientState, Error>;
pub trait LightClientSealing {
type LightClientState;

fn seal(&self, state: &Self::LightClientState) -> Result<(), Error>;
fn unseal(&self) -> Result<Self::LightClientState, Error>;
fn exists(&self) -> bool;
fn path(&self) -> &Path;
}
Expand Down
4 changes: 2 additions & 2 deletions core/parentchain/light-client/src/light_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ impl<Block: ParentchainBlockTrait, OcallApi: EnclaveOnChainOCallApi>
}

// A valid grandpa proof proves finalization of all previous unjustified blocks.
relay.header_hashes.append(&mut relay.unjustified_headers);
relay.header_hashes.push(header.hash());
relay.justify_headers();
relay.push_header_hash(header.hash());

relay.set_last_finalized_block_header(header);

Expand Down
4 changes: 1 addition & 3 deletions core/parentchain/light-client/src/light_validation_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ where
}

fn genesis_hash(&self) -> Result<HashFor<Block>, Error> {
let relay = self.get_relay();
let hash = relay.header_hashes.get(0).ok_or(Error::NoGenesis)?;
Ok(*hash)
Ok(self.get_relay().genesis_hash)
}

fn latest_finalized_header(&self) -> Result<Block::Header, Error> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ impl Default for LightValidationStateSealMock {
}
}

impl LightClientSealing<LightValidationState<Block>> for LightValidationStateSealMock {
impl LightClientSealing for LightValidationStateSealMock {
type LightClientState = LightValidationState<Block>;

fn unseal(&self) -> Result<LightValidationState<Block>, Error> {
Ok(LightValidationState::new(RelayState::new(
ParentchainHeaderBuilder::default().build(),
Expand Down
40 changes: 34 additions & 6 deletions core/parentchain/light-client/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,61 @@ use sp_runtime::{
traits::{Block as BlockT, Header as HeaderT},
OpaqueExtrinsic,
};
use std::{fmt, vec::Vec};
use std::{collections::VecDeque, fmt, vec::Vec};

/// Defines the amount of parentchain headers to keep.
pub const PARENTCHAIN_HEADER_PRUNING: u64 = 1000;

#[derive(Encode, Decode, Clone, Eq, PartialEq)]
pub struct RelayState<Block: BlockT> {
pub genesis_hash: Block::Hash,
pub last_finalized_block_header: Block::Header,
pub penultimate_finalized_block_header: Block::Header,
pub current_validator_set: AuthorityList,
pub current_validator_set_id: SetId,
pub header_hashes: Vec<Block::Hash>,
header_hashes: VecDeque<Block::Hash>,
pub unjustified_headers: Vec<Block::Hash>, // Finalized headers without grandpa proof
pub verify_tx_inclusion: Vec<OpaqueExtrinsic>, // Transactions sent by the relay
pub scheduled_change: Option<ScheduledChangeAtBlock<Block::Header>>, // Scheduled Authorities change as indicated in the header's digest.
}

impl<Block: BlockT> RelayState<Block> {
pub fn push_header_hash(&mut self, header: Block::Hash) {
self.header_hashes.push_back(header);

if self.header_hashes.len() > PARENTCHAIN_HEADER_PRUNING as usize {
self.header_hashes.pop_front().expect("Tested above that is not empty; qed");
}
}

pub fn justify_headers(&mut self) {
self.header_hashes.extend(&mut self.unjustified_headers.iter());
self.unjustified_headers.clear();

while self.header_hashes.len() > PARENTCHAIN_HEADER_PRUNING as usize {
self.header_hashes.pop_front().expect("Tested above that is not empty; qed");
}
}

pub fn header_hashes(&self) -> &VecDeque<Block::Hash> {
&self.header_hashes
}
}

#[derive(Encode, Decode, Clone, Eq, PartialEq)]
pub struct ScheduledChangeAtBlock<Header: HeaderT> {
pub at_block: Header::Number,
pub next_authority_list: AuthorityList,
}

impl<Block: BlockT> RelayState<Block> {
pub fn new(block_header: Block::Header, validator_set: AuthorityList) -> Self {
pub fn new(genesis: Block::Header, validator_set: AuthorityList) -> Self {
RelayState {
header_hashes: vec![block_header.hash()],
last_finalized_block_header: block_header.clone(),
genesis_hash: genesis.hash(),
header_hashes: vec![genesis.hash()].into(),
last_finalized_block_header: genesis.clone(),
// is it bad to initialize with the same? Header trait does no implement default...
penultimate_finalized_block_header: block_header,
penultimate_finalized_block_header: genesis,
current_validator_set: validator_set,
current_validator_set_id: 0,
unjustified_headers: Vec::new(),
Expand Down
16 changes: 12 additions & 4 deletions enclave-runtime/src/initialization/global_components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ use itc_parentchain::{
IndirectCallsExecutor,
},
light_client::{
concurrent_access::ValidatorAccessor, io::LightClientStateSeal,
concurrent_access::ValidatorAccessor, io::LightClientStateSealSync,
light_validation::LightValidation, light_validation_state::LightValidationState,
},
};
Expand Down Expand Up @@ -132,7 +132,7 @@ pub type EnclaveSidechainApi = SidechainApi<ParentchainBlock>;

// Parentchain types
pub type EnclaveLightClientSeal =
LightClientStateSeal<ParentchainBlock, LightValidationState<ParentchainBlock>>;
LightClientStateSealSync<ParentchainBlock, LightValidationState<ParentchainBlock>>;
pub type EnclaveExtrinsicsFactory =
ExtrinsicsFactory<EnclaveParentchainSigner, NonceCache, EnclaveNodeMetadataRepository>;
pub type EnclaveIndirectCallsExecutor = IndirectCallsExecutor<
Expand Down Expand Up @@ -215,8 +215,12 @@ pub type EnclaveSidechainBlockImportQueueWorker = BlockImportQueueWorker<
EnclaveSidechainBlockImportQueue,
EnclaveSidechainBlockSyncer,
>;
pub type EnclaveSealHandler =
SealHandler<EnclaveShieldingKeyRepository, EnclaveStateKeyRepository, EnclaveStateHandler>;
pub type EnclaveSealHandler = SealHandler<
EnclaveShieldingKeyRepository,
EnclaveStateKeyRepository,
EnclaveStateHandler,
EnclaveLightClientSeal,
>;
pub type EnclaveOffchainWorkerExecutor = itc_offchain_worker_executor::executor::Executor<
ParentchainBlock,
EnclaveTopPoolAuthor,
Expand Down Expand Up @@ -244,6 +248,10 @@ pub static GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT: ComponentContainer<
EnclaveSigningKeyRepository,
> = ComponentContainer::new("Signing key repository");

/// Light client db seal.
pub static GLOBAL_LIGHT_CLIENT_SEAL: ComponentContainer<EnclaveLightClientSeal> =
ComponentContainer::new("EnclaveLightClientSealSync");

/// O-Call API
pub static GLOBAL_OCALL_API_COMPONENT: ComponentContainer<EnclaveOCallApi> =
ComponentContainer::new("O-call API");
Expand Down
14 changes: 9 additions & 5 deletions enclave-runtime/src/initialization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ pub mod parentchain;
use crate::{
error::{Error, Result as EnclaveResult},
initialization::global_components::{
EnclaveBlockImportConfirmationHandler, EnclaveGetterExecutor, EnclaveOCallApi,
EnclaveRpcConnectionRegistry, EnclaveRpcResponder, EnclaveShieldingKeyRepository,
EnclaveSidechainApi, EnclaveSidechainBlockImportQueue,
EnclaveBlockImportConfirmationHandler, EnclaveGetterExecutor, EnclaveLightClientSeal,
EnclaveOCallApi, EnclaveRpcConnectionRegistry, EnclaveRpcResponder,
EnclaveShieldingKeyRepository, EnclaveSidechainApi, EnclaveSidechainBlockImportQueue,
EnclaveSidechainBlockImportQueueWorker, EnclaveSidechainBlockImporter,
EnclaveSidechainBlockSyncer, EnclaveStateFileIo, EnclaveStateHandler,
EnclaveStateInitializer, EnclaveStateObserver, EnclaveStateSnapshotRepository,
EnclaveStfEnclaveSigner, EnclaveTopPool, EnclaveTopPoolAuthor,
GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_OCALL_API_COMPONENT,
GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_LIGHT_CLIENT_SEAL, GLOBAL_OCALL_API_COMPONENT,
GLOBAL_RPC_WS_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT,
GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT,
GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT,
Expand Down Expand Up @@ -59,7 +59,7 @@ use itc_tls_websocket_server::{
use itp_attestation_handler::IntelAttestationHandler;
use itp_component_container::{ComponentGetter, ComponentInitializer};
use itp_primitives_cache::GLOBAL_PRIMITIVES_CACHE;
use itp_settings::files::STATE_SNAPSHOTS_CACHE_SIZE;
use itp_settings::files::{LIGHT_CLIENT_DB_PATH, STATE_SNAPSHOTS_CACHE_SIZE};
use itp_sgx_crypto::{
get_aes_repository, get_ed25519_repository, get_rsa3072_repository, key_repository::AccessKey,
};
Expand Down Expand Up @@ -94,6 +94,10 @@ pub(crate) fn init_enclave(
let state_key_repository = Arc::new(get_aes_repository(base_dir.clone())?);
GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.initialize(state_key_repository.clone());

let light_client_seal =
Arc::new(EnclaveLightClientSeal::new(base_dir.join(LIGHT_CLIENT_DB_PATH))?);
GLOBAL_LIGHT_CLIENT_SEAL.initialize(light_client_seal);

let state_file_io =
Arc::new(EnclaveStateFileIo::new(state_key_repository, StateDir::new(base_dir)));
let state_initializer =
Expand Down
20 changes: 9 additions & 11 deletions enclave-runtime/src/initialization/parentchain/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ use crate::{
error::Result,
initialization::{
global_components::{
EnclaveExtrinsicsFactory, EnclaveLightClientSeal, EnclaveNodeMetadataRepository,
EnclaveOCallApi, EnclaveParentchainBlockImportDispatcher, EnclaveStfExecutor,
EnclaveValidatorAccessor, GLOBAL_FULL_PARACHAIN_HANDLER_COMPONENT,
GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT,
EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi,
EnclaveParentchainBlockImportDispatcher, EnclaveStfExecutor, EnclaveValidatorAccessor,
GLOBAL_FULL_PARACHAIN_HANDLER_COMPONENT, GLOBAL_OCALL_API_COMPONENT,
GLOBAL_STATE_HANDLER_COMPONENT,
},
parentchain::common::{
create_extrinsics_factory, create_offchain_immediate_import_dispatcher,
Expand All @@ -33,12 +33,10 @@ use crate::{
use codec::Encode;
use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState};
use itp_component_container::{ComponentGetter, ComponentInitializer};
use itp_settings::{
files::LIGHT_CLIENT_DB_PATH,
worker_mode::{ProvideWorkerMode, WorkerMode},
};
use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode};
use std::{path::PathBuf, sync::Arc, vec::Vec};

use crate::initialization::global_components::GLOBAL_LIGHT_CLIENT_SEAL;
pub use itc_parentchain::primitives::{ParachainBlock, ParachainHeader, ParachainParams};

#[derive(Clone)]
Expand All @@ -54,7 +52,7 @@ pub struct FullParachainHandler {

impl FullParachainHandler {
pub fn init<WorkerModeProvider: ProvideWorkerMode>(
base_path: PathBuf,
_base_path: PathBuf,
params: ParachainParams,
) -> Result<Vec<u8>> {
let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?;
Expand All @@ -63,12 +61,12 @@ impl FullParachainHandler {

let genesis_header = params.genesis_header.clone();

let light_client_seal = EnclaveLightClientSeal::new(base_path.join(LIGHT_CLIENT_DB_PATH))?;
let light_client_seal = GLOBAL_LIGHT_CLIENT_SEAL.get()?;
let validator = itc_parentchain::light_client::io::read_or_init_parachain_validator::<
ParachainBlock,
EnclaveOCallApi,
_,
>(params, ocall_api.clone(), &light_client_seal)?;
>(params, ocall_api.clone(), &*light_client_seal)?;
let latest_header = validator.latest_finalized_header()?;
let validator_accessor =
Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal));
Expand Down
Loading

0 comments on commit f5e059f

Please sign in to comment.