Skip to content

Commit

Permalink
fix no_std for xcq-extension (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
indirection42 authored Jul 16, 2024
1 parent 0772835 commit 2350545
Show file tree
Hide file tree
Showing 14 changed files with 370 additions and 1,376 deletions.
1,458 changes: 171 additions & 1,287 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ xcq-types = { path = "xcq-types", default-features = false }
polkavm = { path = "vendor/polkavm/crates/polkavm", default-features = false }

# polkadot-sdk
sp-api = { version = "29.0.0", default-features = false }
sp-api = { version = "31.0.0", default-features = false }

# nostd
parity-scale-codec = { version = "3.6.12", default-features = false, features = [
Expand All @@ -72,3 +72,4 @@ syn = { version = "2", features = ["full", "extra-traits"] }
quote = "1"
proc-macro2 = "1"
proc-macro-crate = "3"
Inflector = { version = "0.11.4" }
16 changes: 10 additions & 6 deletions poc/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ version = "0.1.0"
edition = "2021"

[dependencies]
parity-scale-codec = { version = "3.0.0", default-features = false }
scale-info = { version = "2.6.0", default-features = false }
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }
# this is a frame-based runtime, thus importing `frame` with runtime feature enabled.
frame = { version = "0.2.0", package = "polkadot-sdk-frame", default-features = false, features = [
"experimental",
Expand All @@ -23,8 +23,10 @@ pallet-transaction-payment-rpc-runtime-api = { version = "33.0.0", default-featu
# genesis builder that allows us to interacto with runtime genesis config
sp-genesis-builder = { version = "0.12.0", default-features = false }

poc-executor = { path = "../executor", default-features = false }
poc-extensions = { path = "../extensions", default-features = false }
xcq-executor = { workspace = true }
xcq-extension = { workspace = true }
xcq-extension-core = { workspace = true }
xcq-extension-fungibles = { workspace = true }

[dev-dependencies]
hex = "0.4"
Expand All @@ -49,6 +51,8 @@ std = [
"sp-genesis-builder/std",
"substrate-wasm-builder",

"poc-executor/std",
"poc-extensions/std",
"xcq-executor/std",
"xcq-extension/std",
"xcq-extension-core/std",
"xcq-extension-fungibles/std",
]
40 changes: 16 additions & 24 deletions poc/runtime/src/xcq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,50 @@ pub type XcqResponse = Vec<u8>;
pub type XcqError = String;
pub type XcqResult = Result<XcqResponse, XcqError>;

use poc_extensions::extension_core::{self, ExtensionCore};
use poc_extensions::extension_fungibles::{self, ExtensionFungibles};
use poc_extensions::{ExtensionsExecutor, Guest, Input, InvokeSource, Method};
use xcq_extension::{ExtensionsExecutor, Guest, Input, InvokeSource, Method};
decl_runtime_apis! {
pub trait XcqApi {
fn execute_query(query: Vec<u8>, input: Vec<u8>) -> XcqResult;
}
}

// extension_core impls
pub struct ExtensionCoreImpl;
pub struct ExtensionImpl;

pub struct ExtensionCoreConfigImpl;
impl extension_core::Config for ExtensionCoreConfigImpl {
impl xcq_extension_core::Config for ExtensionImpl {
type ExtensionId = u64;
}

impl ExtensionCore for ExtensionCoreImpl {
type Config = ExtensionCoreConfigImpl;
fn has_extension(id: <Self::Config as extension_core::Config>::ExtensionId) -> bool {
impl xcq_extension_core::ExtensionCore for ExtensionImpl {
type Config = ExtensionImpl;
fn has_extension(id: <Self::Config as xcq_extension_core::Config>::ExtensionId) -> bool {
matches!(id, 0 | 1)
}
}

// extension_fungibles impls
pub struct ExtensionFungiblesImpl;

pub struct ExtensionFungiblesConfigImpl;

impl extension_fungibles::Config for ExtensionFungiblesConfigImpl {
impl xcq_extension_fungibles::Config for ExtensionImpl {
type AccountId = crate::interface::AccountId;
type Balance = crate::interface::Balance;
type AssetId = crate::interface::AssetId;
}

type AccountIdFor<T> = <<T as ExtensionFungibles>::Config as extension_fungibles::Config>::AccountId;
type BalanceFor<T> = <<T as ExtensionFungibles>::Config as extension_fungibles::Config>::Balance;
type AssetIdFor<T> = <<T as ExtensionFungibles>::Config as extension_fungibles::Config>::AssetId;

impl ExtensionFungibles for ExtensionFungiblesImpl {
type Config = ExtensionFungiblesConfigImpl;
fn balance(asset: AssetIdFor<Self>, who: AccountIdFor<Self>) -> BalanceFor<Self> {
impl xcq_extension_fungibles::ExtensionFungibles for ExtensionImpl {
type Config = ExtensionImpl;
fn balance(
asset: xcq_extension_fungibles::AssetIdFor<Self>,
who: xcq_extension_fungibles::AccountIdFor<Self>,
) -> xcq_extension_fungibles::BalanceFor<Self> {
crate::Assets::balance(asset, who)
}
fn total_supply(asset: AssetIdFor<Self>) -> BalanceFor<Self> {
fn total_supply(asset: xcq_extension_fungibles::AssetIdFor<Self>) -> xcq_extension_fungibles::BalanceFor<Self> {
crate::Assets::total_supply(asset)
}
}

type Extensions = (
extension_core::Call<ExtensionCoreImpl>,
extension_fungibles::Call<ExtensionFungiblesImpl>,
xcq_extension_core::Call<ExtensionImpl>,
xcq_extension_fungibles::Call<ExtensionImpl>,
);

// guest impls
Expand Down
1 change: 1 addition & 0 deletions xcq-extension-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ version.workspace = true
[dependencies]
parity-scale-codec = { workspace = true }
xcq-extension = { workspace = true }
scale-info = { workspace = true }

[features]
default = ["std"]
Expand Down
2 changes: 2 additions & 0 deletions xcq-extension-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#![cfg_attr(not(feature = "std"), no_std)]
use parity_scale_codec::{Decode, Encode};
use scale_info::prelude::vec::Vec;
use xcq_extension::extension;

#[extension]
Expand Down
1 change: 1 addition & 0 deletions xcq-extension-fungibles/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ version.workspace = true
[dependencies]
parity-scale-codec = { workspace = true }
xcq-extension = { workspace = true }
scale-info = { workspace = true }

[features]
default = ["std"]
Expand Down
2 changes: 2 additions & 0 deletions xcq-extension-fungibles/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#![cfg_attr(not(feature = "std"), no_std)]
use parity_scale_codec::{Decode, Encode};
use scale_info::prelude::vec::Vec;
use xcq_extension::extension;

pub type AccountIdFor<T> = <<T as ExtensionFungibles>::Config as Config>::AccountId;
Expand Down
2 changes: 1 addition & 1 deletion xcq-extension/procedural/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fn call_enum_def(trait_ident: &Ident, methods: &[Method]) -> syn::Result<syn::It
// Add phantom data
variants.push(parse_quote!(
#[doc(hidden)]
__Phantom(std::marker::PhantomData<Impl>)
__Phantom(core::marker::PhantomData<Impl>)
));
Ok(parse_quote!(
#[derive(Decode)]
Expand Down
71 changes: 71 additions & 0 deletions xcq-runtime-api/procedural/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use inflector::Inflector;
use proc_macro2::{Span, TokenStream as TokenStream2};
use proc_macro_crate::{crate_name, FoundCrate};
use syn::{spanned::Spanned, Error, Ident, ItemImpl, Path, Result};
/// Should a qualified trait path be required?
///
/// e.g. `path::Trait` is qualified and `Trait` is not.
pub enum RequireQualifiedTraitPath {
Yes,
No,
}
/// Extract the trait that is implemented by the given `ItemImpl`.
pub fn extract_impl_trait(impl_: &ItemImpl, require: RequireQualifiedTraitPath) -> Result<&Path> {
impl_
.trait_
.as_ref()
.map(|v| &v.1)
.ok_or_else(|| Error::new(impl_.span(), "Only implementation of traits are supported!"))
.and_then(|p| {
if p.segments.len() > 1 || matches!(require, RequireQualifiedTraitPath::No) {
Ok(p)
} else {
Err(Error::new(
p.span(),
"The implemented trait has to be referenced with a path, \
e.g. `impl xcq_extension_core::ExtensionCore for Runtime`.",
))
}
})
}

/// Generates the name of the module that contains the trait declaration for the runtime.
pub fn generate_runtime_mod_name_for_trait(trait_: &Ident) -> Ident {
Ident::new(
&format!("runtime_decl_for_{}", trait_.to_string().to_snake_case()),
Span::call_site(),
)
}

/// Generate the crate access for the crate using 2018 syntax.
///
/// If `frame` is in scope, it will use `polkadot_sdk_frame::deps::<def_crate>`. Else, it will try
/// and find `<def_crate>` directly.
pub fn generate_access_from_frame_or_crate(def_crate: &str) -> Result<syn::Path, Error> {
if let Some(path) = get_frame_crate_path(def_crate) {
Ok(path)
} else if let Some(path) = get_sdk_crate_path(def_crate) {
Ok(path)
} else {
let ident = match crate_name(def_crate) {
Ok(FoundCrate::Itself) => {
let name = def_crate.to_string().replace("-", "_");
Ok(syn::Ident::new(&name, Span::call_site()))
}
Ok(FoundCrate::Name(name)) => Ok(Ident::new(&name, Span::call_site())),
Err(e) => Err(Error::new(Span::call_site(), e)),
}?;

Ok(syn::Path::from(ident))
}
}
fn import_xcq_types() -> TokenStream2 {
let found_crate = crate_name("xcq-types").expect("xcq-types not found in Cargo.toml");
match found_crate {
FoundCrate::Itself => quote! { crate },
FoundCrate::Name(name) => {
let name = syn::Ident::new(&name, proc_macro2::Span::call_site());
quote! { ::#name }
}
}
}
4 changes: 4 additions & 0 deletions xcq-test-runner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ version.workspace = true
clap = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
parity-scale-codec = { workspace = true, features = ["std"] }

xcq-executor = { workspace = true, features = ["std"] }
xcq-extension = { workspace = true, features = ["std"] }
xcq-extension-core = { workspace = true, features = ["std"] }
xcq-extension-fungibles = { workspace = true, features = ["std"] }

polkavm = { workspace = true }
138 changes: 86 additions & 52 deletions xcq-test-runner/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,7 @@
use clap::Parser;
use polkavm::{Caller, Config, Linker};
use parity_scale_codec::{Decode, Encode};
use tracing_subscriber::prelude::*;
use xcq_executor::{XcqExecutor, XcqExecutorContext};

struct HostFunctions;

#[derive(Clone, Copy)]
#[repr(C)]
struct GuestArgs {
arg0: u32,
arg1: u32,
}

#[derive(Clone, Copy)]
#[repr(C)]
struct GuestReturn {
data0: u64,
data1: u64,
}

impl XcqExecutorContext for HostFunctions {
fn register_host_functions<T>(&mut self, linker: &mut Linker<T>) {
linker
.func_wrap(
"host_call_impl",
move |mut caller: Caller<_>, args_ptr: u32, out_ptr: u32| {
let args_ptr = args_ptr as *const GuestArgs;
let args_size = core::mem::size_of::<GuestArgs>();
// First we read the args from the guest memory
let args_in_bytes = caller.read_memory_into_vec(args_ptr as u32, args_size as u32).unwrap();
let args: GuestArgs = unsafe { std::ptr::read(args_in_bytes.as_ptr() as *const GuestArgs) };
println!("host_call: arg0: {:?}", args.arg0);
let res = GuestReturn {
data0: (args.arg0 + 1) as u64,
data1: args.arg1 as u64,
};
let res_bytes = unsafe {
std::slice::from_raw_parts(
&res as *const GuestReturn as *const u8,
core::mem::size_of::<GuestReturn>(),
)
};
caller.write_memory(out_ptr, res_bytes).unwrap();
},
)
.unwrap();
}
}
use xcq_extension::{ExtensionId, ExtensionsExecutor, Guest, Input, InvokeSource, Method};

#[derive(Parser, Debug)]
#[command(version, about)]
Expand All @@ -72,12 +27,91 @@ fn main() {

let raw_blob = std::fs::read(cli.program).expect("Failed to read program");

let config = Config::from_env().unwrap();
let mut executor = ExtensionsExecutor::<Extensions, ()>::new(InvokeSource::RuntimeAPI);

let mut executor: XcqExecutor<HostFunctions> = XcqExecutor::new(config, HostFunctions);
let res = executor.execute(&raw_blob[..], "main", &[0u8]).unwrap();
let guest = GuestImpl {
program: raw_blob.to_vec(),
};
let method = CoreMethod::HasExtension { id: 0 };
let mut input_data = <xcq_extension_core::Call<ExtensionImpl> as ExtensionId>::EXTENSION_ID.encode();
input_data.extend_from_slice(&method.encode());
let input = InputImpl {
method: "main".to_string(),
args: input_data,
};
let res = executor.execute_method(guest, input).unwrap();
tracing::info!("Result: {:?}", res);
}

let res = executor.execute(&raw_blob[..], "main", &[1u8, 40u8]).unwrap();
tracing::info!("Result: {:?}", res);
// extension_core impls
pub struct ExtensionImpl;
impl xcq_extension_core::Config for ExtensionImpl {
type ExtensionId = u64;
}
impl xcq_extension_core::ExtensionCore for ExtensionImpl {
type Config = Self;
fn has_extension(id: <Self::Config as xcq_extension_core::Config>::ExtensionId) -> bool {
matches!(id, 0 | 1)
}
}

// extension_fungibles impls
impl xcq_extension_fungibles::Config for ExtensionImpl {
type AccountId = [u8; 32];
type Balance = u32;
type AssetId = u64;
}

impl xcq_extension_fungibles::ExtensionFungibles for ExtensionImpl {
type Config = Self;
fn balance(
_asset: xcq_extension_fungibles::AssetIdFor<Self>,
_who: xcq_extension_fungibles::AccountIdFor<Self>,
) -> xcq_extension_fungibles::BalanceFor<Self> {
0
}
fn total_supply(_asset: xcq_extension_fungibles::AssetIdFor<Self>) -> xcq_extension_fungibles::BalanceFor<Self> {
100
}
}

type Extensions = (
xcq_extension_core::Call<ExtensionImpl>,
xcq_extension_fungibles::Call<ExtensionImpl>,
);

// guest impls
pub struct GuestImpl {
pub program: Vec<u8>,
}

impl Guest for GuestImpl {
fn program(&self) -> &[u8] {
&self.program
}
}

pub struct InputImpl {
pub method: Method,
pub args: Vec<u8>,
}

impl Input for InputImpl {
fn method(&self) -> Method {
self.method.clone()
}
fn args(&self) -> &[u8] {
&self.args
}
}

#[derive(Encode, Decode)]
enum CoreMethod {
HasExtension { id: u64 },
}

#[derive(Encode, Decode)]
enum FungiblesMethod {
TotalSupply { asset: u64 },
Balance { asset: u64, who: [u8; 32] },
}
Loading

0 comments on commit 2350545

Please sign in to comment.