Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provision light-client in mutual remote attestation #1399

Merged
merged 8 commits into from
Aug 1, 2023
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;
Copy link
Contributor Author

@clangenb clangenb Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Associated type is more flexible, as otherwise I would have to declare an extra generic param for the block when being generic of LightClientSealing because the generic type of the state is generic over the block, which in turn means that I would have had to introduce a phantom type for the block, not funny.


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.
clangenb marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Debug)]
pub struct LightClientStateSealSync<B, LightClientState> {
seal: LightClientStateSeal<B, LightClientState>,
_rw_lock: RwLock<()>,
}

Comment on lines +121 to +126
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[derive(Debug)]
pub struct LightClientStateSealSync<B, LightClientState> {
seal: LightClientStateSeal<B, LightClientState>,
_rw_lock: RwLock<()>,
}
// safe to clone into threads.
#[derive(Clone, Debug)]
pub struct LightClientStateSealSync<B, LightClientState> {
seal: Arc<RwLock<LightClientStateSeal<B, LightClientState>>,
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think a bit more before we tackle that: #1401

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> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo: don't define this on the RelayState itself, rather introduce a new type HeaderQueue, which implements those methods, and can be unit tested.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is now initialized at an earlier stage and made available as a global component.

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