diff --git a/Cargo.lock b/Cargo.lock index 9c739c3..5cbb3c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1689,6 +1689,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + [[package]] name = "dtoa" version = "1.0.9" @@ -3010,6 +3016,12 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + [[package]] name = "indicatif" version = "0.17.5" @@ -3381,6 +3393,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "libp2p" version = "0.51.3" @@ -3943,9 +3961,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lru" @@ -4678,7 +4696,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" dependencies = [ "cfg-if", - "libm", + "libm 0.1.4", ] [[package]] @@ -4792,11 +4810,13 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "parity-scale-codec", "scale-info", "sp-core", "sp-io", "sp-runtime", + "wasmi", ] [[package]] @@ -5794,7 +5814,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -8074,6 +8094,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spinners" version = "4.1.0" @@ -9279,6 +9305,37 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasmi" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acfc1e384a36ca532d070a315925887247f3c7e23567e23e0ac9b1c5d6b8bf76" +dependencies = [ + "smallvec", + "spin 0.9.8", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm 0.2.8", + "num-traits", + "paste", +] + [[package]] name = "wasmparser" version = "0.102.0" @@ -9289,6 +9346,15 @@ dependencies = [ "url", ] +[[package]] +name = "wasmparser-nostd" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" +dependencies = [ + "indexmap-nostd", +] + [[package]] name = "wasmtime" version = "8.0.1" diff --git a/asm-scripts/.gitignore b/asm-scripts/.gitignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/asm-scripts/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/asm-scripts/asconfig.json b/asm-scripts/asconfig.json new file mode 100644 index 0000000..3a14874 --- /dev/null +++ b/asm-scripts/asconfig.json @@ -0,0 +1,26 @@ +{ + "targets": { + "debug": { + "outFile": "build/debug.wasm", + "textFile": "build/debug.wat", + "sourceMap": true, + "debug": true + }, + "release": { + "outFile": "build/release.wasm", + "textFile": "build/release.wat", + "sourceMap": true, + "optimizeLevel": 3, + "shrinkLevel": 3 + } + }, + "options": { + "bindings": "raw", + "noExportMemory": true, + "importMemory": true, + "exportTable": false, + "importTable": false, + "exportStart": false, + "noAssert": true + } +} \ No newline at end of file diff --git a/asm-scripts/assembly/index.ts b/asm-scripts/assembly/index.ts new file mode 100644 index 0000000..8a6df90 --- /dev/null +++ b/asm-scripts/assembly/index.ts @@ -0,0 +1,11 @@ +@external("host", "print") +export declare function print(i: i32): void + +export function calc(): i64 { + const a = i32.load(0); + print(memory.size()) + print(a); + const b = i32.load(sizeof()); + print(b); + return a * b; +} \ No newline at end of file diff --git a/asm-scripts/assembly/tsconfig.json b/asm-scripts/assembly/tsconfig.json new file mode 100644 index 0000000..e28fcf2 --- /dev/null +++ b/asm-scripts/assembly/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": [ + "./**/*.ts" + ] +} \ No newline at end of file diff --git a/asm-scripts/build/.gitignore b/asm-scripts/build/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/asm-scripts/build/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/asm-scripts/index.html b/asm-scripts/index.html new file mode 100644 index 0000000..c170dde --- /dev/null +++ b/asm-scripts/index.html @@ -0,0 +1,10 @@ + + + + + + + diff --git a/asm-scripts/package-lock.json b/asm-scripts/package-lock.json new file mode 100644 index 0000000..78c1699 --- /dev/null +++ b/asm-scripts/package-lock.json @@ -0,0 +1,54 @@ +{ + "name": "asm-scripts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "asm-scripts", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "assemblyscript": "^0.27.23" + } + }, + "node_modules/assemblyscript": { + "version": "0.27.23", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.27.23.tgz", + "integrity": "sha512-+oLTB2IapORXof+bel+HliNUuEScUW4jpBIwV3Y4fDGiT0cu1qI+AJ1SG2RbJqvMo7fbUBGXv2ESq3iE9hK2rQ==", + "dev": true, + "dependencies": { + "binaryen": "116.0.0-nightly.20240114", + "long": "^5.2.1" + }, + "bin": { + "asc": "bin/asc.js", + "asinit": "bin/asinit.js" + }, + "engines": { + "node": ">=16", + "npm": ">=7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/assemblyscript" + } + }, + "node_modules/binaryen": { + "version": "116.0.0-nightly.20240114", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-116.0.0-nightly.20240114.tgz", + "integrity": "sha512-0GZrojJnuhoe+hiwji7QFaL3tBlJoA+KFUN7ouYSDGZLSo9CKM8swQX8n/UcbR0d1VuZKU+nhogNzv423JEu5A==", + "dev": true, + "bin": { + "wasm-opt": "bin/wasm-opt", + "wasm2js": "bin/wasm2js" + } + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "dev": true + } + } +} diff --git a/asm-scripts/package.json b/asm-scripts/package.json new file mode 100644 index 0000000..953bc35 --- /dev/null +++ b/asm-scripts/package.json @@ -0,0 +1,25 @@ +{ + "name": "asm-scripts", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "node tests", + "asbuild:debug": "asc assembly/index.ts --target debug", + "asbuild:release": "asc assembly/index.ts --target release", + "asbuild": "npm run asbuild:debug && npm run asbuild:release", + "start": "npx serve ." + }, + "author": "", + "license": "ISC", + "devDependencies": { + "assemblyscript": "^0.27.23" + }, + "type": "module", + "exports": { + ".": { + "import": "./build/release.js", + "types": "./build/release.d.ts" + } + } +} \ No newline at end of file diff --git a/asm-scripts/tests/index.js b/asm-scripts/tests/index.js new file mode 100644 index 0000000..769a0b0 --- /dev/null +++ b/asm-scripts/tests/index.js @@ -0,0 +1,4 @@ +import assert from "assert"; +import { add } from "../build/debug.js"; +assert.strictEqual(add(1, 2), 3); +console.log("ok"); diff --git a/pallets/template/Cargo.toml b/pallets/template/Cargo.toml index 0f01e33..18c3b0a 100644 --- a/pallets/template/Cargo.toml +++ b/pallets/template/Cargo.toml @@ -13,18 +13,22 @@ repository = "https://github.com/substrate-developer-hub/substrate-node-template targets = ["x86_64-unknown-linux-gnu"] [dependencies] +log = "0.4.20" codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", ] } -scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = [ + "derive", +] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } +wasmi = { version = "0.31.1", default-features = false } +sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } [dev-dependencies] sp-core = { version = "21.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } sp-io = { version = "23.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } -sp-runtime = { version = "24.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } [features] default = ["std"] @@ -33,7 +37,9 @@ std = [ "frame-benchmarking?/std", "frame-support/std", "frame-system/std", + "sp-runtime/std", "scale-info/std", + "wasmi/std" ] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/template/src/lib.rs b/pallets/template/src/lib.rs index 9550d3d..e0523ff 100644 --- a/pallets/template/src/lib.rs +++ b/pallets/template/src/lib.rs @@ -14,13 +14,17 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; pub mod weights; +use frame_support::dispatch::*; pub use weights::*; #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use frame_support::{dispatch, dispatch::*, pallet_prelude::*}; use frame_system::pallet_prelude::*; + use scale_info::prelude; + use sp_runtime::{FixedI64, FixedPointNumber, Rounding}; + use wasmi::{self, core::F64, Value}; #[pallet::pallet] pub struct Pallet(_); @@ -49,7 +53,13 @@ pub mod pallet { pub enum Event { /// Event documentation should end with an array that provides descriptive names for event /// parameters. [something, who] - SomethingStored { something: u32, who: T::AccountId }, + SomethingStored { + something: u32, + who: T::AccountId, + }, + AlgoResult { + result: i64, + }, } // Errors inform users that something went wrong. @@ -59,6 +69,12 @@ pub mod pallet { NoneValue, /// Errors should have helpful documentation associated with them. StorageOverflow, + AlgoError1, + AlgoError2, + AlgoError3, + AlgoError4, + AlgoError5, + AlgoError6, } // Dispatchable functions allows users to interact with the pallet and invoke state changes. @@ -104,5 +120,66 @@ pub mod pallet { }, } } + + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::cause_error())] + pub fn run_algo(origin: OriginFor, a: i32, b: i32, wasm: Vec) -> DispatchResult { + let engine = wasmi::Engine::default(); + + let module = + wasmi::Module::new(&engine, wasm.as_slice()).map_err(|_| Error::::AlgoError1)?; + + type HostState = u32; + let mut store = wasmi::Store::new(&engine, 42); + let host_print = wasmi::Func::wrap( + &mut store, + |caller: wasmi::Caller<'_, HostState>, param: i32| { + log::debug!(target: "algo", "Message:{:?}", param); + }, + ); + let memory = wasmi::Memory::new( + &mut store, + wasmi::MemoryType::new(8, None).map_err(|_| Error::::AlgoError2)?, + ) + .map_err(|_| Error::::AlgoError2)?; + + memory.write(&mut store, 0, &a.to_ne_bytes()).map_err(|e| { + log::error!(target: "algo", "Algo1 {:?}", e); + Error::::AlgoError1 + })?; + memory.write(&mut store, 4, &b.to_ne_bytes()).map_err(|e| { + log::error!(target: "algo", "Algo1 {:?}", e); + Error::::AlgoError1 + })?; + // memory.write(&mut store, 0, 5); + + let mut linker = >::new(&engine); + linker.define("host", "print", host_print).map_err(|_| Error::::AlgoError2)?; + linker.define("env", "memory", memory).map_err(|_| Error::::AlgoError2)?; + + let instance = linker + .instantiate(&mut store, &module) + .map_err(|e| { + log::error!(target: "algo", "Algo3 {:?}", e); + Error::::AlgoError3 + })? + .start(&mut store) + .map_err(|_| Error::::AlgoError4)?; + + let hello = instance + .get_typed_func::<(), i64>(&store, "calc") + .map_err(|_| Error::::AlgoError5)?; + + // And finally we can call the wasm! + let a = hello.call(&mut store, ()).map_err(|e| { + log::error!(target: "algo", "Algo6 {:?}", e); + Error::::AlgoError6 + })?; + Self::deposit_event(Event::AlgoResult { + result: a, + }); + + Ok(()) + } } }