Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

121 changes: 121 additions & 0 deletions frame-weight-template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{{header}}
//! Autogenerated weights for `{{pallet}}`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}}
//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`
//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`
//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`
//! EXECUTION: `{{cmd.execution}}`, WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`

// Executed Command:
{{#each args as |arg|}}
// {{arg}}
{{/each}}

#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]

use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use core::marker::PhantomData;

/// Weight functions needed for `{{pallet}}`.
pub trait WeightInfo {
{{#each benchmarks as |benchmark|}}
fn {{benchmark.name~}}
(
{{~#each benchmark.components as |c| ~}}
{{c.name}}: u32, {{/each~}}
) -> Weight;
{{/each}}
}

/// Weights for `{{pallet}}` using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
{{#if (eq pallet "frame_system")}}
impl<T: crate::Config> WeightInfo for SubstrateWeight<T> {
{{else}}
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
{{/if}}
{{#each benchmarks as |benchmark|}}
{{#each benchmark.comments as |comment|}}
/// {{comment}}
{{/each}}
{{#each benchmark.component_ranges as |range|}}
/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.
{{/each}}
fn {{benchmark.name~}}
(
{{~#each benchmark.components as |c| ~}}
{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}
) -> Weight {
// Proof Size summary in bytes:
// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.
Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})
{{#each benchmark.component_weight as |cw|}}
// Standard Error: {{underscore cw.error}}
.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))
{{/each}}
{{#if (ne benchmark.base_reads "0")}}
.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))
{{/if}}
{{#each benchmark.component_reads as |cr|}}
.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))
{{/each}}
{{#if (ne benchmark.base_writes "0")}}
.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))
{{/if}}
{{#each benchmark.component_writes as |cw|}}
.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))
{{/each}}
{{#each benchmark.component_calculated_proof_size as |cp|}}
.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))
{{/each}}
}
{{/each}}
}

// For backwards compatibility and tests.
impl WeightInfo for () {
{{#each benchmarks as |benchmark|}}
{{#each benchmark.comments as |comment|}}
/// {{comment}}
{{/each}}
{{#each benchmark.component_ranges as |range|}}
/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.
{{/each}}
fn {{benchmark.name~}}
(
{{~#each benchmark.components as |c| ~}}
{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}
) -> Weight {
// Proof Size summary in bytes:
// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.
Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})
{{#each benchmark.component_weight as |cw|}}
// Standard Error: {{underscore cw.error}}
.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))
{{/each}}
{{#if (ne benchmark.base_reads "0")}}
.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))
{{/if}}
{{#each benchmark.component_reads as |cr|}}
.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))
{{/each}}
{{#if (ne benchmark.base_writes "0")}}
.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))
{{/if}}
{{#each benchmark.component_writes as |cw|}}
.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))
{{/each}}
{{#each benchmark.component_calculated_proof_size as |cp|}}
.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))
{{/each}}
}
{{/each}}
}
2 changes: 1 addition & 1 deletion node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pallet-transaction-payment-rpc.workspace = true
substrate-frame-rpc-system.workspace = true
substrate-frame-rpc-system.default-features = true
frame-benchmarking-cli.workspace = true
frame-benchmarking-cli.default-features = false
frame-benchmarking-cli.default-features = true
solochain-template-runtime.workspace = true

[build-dependencies]
Expand Down
106 changes: 84 additions & 22 deletions pallets/algorithms/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,43 @@ pub mod pallet {
use sp_runtime::Vec;
use sp_runtime::traits::Hash;
use wasmi::{Func, Caller};
use wasmi::core::Trap;

use pallet_credentials::{self as credentials, Attestations, CredAttestation, CredSchema, AcquirerAddress};

use super::*;

#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct GasMeter {
pub consumed: u64,
pub limit: u64,
}

impl GasMeter {
pub fn new(limit: u64) -> Self {
Self {
consumed: 0,
limit
}
}

pub fn charge(&mut self, amount: u64) -> Result<(), DispatchError> {
self.consumed = self.consumed.checked_add(amount)
.ok_or(DispatchError::Other("Gas Overflow"))?;

if self.consumed > self.limit {
return Err(DispatchError::Other("Out of Gas"));
}
Ok(())
}
}

#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct Algorithm<T: Config> {
pub schema_hashes: BoundedVec<T::Hash, T::MaxSchemas>,
pub code: BoundedVec<u8, T::MaxCodeSize>,
pub gas_limit: u64,
}

#[pallet::pallet]
Expand All @@ -39,6 +66,22 @@ pub mod pallet {

#[pallet::constant]
type MaxCodeSize: Get<u32>;

#[pallet::constant]
type MaxMemoryPages: Get<u32>;

#[pallet::constant]
type DefaultGasLimit: Get<u64>;

#[pallet::constant]
type GasCost: Get<GasCosts>;
}

#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct GasCosts {
pub basic_op: u64,
pub memory_op: u64,
pub call_op: u64,
}

#[pallet::storage]
Expand Down Expand Up @@ -74,15 +117,19 @@ pub mod pallet {
AlgoError6,
InvalidWasmProvided,
TooManySchemas,
CodeTooHeavy

CodeTooHeavy,
AlgoExecutionFailed,
TooComplexModule,
OutOfGas,
GasOverflow,
GasMeteringNotSupported,
}

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(1)]
#[pallet::weight(100_000)]
pub fn save_algo(origin: OriginFor<T>, schema_hashes: Vec<T::Hash>, code: Vec<u8>) -> DispatchResult {
pub fn save_algo(origin: OriginFor<T>, schema_hashes: Vec<T::Hash>, code: Vec<u8>, gas_limit: Option<u64>) -> DispatchResult {
let who = ensure_signed(origin)?;

ensure!(schema_hashes.len() <= T::MaxSchemas::get() as usize, Error::<T>::TooManySchemas);
Expand All @@ -103,6 +150,7 @@ pub mod pallet {
Algorithms::<T>::insert(id, Algorithm {
schema_hashes: BoundedVec::try_from(schema_hashes).map_err(|_| Error::<T>::TooManySchemas)?,
code: BoundedVec::try_from(code).map_err(|_| Error::<T>::CodeTooHeavy)?,
gas_limit: gas_limit.unwrap_or_else(|| T::DefaultGasLimit::get()),
});

Self::deposit_event(Event::AlgorithmAdded {
Expand Down Expand Up @@ -130,53 +178,64 @@ pub mod pallet {
}


return Pallet::<T>::run_code(algorithm.code.to_vec(), attestations);
return Pallet::<T>::run_code(algorithm.code.to_vec(), attestations, algorithm.gas_limit);
}
}

impl<T: Config> Pallet<T> {
pub fn run_code(code: Vec<u8>, attestations: Vec<CredAttestation<T>>) -> DispatchResult {
pub fn run_code(code: Vec<u8>, attestations: Vec<CredAttestation<T>>, gas_limit: u64) -> DispatchResult {
let engine = wasmi::Engine::default();

let gas_meter = GasMeter::new(gas_limit);

let module =
wasmi::Module::new(&engine, code.as_slice()).map_err(|_| Error::<T>::AlgoError1)?;
wasmi::Module::new(&engine, code.as_slice()).map_err(|_| Error::<T>::InvalidWasmProvided)?;

type HostState = u32;
let mut store = wasmi::Store::new(&engine, 42);
let mut store = wasmi::Store::new(&engine, gas_meter);

let host_print = wasmi::Func::wrap(
&mut store,
|caller: wasmi::Caller<'_, HostState>, param: i32| {
|mut caller: wasmi::Caller<'_, GasMeter>, param: i32| {
caller.data_mut().charge(T::GasCost::get().basic_op).map_err(|_| Trap::new("Gas charge failed"))?;
log::debug!(target: "algo", "Message:{:?}", param);
Ok(())
},
);

let abort_func = wasmi::Func::wrap(
&mut store,
|_: Caller<'_, HostState>, msg_id: i32, filename: i32, line: i32, col: i32| {
|mut caller: Caller<'_, GasMeter>, msg_id: i32, filename: i32, line: i32, col: i32| -> Result<(), Trap> {
caller.data_mut().charge(T::GasCost::get().call_op).map_err(|_| Trap::new("Gas charge failed"))?;
log::error!(
target: "algo",
"Abort called: msg_id={}, file={}, line={}, col={}",
msg_id, filename, line, col
);
// Err(wasmi::Trap::new(wasmi::TrapKind::Unreachable))
Err(Trap::new("Gas charge failed"))
},
);

let memory = wasmi::Memory::new(
&mut store,
wasmi::MemoryType::new(8, None).map_err(|_| Error::<T>::AlgoError2)?,
wasmi::MemoryType::new(T::MaxMemoryPages::get(), Some(T::MaxMemoryPages::get())).map_err(|_| Error::<T>::AlgoError2)?,
)
.map_err(|_| Error::<T>::AlgoError2)?;


let bytes = attestations.into_iter().flatten().flatten().collect::<Vec<u8>>();

memory.write(&mut store, 0, &bytes).map_err(|e| {
log::error!(target: "algo", "Algo1 {:?}", e);
log::error!(target: "algo", "Memory write error {:?}", e);
Error::<T>::AlgoError1
})?;

store.data_mut().charge(
T::GasCost::get().memory_op * (bytes.len() as u64 / 32 + 1))
.map_err(|_| Error::<T>::OutOfGas)?;

// memory.write(&mut store, 0, 5);

let mut linker = <wasmi::Linker<HostState>>::new(&engine);
let mut linker = <wasmi::Linker<GasMeter>>::new(&engine);
linker.define("host", "print", host_print).map_err(|_| Error::<T>::AlgoError2)?;
linker.define("env", "memory", memory).map_err(|_| Error::<T>::AlgoError2)?;

Expand All @@ -185,24 +244,27 @@ pub mod pallet {

log::error!(target: "algo", "Algo3 {:?}", bytes.clone());
log::error!(target: "algo", "Algo3 {:?}", bytes.len());

let instance = linker
.instantiate(&mut store, &module)
.map_err(|e| {
log::error!(target: "algo", "Algo3 {:?}", e);
Error::<T>::AlgoError3
})?
.start(&mut store)
.map_err(|_| Error::<T>::AlgoError4)?;
.instantiate(&mut store, &module)
.map_err(|e| {
log::error!(target: "algo", "Instantiation error {:?}", e);
Error::<T>::AlgoError3
})?
.start(&mut store)
.map_err(|_| Error::<T>::AlgoError4)?;

let calc = instance
.get_typed_func::<(), i64>(&store, "calc")
.map_err(|_| Error::<T>::AlgoError5)?;

// And finally we can call the wasm!
let result = calc.call(&mut store, ()).map_err(|e| {
log::error!(target: "algo", "Algo6 {:?}", e);
log::error!(target: "algo", "Execution error {:?}", e);
Error::<T>::AlgoError6
})?;


Self::deposit_event(Event::AlgoResult {
result,
});
Expand Down
8 changes: 5 additions & 3 deletions pallets/credentials/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ codec = { features = [
scale-info = { features = [
"derive",
], workspace = true }
frame-benchmarking = { optional = true, workspace = true }
frame-benchmarking.workspace = true
frame-support.workspace = true
frame-system.workspace = true
pallet-balances.workspace = true
pallet-issuers = { path = "../issuers", default-features = false }
bs58 = { version = "0.5.1", default-features = false }
sp-std.workspace = true
Expand All @@ -39,13 +40,14 @@ sp-runtime = { default-features = true, workspace = true }
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"scale-info/std",
"sp-runtime/std",
"pallet-issuers/std",
"sp-core/std"
"sp-core/std",
"pallet-balances/std"
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
Expand Down
Loading