Skip to content

Commit

Permalink
Add wasmer web assembly engine as POC
Browse files Browse the repository at this point in the history
This adds the wasmer-d submodule and an engine which can run in d at
runtime. Currently the Engine is only run in the unit tests in
`WasmEngine.d` to experiment with running Web Assembly programs.
`dtest=WasmEngine dub test`
  • Loading branch information
hewison-chris committed Jun 28, 2021
1 parent 43a2aea commit 1bc36ac
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ jobs:
sudo apt-get install libsodium-dev
sudo apt-get install clang
- name: Install Wasmer
run: |
curl https://get.wasmer.io -sSfL | WASMER_DIR=${HOME}/.wasmer sh
echo 'WASMER_DIR=${HOME}/.wasmer' >> $GITHUB_ENV
- name: Build documentation
run: |
AGORA_VERSION=HEAD dub build -b ddox
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ jobs:
sudo apt-get update
sudo apt-get install libsodium-dev libsqlite3-dev clang
- name: Install Wasmer
run: |
curl https://get.wasmer.io -sSfL | WASMER_DIR=${HOME}/.wasmer sh
echo 'WASMER_DIR=${HOME}/.wasmer' >> $GITHUB_ENV
- name: Checkout faucet
uses: actions/checkout@v2
with:
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,17 @@ jobs:
brew install coreutils libsodium pkg-config
echo "LIBRARY_PATH=${LD_LIBRARY_PATH-}:/usr/local/lib/" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH=/usr/local/opt/sqlite/lib/pkgconfig:/usr/local/opt/[email protected]/lib/pkgconfig/" >> $GITHUB_ENV
env | grep HOME
curl https://get.wasmer.io -sSfL | WASMER_DIR=${HOME}/.wasmer sh
echo 'WASMER_DIR=${HOME}/.wasmer' >> $GITHUB_ENV
- name: '[Linux] Install dependencies & setup environment'
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install libsodium-dev libsqlite3-dev clang
curl https://get.wasmer.io -sSfL | WASMER_DIR=${HOME}/.wasmer sh
echo 'WASMER_DIR=${HOME}/.wasmer' >> $GITHUB_ENV
- name: '[Windows] Install dependencies & setup environment'
if: runner.os == 'Windows'
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@
[submodule "serialization"]
path = submodules/serialization
url = https://github.com/bosagora/serialization.git
[submodule "wasmer-d"]
path = submodules/wasmer-d
url = https://github.com/chances/wasmer-d.git
8 changes: 8 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ FROM bpfk/agora-builder:latest AS Builder
ARG DUB_OPTIONS
ARG AGORA_VERSION="HEAD"
RUN apk --no-cache add linux-headers python3 npm
WORKDIR /
RUN wget https://github.com/wasmerio/wasmer/releases/download/2.0.0/wasmer-linux-musl-amd64.tar.gz
RUN tar -zxvf wasmer-linux-musl-amd64.tar.gz
RUN wasmer -V
ADD . /root/agora/
WORKDIR /root/agora/talos/
RUN npm ci && npm run build
Expand All @@ -22,5 +26,9 @@ RUN apk --no-cache add --allow-untrusted -X /root/packages/build/ ldc-runtime=1.
RUN apk --no-cache add llvm-libunwind libgcc libsodium libstdc++ sqlite-libs
COPY --from=Builder /root/agora/talos/build/ /usr/share/agora/talos/
COPY --from=Builder /root/agora/build/agora /usr/local/bin/agora
WORKDIR /
RUN wget https://github.com/wasmerio/wasmer/releases/download/2.0.0/wasmer-linux-musl-amd64.tar.gz
RUN tar -zxvf wasmer-linux-musl-amd64.tar.gz
RUN wasmer -V
WORKDIR /agora/
ENTRYPOINT [ "/usr/local/bin/agora" ]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Additionally, the following are dependencies:
- `openssl`: Binary (to detect the version) and development library
- `sqlite3`: Development library
- `zlib`: Development library
- `wasmer`: Web Assembly Engine - [Install](https://github.com/wasmerio/wasmer-install#readme)

Additionally, on OSX, `PKG_CONFIG_PATH` needs to be properly set to pick up `sqlite3` and `openssl`.
Provided you installed those packages via `brew`, you can run the following command prior to building:
Expand Down
3 changes: 2 additions & 1 deletion dub.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
"localrest": { "version": "*", "dflags" : [ "-preview=in", "-revert=dtorfields" ] },
"ocean": { "version": "*", "dflags" : [ "-preview=in", "-revert=dtorfields" ] },
"serialization": { "version": "*", "dflags" : [ "-preview=in", "-revert=dtorfields" ] },
"vibe-d": { "version": "*", "dflags" : [ "-preview=in", "-revert=dtorfields" ] }
"vibe-d": { "version": "*", "dflags" : [ "-preview=in", "-revert=dtorfields" ] },
"wasmer": { "version": "*", "dflags" : [ "-preview=in", "-revert=dtorfields" ] }
}
}
3 changes: 2 additions & 1 deletion dub.selections.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"taggedalgebraic": {"path":"submodules/taggedalgebraic/"},
"tinyendian": {"path":"submodules/tinyendian/"},
"vibe-core": {"path":"submodules/vibe-core/"},
"vibe-d": {"path":"submodules/vibe.d/"}
"vibe-d": {"path":"submodules/vibe.d/"},
"wasmer": {"path":"submodules/wasmer-d/"}
}
}
155 changes: 155 additions & 0 deletions source/agora/node/WasmEngine.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*******************************************************************************
Contains the wasmer WebAssembly engine to be used for Trust Contracts.
The submodule `wasmer-d` provides a `D` wrapper of the `wamser` runtime.
(https://chances.github.io/wasmer-d/wasmer.html)
Copyright:
Copyright (c) 2021 BOS Platform Foundation Korea
All rights reserved.
License:
MIT License. See LICENSE for details.
*******************************************************************************/

module agora.node.WasmEngine;

import agora.utils.Log;

mixin AddLogger!();

/// Ditto
public class WasmEngine
{
import wasmer;

/// The JIT engine for executing WebAssembly Programs (WASM)
Engine engine;

/// The global state that can be manipulated by WebAssembly programs
/// https://webassembly.github.io/spec/core/exec/runtime.html#syntax-store
Store store;

public void start ()
{
log.info("Starting WasmEngine");
engine = new Engine();
store = new Store(engine);
log.info("Started WasmEngine");
}

public void stop ()
{
log.info("Stopping WasmEngine");
destroy(store);
destroy(engine);
log.info("Stopped WasmEngine");
}

public void exampleTime ()
{
log.info("Running {} using WasmEngine", __FUNCTION__);
const wat_callback_module_time = "(module
(type $FUNCSIG$ii (func (param i32) (result i32)))
(import \"env\" \"time\" (func $time (param i32) (result i32)))
(table 0 anyfunc)
(memory $0 1)
(export \"memory\" (memory $0))
(export \"main\" (func $main))
(func $main (; 1 ;) (result i32)
(call $time
(i32.const 0)
)
)
)";
log.trace("wat (text representation of wasm)");
log.trace("==============================");
log.trace("{}", wat_callback_module_time);
log.trace("==============================");
auto module_ = Module.from(store, wat_callback_module_time);
assert(module_.valid, "Error compiling module!");

auto print = (Module module_, int value) =>
{
return value;
}();
auto imports = [new Function(store, module_, print.toDelegate).asExtern];
auto instance = module_.instantiate(imports);
assert(instance.valid, "Could not instantiate module!");
auto runFunc = Function.from(instance.exports[1]);
assert(instance.exports[1].name == "main", "Failed to get the `main` function!");
assert(runFunc.valid, "`main` function invalid!");
Value[] results;
assert(runFunc.call(results), "Error calling the `main` function!");
assert(results.length == 1, "length of results should be 1!");
// assert(results[0].value.of.i32 > 0, "time should be greater than 0");
log.info("Used web assembly engine to get time. Result = {}", results[0].value.of.i32);
destroy(instance);
destroy(module_);
}


public void exampleAdd ()
{
const wat_callback_module_add = "(module" ~
" (func $print (import \"\" \"print\") (param i32) (result i32))" ~
" (func (export \"run\") (param $x i32) (param $y i32) (result i32)" ~
" (call $print (i32.add (local.get $x) (local.get $y)))" ~
" )" ~
")";
log.info("Running {} using WasmEngine", __FUNCTION__);
log.trace("wat (text representation of wasm)");
log.trace("==============================");
log.trace("{}", wat_callback_module_add);
log.trace("==============================");

auto module_ = Module.from(store, wat_callback_module_add);
assert(module_.valid, "Error compiling module!");

auto print = (Module module_, int value) =>
{
return value;
}();
auto imports = [new Function(store, module_, print.toDelegate).asExtern];
auto instance = module_.instantiate(imports);
assert(instance.valid, "Could not instantiate module!");
auto runFunc = Function.from(instance.exports[0]);
assert(instance.exports[0].name == "run" && runFunc.valid, "Failed to get the `run` function!");

auto three = new Value(3);
auto four = new Value(4);
Value[] results;
assert(runFunc.call([three, four], results), "Error calling the `run` function!");
assert(results.length == 1 && results[0].value.of.i32 == 7);
log.info("Used web assembly engine to add 3 and 4. Result = {}", results[0].value.of.i32);

destroy(three);
destroy(four);
destroy(instance);
destroy(module_);
}
}

unittest
{
import std.stdio;
import std.format;

Log.root.level(LogLevel.Trace, true);
auto output = stdout.lockingTextWriter();
try
{
auto wasmEngine = new WasmEngine();
wasmEngine.start();
wasmEngine.exampleAdd();
wasmEngine.exampleTime();
wasmEngine.stop();
}
catch (Exception e)
{
output.formattedWrite("%s", e);
}
// print logs of the work thread
CircularAppender!()().print(output);
}
1 change: 1 addition & 0 deletions submodules/wasmer-d
Submodule wasmer-d added at c94c72

0 comments on commit 1bc36ac

Please sign in to comment.