-
Notifications
You must be signed in to change notification settings - Fork 46
Description
#1155 proposes a refactoring to the way we handle indirect calls execution, which is already an improvement to the status quo. However, I was wondering if we can improve more such that:
- boilerplate code is reduced
- We don't need to touch the indirect calls executor when a new call should be supported.
So I thought of:
/// Parentchain calls that should be handled in some way by the enclave
pub enum IndirectCall {
#[codec(index = <parentchain_module_index, e.g., 7>]
Teerex(TeerexCall)
}
pub enum TeerexCall {
#[codec(index = <parentchain_call_index, e.g., 2>]
ShieldFunds(ShieldFundsCall)
}
struct ShieldFundsCall {
shard: ShardIdentifier,
encrypted_call: Vec<u8>,
}
impl IndirectExection for IndirectCall {
fn execute(&self) -> {
match self {
ShielFunds(call) => call.execute(),
}
}
}
The cool thing about this would be that we can simply run a IndirectCalls::decode()
in the executor. If it succeeds, we know that the call should be handled, and we simply do call.execute()
in the executor. The bad thing is: indexes are hardcoded again, which gives a lot less flexibility. Even if we introduce a proc_macro
that connects to the parentchain to fetch the current indexes, it might still break running systems if the call index is changed in the parentchain. Hence, at some point, we have to care about the index of the parentchain metadata, and analyse at runtime if the supplied call matches. I propose that we add a layer of indirection:
pub trait MaybeIntoIndirectCall: IndirectExecution {
// Tries to interpret the slice as an Call if it is one.
pub fn maybe_into_indirect_call(Self, call: &mut [u8], node_metadata: NodeMetadata) -> Option<Self>;
}
impl MaybeIntoIndirectCall for IndirectCall {
pub fn maybe_into_indirect_call(Self, call: &mut [u8], node_metadata: NodeMetadata) -> Option<Self>{
// this will just skip the rest of the bytes.
let index = [u8; 2]::decode().ok()?;
if index == node_metadata.shield_funds_indexes() {
IndirectCall::ShieldFunds(Decode::decode(call).ok()?);
} else if {
// some other supported calls.
} else {
None
}
}
}
The executor code would contain the following calls:
// new traitbound for the executor
Call: MaybeIntoIndirectCall
for xt_opaque in block.extrinsics().iter() {
if let Some(call) = Call::maybe_into_indirect_call(&mut xt_opaque, &node_metadata) {
match call.execute() {
Ok() => executed_calls.push(hash_of(call),
Err(e) => // log....
}
}
}
Finally, we can even decide to expose the IndirectCalls
enum in the app-libs, so sdk users could adjust the enum without having to touch the indirect executor.