Skip to content

Conversation

@murerfel
Copy link
Contributor

@murerfel murerfel commented Nov 10, 2021

Created new traits/components that execute the trusted operations from the top pool.
Also separated the execution and block/confirmation composition.

The is in preparation for moving these parts into the sidechain crate.

Comment on lines +44 to +64
pub trait ComposeBlockAndConfirmation {
type SidechainBlockT: SignedBlockT;
type ParentchainBlockT: BlockT;

fn compose_block_and_confirmation(
&self,
latest_onchain_header: &<Self::ParentchainBlockT as BlockT>::Header,
top_call_hashes: Vec<H256>,
shard: ShardIdentifier,
state_hash_apriori: H256,
) -> Result<(OpaqueCall, Self::SidechainBlockT)>;
}

/// Block composer implementation for the sidechain
pub struct BlockComposer<PB, SB, Signer, StateKey, RpcAuthor, StfExecutor> {
signer: Signer,
state_key: StateKey,
rpc_author: Arc<RpcAuthor>,
stf_executor: Arc<StfExecutor>,
_phantom: PhantomData<(PB, SB)>,
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the new (sidechain) block composer, extracted from the former compose_block_and_confirmation function

@murerfel murerfel self-assigned this Nov 10, 2021
@murerfel murerfel requested review from clangenb and haerdib November 10, 2021 10:11
let opaque_call =
OpaqueCall::from_tuple(&(xt_block, shard, block_hash, state_hash_new.encode()));

self.rpc_author.on_block_created(block.signed_top_hashes(), block.hash());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The composer is now responsible for doing the callback to the rpc author

Comment on lines +99 to +105
mod sidechain_block_composer;
mod sidechain_impl;
mod sync;
pub mod tls_ra;
pub mod top_pool_execution;
mod top_pool_operation_executor;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Lots of code was removed from this lib.rs, refactored and moved to these new modules (temporary before we further refactor and move them into the sidechain crate)

Comment on lines -118 to +143
let (calls, blocks) = execute_top_pool_trusted_calls::<PB, SB, _, _, Signer>(
self.author.as_ref(),
self.stf_executor.as_ref(),
self.signer.clone(),
&self.parentchain_header,
self.shard,
max_duration,
)
.map_err(|e| ConsensusError::Other(e.to_string().into()))?;

Ok(Proposal {
block: blocks.ok_or(ConsensusError::CannotPropose)?,
parentchain_effects: calls,
})
let latest_onchain_header = &self.parentchain_header;

let batch_execution_result = self
.top_pool_executor
.execute_trusted_calls(latest_onchain_header, self.shard, max_duration)
.map_err(|e| ConsensusError::Other(e.to_string().into()))?;

let mut parentchain_extrinsics = batch_execution_result.get_extrinsic_callbacks();

let executed_operation_hashes =
batch_execution_result.get_executed_operation_hashes().iter().copied().collect();

let (confirmation_extrinsic, sidechain_block) = self
.block_composer
.compose_block_and_confirmation(
latest_onchain_header,
executed_operation_hashes,
self.shard,
batch_execution_result.previous_state_hash,
)
.map_err(|e| ConsensusError::Other(e.to_string().into()))?;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The workflow here has now changed a bit. We have to first execute the trusted calls and then call the block composer to compose the block and corresponding confirmation extrinsic.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is great, I had this separation of concerns also in mind. 😄

Comment on lines -218 to +245
pub struct BlockImporter<A, PB, SB, O, ST, StateHandler> {
pub struct BlockImporter<A, PB, SB, O, ST, StateHandler, StateKey> {
state_handler: Arc<StateHandler>,
state_key: StateKey,
_phantom: PhantomData<(A, PB, SB, ST, O)>,
}

impl<A, PB, SB, O, ST, StateHandler> BlockImporter<A, PB, SB, O, ST, StateHandler> {
impl<A, PB, SB, O, ST, StateHandler, StateKey>
BlockImporter<A, PB, SB, O, ST, StateHandler, StateKey>
{
#[allow(unused)]
pub fn new(state_handler: Arc<StateHandler>) -> Self {
Self { state_handler, _phantom: Default::default() }
pub fn new(state_handler: Arc<StateHandler>, state_key: StateKey) -> Self {
Self { state_handler, state_key, _phantom: Default::default() }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was more of a small improvement on the side: The block importer should not read the state encryption key directly from file, but rather have it passed as member when it's constructed. This makes the dependency more obvious and lets us test this importer more easily, without having to rely on a file existing on the filesystem.

Comment on lines -210 to +226
submit_and_execute_top(&rpc_author, &signed_getter.clone().into(), &shielding_key, shard)
.unwrap();
submit_and_execute_top(&rpc_author, &direct_top(signed_call.clone()), &shielding_key, shard)
.unwrap();
submit_operation_to_top_pool(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

minor re-naming, I realized submit_and_execute_top in fact only submits to the top pool, does not execute.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yes, this changed quite a while ago, thanks! 👍

Comment on lines -225 to +261
let stf_executor = StfExecutor::new(Arc::new(OcallApi), state_handler.clone());
let stf_executor = Arc::new(StfExecutor::new(Arc::new(OcallApi), state_handler.clone()));
let top_pool_executor = TopPoolOperationExecutor::<Block, SignedBlock, _, _>::new(
rpc_author.clone(),
stf_executor.clone(),
);
let block_composer = BlockComposer::<Block, SignedBlock, _, _, _, _>::new(
test_account(),
state_key(),
rpc_author.clone(),
stf_executor,
);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

in some of the tests here we now need the top pool executor and the block composer explicitly

Comment on lines +512 to +544
fn state_key() -> Aes {
Aes::default()
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Use a default AES key for state encryption instead of reading it from file (and thus requiring that file to exists when we run the tests)


sgx_status_t::SGX_SUCCESS
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this file we have the top-level e-calls for executing trusted getters and trusted calls from the top pool. These will be further refactored and moved to the sidechain crate in the next PR

Copy link
Contributor

Choose a reason for hiding this comment

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

I know it will be further refactored - but what do you think about adding top-level-file description, i.e. documenting the thought process of about what should be placed in this file?

What I'm dreaming about:
https://github.com/paritytech/substrate/blob/master/frame/scheduler/src/lib.rs#L18-L48

What might be reality:
https://github.com/paritytech/substrate/blob/master/primitives/core/src/hash.rs#L18

But we could try?

Comment on lines +60 to +75
let rpc_author = GlobalAuthorContainer.get().ok_or_else(|| {
error!("Failed to retrieve author mutex. It might not be initialized?");
Error::MutexAccess
})?;

let state_handler = Arc::new(GlobalFileStateHandler);
let stf_executor = Arc::new(StfExecutor::new(Arc::new(OcallApi), state_handler.clone()));

let shards = state_handler.list_shards()?;
let mut remaining_shards = shards.len() as u32;
let ends_at = duration_now() + MAX_TRUSTED_GETTERS_EXEC_DURATION;

let top_pool_executor = TopPoolOperationExecutor::<Block, SignedSidechainBlock, _, _>::new(
rpc_author,
stf_executor,
);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is basically constructing all the necessary components to run this function. It's what a dependency injection framework would do for us. I'm thinking about having a 'sidechain container' (container being a dependency injection concept, where a container contains all registered and constructed components) that is initialized and constructed once and can be accessed at each call.

Copy link
Contributor

Choose a reason for hiding this comment

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

One day, I'd really love to see the architecture you have in mind.. 😵‍💫

Comment on lines +128 to +153
let mut validator = LightClientSeal::<PB>::unseal()?;

let authority = Ed25519Seal::unseal()?;
let state_key = AesSeal::unseal()?;

let rpc_author = GlobalAuthorContainer.get().ok_or_else(|| {
error!("Failed to retrieve author mutex. Maybe it's not initialized?");
Error::MutexAccess
})?;

let state_handler = Arc::new(GlobalFileStateHandler);
let stf_executor = Arc::new(StfExecutor::new(Arc::new(OcallApi), state_handler.clone()));

let latest_onchain_header = validator.latest_finalized_header(validator.num_relays()).unwrap();
let genesis_hash = validator.genesis_hash(validator.num_relays())?;
let extrinsics_factory =
ExtrinsicsFactory::new(genesis_hash, authority.clone(), GLOBAL_NONCE_CACHE.clone());

let top_pool_executor =
Arc::new(TopPoolOperationExecutor::<PB, SignedSidechainBlock, _, _>::new(
rpc_author.clone(),
stf_executor.clone(),
));

let block_composer =
Arc::new(BlockComposer::new(authority.clone(), state_key, rpc_author, stf_executor));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All of this is also just constructing the necessary components. Will try to refactor this in the next PR (as described above)

@murerfel murerfel force-pushed the feature/fm-extract-top-pool-execution branch from 67622c2 to 4a267f8 Compare November 10, 2021 10:42
Base automatically changed from feature/fm-extrinsics-factory-nonce-cache to master November 10, 2021 13:49
@murerfel murerfel force-pushed the feature/fm-extract-top-pool-execution branch from 4a267f8 to e50f4d0 Compare November 10, 2021 13:59
Copy link
Contributor

@haerdib haerdib left a comment

Choose a reason for hiding this comment

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

It looks good, but I think I need to take another look at it tomorrow.. too late now to wrap my head around everything in here.

sgx_status_t::SGX_SUCCESS
}

#[no_mangle]
Copy link
Contributor

Choose a reason for hiding this comment

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

So much red. I love it !

Comment on lines +60 to +75
let rpc_author = GlobalAuthorContainer.get().ok_or_else(|| {
error!("Failed to retrieve author mutex. It might not be initialized?");
Error::MutexAccess
})?;

let state_handler = Arc::new(GlobalFileStateHandler);
let stf_executor = Arc::new(StfExecutor::new(Arc::new(OcallApi), state_handler.clone()));

let shards = state_handler.list_shards()?;
let mut remaining_shards = shards.len() as u32;
let ends_at = duration_now() + MAX_TRUSTED_GETTERS_EXEC_DURATION;

let top_pool_executor = TopPoolOperationExecutor::<Block, SignedSidechainBlock, _, _>::new(
rpc_author,
stf_executor,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

One day, I'd really love to see the architecture you have in mind.. 😵‍💫


sgx_status_t::SGX_SUCCESS
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I know it will be further refactored - but what do you think about adding top-level-file description, i.e. documenting the thought process of about what should be placed in this file?

What I'm dreaming about:
https://github.com/paritytech/substrate/blob/master/frame/scheduler/src/lib.rs#L18-L48

What might be reality:
https://github.com/paritytech/substrate/blob/master/primitives/core/src/hash.rs#L18

But we could try?

Copy link
Contributor

@clangenb clangenb left a comment

Choose a reason for hiding this comment

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

Looks very good, nothing really to add!

Comment on lines -118 to +143
let (calls, blocks) = execute_top_pool_trusted_calls::<PB, SB, _, _, Signer>(
self.author.as_ref(),
self.stf_executor.as_ref(),
self.signer.clone(),
&self.parentchain_header,
self.shard,
max_duration,
)
.map_err(|e| ConsensusError::Other(e.to_string().into()))?;

Ok(Proposal {
block: blocks.ok_or(ConsensusError::CannotPropose)?,
parentchain_effects: calls,
})
let latest_onchain_header = &self.parentchain_header;

let batch_execution_result = self
.top_pool_executor
.execute_trusted_calls(latest_onchain_header, self.shard, max_duration)
.map_err(|e| ConsensusError::Other(e.to_string().into()))?;

let mut parentchain_extrinsics = batch_execution_result.get_extrinsic_callbacks();

let executed_operation_hashes =
batch_execution_result.get_executed_operation_hashes().iter().copied().collect();

let (confirmation_extrinsic, sidechain_block) = self
.block_composer
.compose_block_and_confirmation(
latest_onchain_header,
executed_operation_hashes,
self.shard,
batch_execution_result.previous_state_hash,
)
.map_err(|e| ConsensusError::Other(e.to_string().into()))?;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is great, I had this separation of concerns also in mind. 😄

Comment on lines -210 to +226
submit_and_execute_top(&rpc_author, &signed_getter.clone().into(), &shielding_key, shard)
.unwrap();
submit_and_execute_top(&rpc_author, &direct_top(signed_call.clone()), &shielding_key, shard)
.unwrap();
submit_operation_to_top_pool(
Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yes, this changed quite a while ago, thanks! 👍

Also separated the execution and block/confirmation composition.
The is in preparation for moving these parts into the sidechain crate.
@murerfel murerfel force-pushed the feature/fm-extract-top-pool-execution branch from e50f4d0 to de5852b Compare November 11, 2021 10:06
@murerfel murerfel merged commit bca26ab into master Nov 11, 2021
@murerfel murerfel deleted the feature/fm-extract-top-pool-execution branch November 11, 2021 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants