-
Notifications
You must be signed in to change notification settings - Fork 47
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
Extract the STF execution logic to a separate crate 'stf-executor' #485
Conversation
@@ -16,6 +16,7 @@ members = [ | |||
"core-primitives/settings", | |||
"core-primitives/sgx/crypto", | |||
"core-primitives/sgx/io", | |||
"core-primitives/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.
new crate in core-primitives
: stf-executor
//! Extrinsic helpers for author RPC module. | ||
|
||
use crate::TrustedOperation; | ||
use codec::{Decode, Encode}; | ||
use ita_stf::TrustedOperation; | ||
use std::vec::Vec; | ||
|
||
/// RPC Trusted call or hash | ||
/// Trusted operation Or hash | ||
/// | ||
/// Allows to refer to trusted calls either by its raw representation or its hash. | ||
#[derive(Debug, Encode, Decode)] | ||
#[derive(Clone, Debug, Encode, Decode)] | ||
pub enum TrustedOperationOrHash<Hash> { | ||
/// The hash of the call. | ||
Hash(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.
Moved this from the top-pool-rpc-author
crate to the ita-stf
crate, so it can be re-used in our new crate stf-executor
.
pub type StfResult<T> = Result<T, StfError>; | ||
|
||
#[derive(Debug, Display, PartialEq, Eq)] | ||
pub enum StfError { | ||
#[display(fmt = "Insufficient privileges {:?}, are you sure you are root?", _0)] | ||
MissingPrivileges(AccountId), | ||
#[display(fmt = "Error dispatching runtime call. {:?}", _0)] | ||
Dispatch(String), | ||
#[display(fmt = "Not enough funds to perform operation")] | ||
MissingFunds, | ||
#[display(fmt = "Account does not exist {:?}", _0)] | ||
InexistentAccount(AccountId), | ||
#[display(fmt = "Invalid Nonce {:?}", _0)] | ||
InvalidNonce(Index), | ||
StorageHashMismatch, | ||
InvalidStorageDiff, | ||
} |
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.
Moved the result and error type definition out of the sgx
-only module, in order to have more parts of the code available in both std
and sgx
feature.
pub fn get_state(ext: &mut State, getter: Getter) -> Option<Vec<u8>> { | ||
pub fn get_state(ext: &mut impl SgxExternalitiesTrait, getter: Getter) -> Option<Vec<u8>> { |
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.
Generalize this method signature, making it consistent with some of the other methods that already used this type of signature.
app-libs/stf/src/stf_sgx.rs
Outdated
TrustedGetter::free_balance(who) => | ||
TrustedGetter::free_balance(who) => { |
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.
Seems like there was a change in the formatter after some update that happened on master? Now we have some formatting changes in this PR as a result. Either that, or this crate was never properly formatted to begin with (although shouldn't the formatter have complained?)
pub extern crate alloc; | ||
|
||
use alloc::vec::Vec; | ||
use codec::{Decode, Encode}; | ||
use core::fmt::Debug; | ||
use itp_types::{TrustedOperationStatus, WorkerRequest, WorkerResponse}; | ||
use its_primitives::traits::SignedBlock; | ||
use sgx_types::*; | ||
use sp_runtime::OpaqueExtrinsic; | ||
use sp_std::prelude::Vec; |
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.
Here I'm not sure: We have 3 ways to bring some of the alloc
or std
types into our sgx
feature:
extern crate alloc
+use alloc::vec::Vec
use std::vec::Vec
use sp_std::prelude::Vec
(re-export insp_std
)
What do you guys think? Which option do you prefer and why?
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 think it does not matter. extern crate alloc + use alloc::vec::Vec
is the only one that would be available always. But as it is more verbose, I prefer this the least.
For crates that are close to supporting plain no_std, I would choose alloc
still because:
- sp_std: pulls in
sp-io
, which assumes wasm in no_std, implying that is does not support plain no_std. The library can be used no_std only in wasm (e.g. substrate-node-runtime`), or in sgx (where we patch sp-io, with sgx-sp-io) - sgx: can be used no_std only in an sgx-environment.
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'm against option three, because I don't think we should import substrate deps just to use vec::Vec.
Option 1 & 2.. I don't really care. You said that sgx ports Vec
to alloc anyway, so it's only about style. Since we've been using std::vec::Vec quite often, maybe lets stick to that.
/// STF Executor implementation | ||
/// | ||
/// | ||
pub struct StfExecutor<OCallApi, StateHandler> { | ||
ocall_api: Arc<OCallApi>, | ||
state_handler: Arc<StateHandler>, | ||
} |
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 file now contains all the relevant implementation for executing calls, getters and operations in general on the STF. It's all moved from the enclave-runtime
. It's probably a bit large and does not have any unit-tests 😞 . There are a couple of indirect tests of these parts, in the enclave-runtime
.
Unfortunately this module is only available in sgx
feature, because we heavily use Stf
and all related structures.
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 we should introduce unit tests for this part.. I guess? Maybe create an issue for this if its too much work for this 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.
Having unit tests would certainly be a great improvement. And we're lacking them everywhere. So I'm not sure an issue 'create unit tests for this' will do much good. In my experience those kind of issues never get prioritized in a sprint 😝 Next time we touch this code and we extend or change the behavior, we need to introduce unit tests. So writing the tests should be part of the next feature that happens in this part.
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.
You got me there.. can't reason against that :(
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.
Well you could argue to still do them now 😄 I'm a bit on the fence about it myself, because I like to write unit tests. And on the other hand, I should make some progress on the actual feature (#301) I'm trying to achieve
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 also agree. Maybe add a //FIXME comment. Not an issue but atleast something that someone will stumble upon when this code part is touched?
fn execute_trusted_call_on_stf<PB, E>( | ||
&self, | ||
state: &mut E, | ||
stf_call_signed: &TrustedCallSigned, | ||
header: &PB::Header, | ||
shard: &ShardIdentifier, | ||
post_processing: StatePostProcessing, | ||
) -> Result<ExecutedOperation> |
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.
A re-usable method to execute a single trusted call on the STF (not part of a trait)
fn execute_trusted_call<PB>( | ||
&self, | ||
calls: &mut Vec<OpaqueCall>, | ||
stf_call_signed: &TrustedCallSigned, | ||
header: &PB::Header, | ||
shard: &ShardIdentifier, | ||
post_processing: StatePostProcessing, | ||
) -> Result<Option<(H256, H256)>> |
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 quite dislike the fact that the result of this method is both in the returned value, and in the &mut Vec<OpaqueCall>
parameter. In some parts, I replaced this out
parameter by just a return value. But not here yet.
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.
Yes, this has been bugging me for ages too.
impl<OCallApi, StateHandler> StfExecuteShieldFunds for StfExecutor<OCallApi, StateHandler> | ||
where | ||
OCallApi: EnclaveAttestationOCallApi + EnclaveOnChainOCallApi + GetStorageVerified, | ||
StateHandler: HandleState, | ||
{ | ||
fn execute_shield_funds( | ||
&self, | ||
account: AccountId, | ||
amount: Amount, | ||
shard: &ShardIdentifier, | ||
calls: &mut Vec<OpaqueCall>, | ||
) -> Result<H256> { | ||
let (state_lock, mut state) = self.state_handler.load_for_mutation(shard)?; | ||
|
||
let root = Stf::get_root(&mut state); | ||
let nonce = Stf::account_nonce(&mut state, &root); | ||
|
||
let trusted_call = TrustedCallSigned::new( | ||
TrustedCall::balance_shield(root, account, amount), | ||
nonce, | ||
Default::default(), //don't care about signature here | ||
); | ||
|
||
Stf::execute(&mut state, trusted_call, calls).map_err::<Error, _>(|e| e.into())?; | ||
|
||
self.state_handler.write(state, state_lock, shard).map_err(|e| e.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.
Execute shield funds logic on the Stf
fn execute_timed_calls_batch<PB, F>( | ||
&self, | ||
trusted_calls: &[TrustedCallSigned], | ||
header: &PB::Header, | ||
shard: &ShardIdentifier, | ||
max_exec_duration: Duration, | ||
prepare_state_function: F, | ||
) -> Result<ExecutionResult> | ||
where | ||
PB: BlockT<Hash = H256>, | ||
F: FnOnce(Self::Externalities) -> Self::Externalities, | ||
{ |
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.
Execute a batch of trusted calls with a given time budget and return an ExecutionResult
which includes the previous state hash, the execution status, and more information.
/// Result of an execution on the STF | ||
/// | ||
/// Contains multiple executed calls | ||
#[derive(Clone, Debug)] | ||
pub struct ExecutionResult { | ||
pub previous_state_hash: H256, | ||
pub executed_operations: Vec<ExecutedOperation>, | ||
} |
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.
Utility structure for returning information about the execution of a collection of operations.
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 really like the abstractions here. 👍
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.
Very cool. I really like this approach. However, is the ExecutionResult
naming correct? Until now, for me an execution was the execution of one extrsinic. Maybe I'm in the wrong here, but how about BatchExecutionResult
?
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 re-named it to BatchExecutionResult
👍
fn sync_blocks_on_light_client<PB, V, OCallApi, StateHandler>( | ||
fn sync_blocks_on_light_client<PB, V, OCallApi, StfExecutor>( | ||
blocks_to_sync: Vec<SignedBlockG<PB>>, | ||
validator: &mut V, | ||
on_chain_ocall_api: &OCallApi, | ||
state_handler: &StateHandler, | ||
stf_executor: &StfExecutor, |
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 the enclave-runtime
the StfExecutor
now replaces the StateHandler
in a lot of places (not everywhere) and thus hides the state handler details.
|s| { | ||
let mut sidechain_db = SidechainDB::<SB::Block, _>::new(s); | ||
sidechain_db.set_block_number(&sidechain_db.get_block_number().map_or(1, |n| n + 1)); | ||
sidechain_db.set_timestamp(&now_as_u64()); | ||
sidechain_db.ext | ||
}, |
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 logic for setting block number, timestamp etc is now packed into a state 'pre-processing' lambda.
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 think, this looks very nice.
f55dcdd
to
174fc87
Compare
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.
Complicated stuff.. left some comments.
pub extern crate alloc; | ||
|
||
use alloc::vec::Vec; | ||
use codec::{Decode, Encode}; | ||
use core::fmt::Debug; | ||
use itp_types::{TrustedOperationStatus, WorkerRequest, WorkerResponse}; | ||
use its_primitives::traits::SignedBlock; | ||
use sgx_types::*; | ||
use sp_runtime::OpaqueExtrinsic; | ||
use sp_std::prelude::Vec; |
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'm against option three, because I don't think we should import substrate deps just to use vec::Vec.
Option 1 & 2.. I don't really care. You said that sgx ports Vec
to alloc anyway, so it's only about style. Since we've been using std::vec::Vec quite often, maybe lets stick to that.
#[error("Trusted operation has invalid signature")] | ||
OperationHasInvalidSignature, | ||
#[error("SGX error, status: {0}")] | ||
SgxError(sgx_status_t), |
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.
Not for this PR, but as a general Info in case you're introducing a new error type: Clippy will warn about using an Error
ending in the future rust version: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
So don't name them xxxxError
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 think you have a point here, the error enum element should not contain 'error' itself, I will fix that 👍
/// STF Executor implementation | ||
/// | ||
/// | ||
pub struct StfExecutor<OCallApi, StateHandler> { | ||
ocall_api: Arc<OCallApi>, | ||
state_handler: Arc<StateHandler>, | ||
} |
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 we should introduce unit tests for this part.. I guess? Maybe create an issue for this if its too much work for this PR?
|
||
use crate::{ | ||
error::{Error, Result}, | ||
traits::{ |
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.
Is it really necessary to have so many traits? Is there no other way to abstract the stf?
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 design question of how many methods should a trait have. In other languages, recommendations for interfaces are in the range of 2-5 or 2-7. With traits, where we have trait mix-in (with +
) that makes trait aggregation very easy, we can achieve very fine granularity by having one method per trait.
In our case here, most consumers don't ever need more than 2 of these methods. So grouping them together in a trait, will cause unnecessary exposure of methods. That was my reasoning behind this design. However, I think this can be open for debate, I'd be happy to hear your arguments 😄
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 second Felix. However, the real merit of this will not be obvious before we start having generic functions only requiring one of these 'subtraits'.
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.
However, by better core design, we'd might want to some of these traits, but I think this is a different construction site.
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.
We'd might want to do __ what with these traits? I think you lost the verb there in the sentence 😅
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.
haha, thought faster than I typed. 😄 I meant 'remove'. However, not in terms of combining traits, but with a better core design. :P
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 okay, now it makes sense 😝. Yes I totally agree, I think these stf executor traits can be reduced in number, because they are somewhat redundant and can be combined into more generalized trait methods. However, I also believe refactoring that requires a bit of effort, it's not that straight forward (and will require writing some tests in advance, to make sure we don't break stuff in the process).
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.
My reasoning is just to keep it simple. The stf is made to be adaptable by clients such that they can easily integrate their stuff into the sgx-runtime. As long as we keep in mind the users who want to remove unshielding & shielding, or whatever, I'm fine with it. And it's not like I have a better solution here myself ;)
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.
We can certainly make it more simple. What I have here in this PR is just extracting code as-is into a crate. It is by no means the end-result as we would want to have it. There are still many improvements that can be done on this part, this was just a first step.
/// Result of an execution on the STF | ||
/// | ||
/// Contains multiple executed calls | ||
#[derive(Clone, Debug)] | ||
pub struct ExecutionResult { | ||
pub previous_state_hash: H256, | ||
pub executed_operations: Vec<ExecutedOperation>, | ||
} |
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.
Very cool. I really like this approach. However, is the ExecutionResult
naming correct? Until now, for me an execution was the execution of one extrsinic. Maybe I'm in the wrong here, but how about BatchExecutionResult
?
.collect() | ||
} | ||
|
||
pub fn get_all_execution_hashes(&self) -> Vec<ExecutionHashes> { |
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.
Renaming suggestion: get_executed_operation_hashes
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 can do that 👍
@@ -64,7 +65,7 @@ pub type MrEnclave = [u8; 32]; | |||
pub type BlockHash = H256; | |||
|
|||
pub type ConfirmCallFn = ([u8; 2], ShardIdentifier, H256, Vec<u8>); | |||
pub type ShieldFundsFn = ([u8; 2], Vec<u8>, u128, ShardIdentifier); | |||
pub type ShieldFundsFn = ([u8; 2], Vec<u8>, Amount, ShardIdentifier); |
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.
Very nice!
|
||
/// Hash results of an operation execution on the STF | ||
#[derive(Clone, Debug)] | ||
pub struct ExecutionHashes { |
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.
For me this naming was a little confusing. ExecutionHashes could also be the State hashes after execution. We could simply name it ExtrinsicHash
to follow substrates example. Or OperationHash
. ( I guess it should be singular, as it only contains one hash each? Also, why are both, call hashes and operation hashes needed?
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.
Yes the naming is not ideal here. It's probably a combination of me not understanding exactly what it represents, and the thing having a confusing name before the refactoring.
I don't know why we have both a OperationHash
and a CallHash
- this is just ported over as it was before. I also don't exactly understand what the difference should be. ExecutionHashes
should probably be replaced by a better name. Provided we still need OperationHash
AND CallHash
, what could the aggregation of those two be called?
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 think @haerdib introduced these (not sure), so she might correct my statements.
I have taken a look at the code. Deriving from the current usage of handle_trusted_worker_call
, which is only at two places. The first time, we only care about the potential error, the second time, we are only interested in the trusted_operation_hash
, which is put into a call_hash
vector (!!!), I sense some code smell here. :)
I am confident, we can get rid of one. I actually think we should not use the trusted operations hash. Reasoning: this is an internal helper struct of the worker. The client sends either TrustedCall
s or TrustedGetter
s AFAIK. If we want to give a block inclusion proof to a client in the future, it will be more straightforward if we hash the exact object the client has sent to the worker. The hash is not needed for anything else.
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 would simply use the following structure:
ExecutionResult {
// hash of the executed trusted call.
call_hash: H256
}
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'd be happy to get rid of one of those hashes. And yes, we mix the names in the process, that adds to the confusion. We now use the operation_hash
and use it in a call_hashes
vector. And you suggest to only use the call_hash
and get rid of the operation_hash
? Won't that break something, because we're now returning a different hash value?
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 think that's fine if everything is still working. But we might want to create an issue to discuss the use case of TrustedOperation itself @clangenb ?
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.
Yes, I think @mullefel change is fine.
I also agree to discuess the usage of trusted op.
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.
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.
fyi: I had to revert back to using the operation hash (not the call hash), since we have tests that check for that. So to be on the safe side, I'll use the operation hash for now to not alter any behavior in the code.
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.
Ok, even better. 👍
enclave-runtime/src/lib.rs
Outdated
shard, | ||
executed_operation.is_success(), | ||
) | ||
.unwrap(); |
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 this was here before, but I think we should not panic in case we can not remove a top. Should we?
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.
We probably shouldn't. I'll change that to properly handle the result 👍
Unfortunately I cannot answer to your suggestion: #485 (comment) , so I'll do it here. |
further step towards moving out the sidechain logic out of the enclave-runtime
174fc87
to
12743a4
Compare
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 general, very impressive. I think you made a good compromise between simple modularization and refactoring.
Only some small questions.
pub extern crate alloc; | ||
|
||
use alloc::vec::Vec; | ||
use codec::{Decode, Encode}; | ||
use core::fmt::Debug; | ||
use itp_types::{TrustedOperationStatus, WorkerRequest, WorkerResponse}; | ||
use its_primitives::traits::SignedBlock; | ||
use sgx_types::*; | ||
use sp_runtime::OpaqueExtrinsic; | ||
use sp_std::prelude::Vec; |
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 think it does not matter. extern crate alloc + use alloc::vec::Vec
is the only one that would be available always. But as it is more verbose, I prefer this the least.
For crates that are close to supporting plain no_std, I would choose alloc
still because:
- sp_std: pulls in
sp-io
, which assumes wasm in no_std, implying that is does not support plain no_std. The library can be used no_std only in wasm (e.g. substrate-node-runtime`), or in sgx (where we patch sp-io, with sgx-sp-io) - sgx: can be used no_std only in an sgx-environment.
fn execute_trusted_call<PB>( | ||
&self, | ||
calls: &mut Vec<OpaqueCall>, | ||
stf_call_signed: &TrustedCallSigned, | ||
header: &PB::Header, | ||
shard: &ShardIdentifier, | ||
post_processing: StatePostProcessing, | ||
) -> Result<Option<(H256, H256)>> |
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.
Yes, this has been bugging me for ages too.
/// Result of an execution on the STF | ||
/// | ||
/// Contains multiple executed calls | ||
#[derive(Clone, Debug)] | ||
pub struct ExecutionResult { | ||
pub previous_state_hash: H256, | ||
pub executed_operations: Vec<ExecutedOperation>, | ||
} |
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 really like the abstractions here. 👍
} | ||
} | ||
|
||
impl<OCallApi, StateHandler> StfExecuteGenericUpdate for StfExecutor<OCallApi, StateHandler> |
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 think this can be even more useful, if we add Externalities
to the generic arguments and require the SgxExternalities
trait bound here; what do you think?
This would require the that the externalities are part of the StfExecutor
as a PhantomData
field, but this is totally fine.
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.
Yes I can do that, it would further decouple us from the concrete SgxExternalities
👍
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 made that change now. It required a bit of refactoring in the state handler as well, to make that trait generic over the externalities. And I also had to further generalize some of the method signature in the Stf
struct. The explains the rather large change set that I had to commit on top of this 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.
Haha, yes, but I think it was worth the effort. Thanks! Looks good.
|
||
use crate::{ | ||
error::{Error, Result}, | ||
traits::{ |
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.
However, by better core design, we'd might want to some of these traits, but I think this is a different construction site.
|
||
/// Hash results of an operation execution on the STF | ||
#[derive(Clone, Debug)] | ||
pub struct ExecutionHashes { |
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 think @haerdib introduced these (not sure), so she might correct my statements.
I have taken a look at the code. Deriving from the current usage of handle_trusted_worker_call
, which is only at two places. The first time, we only care about the potential error, the second time, we are only interested in the trusted_operation_hash
, which is put into a call_hash
vector (!!!), I sense some code smell here. :)
I am confident, we can get rid of one. I actually think we should not use the trusted operations hash. Reasoning: this is an internal helper struct of the worker. The client sends either TrustedCall
s or TrustedGetter
s AFAIK. If we want to give a block inclusion proof to a client in the future, it will be more straightforward if we hash the exact object the client has sent to the worker. The hash is not needed for anything else.
|
||
/// Hash results of an operation execution on the STF | ||
#[derive(Clone, Debug)] | ||
pub struct ExecutionHashes { |
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 would simply use the following structure:
ExecutionResult {
// hash of the executed trusted call.
call_hash: H256
}
|s| { | ||
let mut sidechain_db = SidechainDB::<SB::Block, _>::new(s); | ||
sidechain_db.set_block_number(&sidechain_db.get_block_number().map_or(1, |n| n + 1)); | ||
sidechain_db.set_timestamp(&now_as_u64()); | ||
sidechain_db.ext | ||
}, |
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 think, this looks very nice.
[features] | ||
default = ["std"] | ||
std = [ | ||
"ita-stf/std", |
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 sucks big time, but I think we will have to live with this until we abstract TrustedCalls, TrustedOp, TrustedGetter into core-primitives.
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.
We have the ita-stf
dependency everywhere because of it, yes. I've looked at it a couple of times, and I'm not sure there is a 'simple' solution, provided we don't break with @brenzi 's intention to keep all the domain specific stuff in that crate (and TrustedCall
, TrustedGetter
etc. contain exactly the domain specific logic)
return only the call hash, not the operation 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.
Looks very good!
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.
Very cool, thanks !
A further refactoring step towards #301 . This time I extracted code from the
enclave-runtime
that is related to executing logic on the STF. This includes:The main motivation for separating this code to a separate crate, is that the
sidechain
components use it, and it's the reason why we still have quite a bit ofsidechain
implementation in theenclave-runtime
instead of thesidechain
crate.The extracted code has not been much beautified or re-designed, it's mostly moved in its original state. I mention this, because there are certainly quite a few improvements that could be done on those parts of the code (that now reside in
stf-executor
). But in order to move along and get to the end-goal (#301), I leave it as-is for now.