-
Notifications
You must be signed in to change notification settings - Fork 46
Extract the top pool operation execution into separate modules #500
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
Conversation
| 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)>, | ||
| } |
There was a problem hiding this comment.
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
| 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()); |
There was a problem hiding this comment.
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
| mod sidechain_block_composer; | ||
| mod sidechain_impl; | ||
| mod sync; | ||
| pub mod tls_ra; | ||
| pub mod top_pool_execution; | ||
| mod top_pool_operation_executor; |
There was a problem hiding this comment.
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)
| 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()))?; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. 😄
| 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() } |
There was a problem hiding this comment.
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.
| 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( |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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! 👍
| 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, | ||
| ); |
There was a problem hiding this comment.
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
| fn state_key() -> Aes { | ||
| Aes::default() | ||
| } |
There was a problem hiding this comment.
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 | ||
| } | ||
|
|
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
| 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, | ||
| ); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.. 😵💫
| 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)); |
There was a problem hiding this comment.
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)
67622c2 to
4a267f8
Compare
4a267f8 to
e50f4d0
Compare
haerdib
left a comment
There was a problem hiding this 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] |
There was a problem hiding this comment.
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 !
| 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, | ||
| ); |
There was a problem hiding this comment.
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 | ||
| } | ||
|
|
There was a problem hiding this comment.
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?
clangenb
left a comment
There was a problem hiding this 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!
| 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()))?; |
There was a problem hiding this comment.
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. 😄
| 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( |
There was a problem hiding this comment.
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.
e50f4d0 to
de5852b
Compare
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.