-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: prototype extension executor and extension trait
- Loading branch information
1 parent
3bbcb6d
commit 0867526
Showing
10 changed files
with
190 additions
and
84 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use super::{Extension, ExtensionTypeId}; | ||
pub trait ExtensionCore: Extension { | ||
// ExtensionId should be generated by the macro | ||
// It should normalize the order of methods and parameter names | ||
const TYPE_ID: ExtensionTypeId = 0u64; | ||
// TODO: Actual args and return values are complex types | ||
// and we adapt them to polkavm ABI in `impl XcqExecutorContext for HostFunctions` | ||
fn some_host_function() -> u32; | ||
fn another_host_function() -> u32; | ||
} | ||
|
||
#[derive(Clone)] | ||
struct ExtensionCoreImpl; | ||
|
||
impl Extension for ExtensionCoreImpl { | ||
fn methods(&self) -> Vec<String> { | ||
vec!["some_host_function".to_string(), "another_host_function".to_string()] | ||
} | ||
} | ||
|
||
impl ExtensionCore for ExtensionCoreImpl { | ||
fn some_host_function() -> u32 { | ||
100 | ||
} | ||
fn another_host_function() -> u32 { | ||
42 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use super::{Extension, ExtensionTypeId}; | ||
pub trait ExtensionFungibles: Extension { | ||
// ExtensionId should be generated by the macro | ||
// It should normalize the order of methods and parameter names | ||
const TYPE_ID: ExtensionTypeId = 1u64; | ||
// TODO: Actual args and return values are complex types | ||
// and we adapt them to polkavm ABI in `impl XcqExecutorContext for HostFunctions` | ||
fn transfer(from: u32, to: u32, amount: u64) -> u64; | ||
fn balance(account: u32) -> u64; | ||
} | ||
|
||
#[derive(Clone)] | ||
struct ExtensionFungiblesImpl; | ||
|
||
impl Extension for ExtensionFungiblesImpl { | ||
fn methods(&self) -> Vec<String> { | ||
vec!["transfer".to_string(), "balance".to_string()] | ||
} | ||
} | ||
|
||
impl ExtensionFungibles for ExtensionFungiblesImpl { | ||
fn transfer(_from: u32, _to: u32, _amount: u64) -> u64 { | ||
unimplemented!() | ||
} | ||
fn balance(_account: u32) -> u64 { | ||
unimplemented!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
use super::ExtensionTypeId; | ||
pub trait Guest { | ||
fn type_id(&self) -> ExtensionTypeId; | ||
fn program(&self) -> &[u8]; | ||
} | ||
|
||
struct GuestImpl { | ||
extension_type: ExtensionTypeId, | ||
program: Vec<u8>, | ||
} | ||
|
||
impl Guest for GuestImpl { | ||
fn type_id(&self) -> ExtensionTypeId { | ||
self.extension_type | ||
} | ||
fn program(&self) -> &[u8] { | ||
&self.program | ||
} | ||
} | ||
|
||
type Method = String; | ||
|
||
pub trait Input { | ||
fn method(&self) -> Method; | ||
fn args(&self) -> &[u8]; | ||
} | ||
|
||
struct InputImpl { | ||
method: Method, | ||
args: Vec<u8>, | ||
} | ||
|
||
impl Input for InputImpl { | ||
fn method(&self) -> Method { | ||
self.method.clone() | ||
} | ||
fn args(&self) -> &[u8] { | ||
&self.args | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,102 +1,95 @@ | ||
#![cfg_attr(not(feature = "std"), no_std)] | ||
|
||
use poc_executor::{XcqExecutor, XcqExecutorContext}; | ||
#[cfg(not(feature = "std"))] | ||
use scale_info::prelude::{format, string::String}; | ||
pub type XcqResponse = Vec<u8>; | ||
pub type XcqError = String; | ||
pub type XcqResult = Result<XcqResponse, XcqError>; | ||
|
||
mod extension_core; | ||
pub use extension_core::ExtensionCore; | ||
mod extension_fungibles; | ||
pub use extension_fungibles::ExtensionFungibles; | ||
mod guest; | ||
pub use guest::{Guest, Input}; | ||
|
||
mod macros; | ||
|
||
type ExtensionTypeId = u32; | ||
type Error = String; | ||
|
||
// Runtime Side | ||
// General trait for all host extension interfaces | ||
pub trait HostInterface { | ||
fn type_id(&self) -> ExtensionTypeId; | ||
fn methods(&self) -> Vec<String>; | ||
} | ||
|
||
// Example extension trait | ||
// Implemented by Runtime | ||
pub trait SomeHostInterface { | ||
type HostFunctions: XcqExecutorContext; | ||
// TODO: should be generated automatically by macro | ||
const EXTENSION_TYPE_ID: ExtensionTypeId; | ||
fn some_host_function() -> XcqResult; | ||
fn another_host_function() -> XcqResult; | ||
} | ||
|
||
// Guest Side | ||
pub trait Guest { | ||
fn type_id(&self) -> ExtensionTypeId; | ||
type ExtensionTypeId = u64; | ||
// General trait for all extension interfaces | ||
pub trait Extension { | ||
fn methods(&self) -> Vec<String>; | ||
} | ||
|
||
struct GuestImpl { | ||
program: Vec<u8>, | ||
// Aggregated struct for all extension functions that runtime supports | ||
// Generated by some macros | ||
pub struct HostFunctions<E1: ExtensionCore, E2: ExtensionFungibles> { | ||
phantom: core::marker::PhantomData<(E1, E2)>, | ||
} | ||
|
||
impl Guest for GuestImpl { | ||
fn type_id(&self) -> ExtensionTypeId { | ||
unimplemented!() | ||
} | ||
fn methods(&self) -> Vec<String> { | ||
unimplemented!() | ||
// Generated by some macros | ||
impl<E1: ExtensionCore, E2: ExtensionFungibles> XcqExecutorContext for HostFunctions<E1, E2> { | ||
fn register_host_functions<T>(&mut self, linker: &mut poc_executor::Linker<T>) { | ||
// As aforementioned, we may use macros to directly copy the functions impls to here | ||
linker | ||
.func_wrap("some_host_function", move || -> u32 { | ||
<E1 as ExtensionCore>::some_host_function() | ||
}) | ||
.unwrap(); | ||
linker | ||
.func_wrap("another_host_function", || -> u32 { | ||
<E1 as ExtensionCore>::another_host_function() | ||
}) | ||
.unwrap(); | ||
linker | ||
.func_wrap("transfer", move |from: u32, to: u32, amount: u64| -> u64 { | ||
<E2 as ExtensionFungibles>::transfer(from, to, amount) | ||
}) | ||
.unwrap(); | ||
linker | ||
.func_wrap("balance", move |account: u32| -> u64 { | ||
<E2 as ExtensionFungibles>::balance(account) | ||
}) | ||
.unwrap(); | ||
} | ||
} | ||
|
||
type Method = String; | ||
// Which should be implemented by the runtime | ||
pub trait ExtensionsExecutor { | ||
type SafeGuard: PermController; | ||
fn register_host_interface<H: HostInterface>(&mut self, host_interface: H) -> Result<(), Error>; | ||
fn support_extension_types() -> Result<Vec<ExtensionTypeId>, Error>; | ||
// extension type is opaque to the runtime | ||
// or we parse it before | ||
fn discover_methods<G: Guest>(guest: G) -> Result<Vec<Method>, Error>; | ||
fn execute_method<G: Guest>(&self, guest: G, method: Method, input: Vec<u8>) -> XcqResult; | ||
} | ||
pub trait PermController { | ||
fn check<G: Guest>(extension: &E, method: &Method) -> Result<(), Error>; | ||
} | ||
// pub trait ExtensionsExecutor { | ||
// type ExecutorType; | ||
// fn from_extensions()-> | ||
// fn execute_method(&self, guest: Guest, input: Input) -> XcqResult; | ||
// } | ||
|
||
struct SimplePermController; | ||
impl PermController for SimplePermController { | ||
fn check<E: Extension>(extension: &E, method: &Method) -> Result<(), Error> { | ||
unimplemented!() | ||
} | ||
// Expanded | ||
// In practice, generics are generated by macros | ||
struct ExtensionsExecutor<E1: ExtensionCore, E2: ExtensionFungibles> { | ||
executor: XcqExecutor<HostFunctions<E1, E2>>, | ||
} | ||
|
||
// Mock implementation | ||
struct ExtensionApiImpl; | ||
impl ExtensionsExecutor for ExtensionApiImpl { | ||
type SafeGuard = SimplePermController; | ||
fn support_extension_types() -> Result<Vec<ExtensionTypeId>, Error> { | ||
unimplemented!() | ||
impl<E1: ExtensionCore, E2: ExtensionFungibles> ExtensionsExecutor<E1, E2> { | ||
// Generated by macro | ||
pub fn from_extensions(_extension_core: E1, _extension_fungibles: E2) -> Self { | ||
let host_functions = HostFunctions::<E1, E2> { | ||
phantom: core::marker::PhantomData, | ||
}; | ||
let executor = XcqExecutor::new(Default::default(), host_functions); | ||
Self { executor } | ||
} | ||
fn register_host_interface<H: HostInterface>(&mut self, host_interface: H) -> Result<(), Error> { | ||
unimplemented!() | ||
} | ||
fn discover_methods<G: Guest>(guest_impl: G) -> Result<Vec<Method>, Error> { | ||
// TODO: extension is opaque to the runtime, | ||
// we need to use polkavm discover symbols to get the methods | ||
unimplemented!() | ||
} | ||
fn execute_method<G: Guest>(&self, guest: G, method: Method, input: Vec<u8>) -> XcqResult { | ||
// Check if the extension is supported | ||
let extension_ids = Self::support_extension_types()?; | ||
if !extension_ids.contains(&guest.type_id()) { | ||
return Err("Extension not supported".to_string()); | ||
} | ||
// Check if the method is supported | ||
let methods = Self::discover_methods(&guest)?; | ||
if !methods.contains(&method) { | ||
return Err("Method not supported".to_string()); | ||
// In PoC, guest and input are opague to the runtime | ||
// In SDK, we can make them has type | ||
fn execute_method<G: Guest, I: Input>(&self, guest: G, input: I) -> XcqResult { | ||
let extension_id = guest.type_id(); | ||
// In practice, these match statements should be generated by some macros | ||
// Check if the runtime supports the extension | ||
match extension_id { | ||
// TODO: E1, E2 information are discarded after `HostFunctions` is generated | ||
// How to fix it? | ||
E1::TYPE_ID | E2::TYPE_ID => {} | ||
_ => return Err(format!("Unsupported extension: {}", extension_id)), | ||
} | ||
// Check if the method pass the safe guard | ||
Self::SafeGuard::check(&guest, &method)?; | ||
// TODO: Execute the method | ||
Ok(vec![]) | ||
self.executor | ||
.execute(guest.program(), input.method(), input.args()) | ||
.map_err(|e| format!("{:?}", e)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
pub trait PermController { | ||
fn check<G: Guest>(extension: &E, method: &Method) -> Result<(), Error>; | ||
} | ||
|
||
struct SimplePermController; | ||
impl PermController for SimplePermController { | ||
fn check<E: Extension>(extension: &E, method: &Method) -> Result<(), Error> { | ||
unimplemented!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters