Extending Fluence functions (experimental)
This section is experimental as the API is not stable and probably will change.
Intro
As you already know, JS client allows you to process incoming event and dispatch events to execute Aqua. Additionally, JS client provides API for creating not just JavaScript services but essentially with any programming language which support WASI compilation, e.g. rust, go.
Use cases
Here are the reasons why you might want to write a Marine JS WASI service instead of writing a regular JavaScript service for JS client:
- A service which works both on Nox peer and JS client peer, thus you eliminate the need to copy-paste the codelogic.
- Heavy computations, CPU intensive tasks. Some languages cope with that better than high-level JavaScript.
- Would prefer to write a service in Rust or other language with compilation to WASI (at this moment only Rust is officially supported)
What is Marine JS
When JS client starts up, it starts with initialization of Marine JS runtime. The runtime hosts wasm services and even allows you to register your own wasm services. For example, AquaVM service (which process event data) resides in Marine JS. That's why Marine JS is a foundation of JS client.
Extending JS client with Marine services
Currently only pure single-module services are supported.
Using Marine services is pretty straight forward. The first thing to do is to load a compiled WASM file into your environment.
Loading module binary
Here are the examples of how to load your wasm file in browser or Node.js
Node.js
javascript
import { readFile } from 'fs/promises';const wasm = await readFile('path/to/wasm/file.wasm', 'base64');
javascript
import { readFile } from 'fs/promises';const wasm = await readFile('path/to/wasm/file.wasm', 'base64');
Browser
Need to install additional external package in browser env -
js-base64
javascript
import { fromUint8Array } from 'js-base64';const wasmBinary = await fetch('https://wasm.com/url/to/wasm').then(res => res.arrayBuffer());const wasm = fromUint8Array(new Uint8Array(greetingWasm));
javascript
import { fromUint8Array } from 'js-base64';const wasmBinary = await fetch('https://wasm.com/url/to/wasm').then(res => res.arrayBuffer());const wasm = fromUint8Array(new Uint8Array(greetingWasm));
Registering service in JS client
JS client doesn't provide any specific interface for creating services in Marine JS.
However, the client loads wasm content through its own special service called Srv
.
This is JS client's specific API that's why you need to add Srv
definition in your Aqua project.
Add the following aqua file in your project near the other aqua files.
data ServiceCreationResult:success: boolservice_id: ?stringerror: ?stringdata RemoveResult:success: boolerror: ?stringalias ListServiceResult: []stringservice Srv("single_module_srv"):-- Used to create a service on a certain node-- Arguments:-- bytes – a base64 string containing the .wasm module to add.-- Returns: service_id – the service ID of the created service.create(wasm_b64_content: string) -> ServiceCreationResult-- Used to remove a service from a certain node-- Arguments:-- service_id – ID of the service to removeremove(service_id: string) -> RemoveResult-- Returns a list of services ids running on a peerlist() -> ListServiceResult
data ServiceCreationResult:success: boolservice_id: ?stringerror: ?stringdata RemoveResult:success: boolerror: ?stringalias ListServiceResult: []stringservice Srv("single_module_srv"):-- Used to create a service on a certain node-- Arguments:-- bytes – a base64 string containing the .wasm module to add.-- Returns: service_id – the service ID of the created service.create(wasm_b64_content: string) -> ServiceCreationResult-- Used to remove a service from a certain node-- Arguments:-- service_id – ID of the service to removeremove(service_id: string) -> RemoveResult-- Returns a list of services ids running on a peerlist() -> ListServiceResult
Also, you need Aqua function which will call the service above. Here is an example of what that function could look like in a simple form.
In this example, wasm binary loaded as a string (see above) passed to Aqua function param.
service GreetingService("service-id"): -- (1)greeting: string -> stringfunc hello(name: string, wasm_content: string) -> string:created_service <- Srv.create(wasm_content) -- (2)Greeting created_service.service_id! -- (3)<- Greeting.greeting(name) -- (4)
service GreetingService("service-id"): -- (1)greeting: string -> stringfunc hello(name: string, wasm_content: string) -> string:created_service <- Srv.create(wasm_content) -- (2)Greeting created_service.service_id! -- (3)<- Greeting.greeting(name) -- (4)
- (1) Service definition for the passed module
- (2) Using JS client
Srv
service to register module as a service in Marine JS. - (3) Creating service variable. It will allow you to call methods from service definition
- (4) Interacting with your single module service
Then you need to pass wasm
variable above as a second parameter in the function.
You can load and register as many WASM modules as you want. Remember to keep each of them in a separate service.
There is a working marine service example in examples repo.