---
# System prepended metadata

title: IOTA Script (Wasm)
tags: [Crypto]

---

# IOTA Script (Wasm)

Iota scripting using WASM have the following requirements:

* Is it possible to write simple scripts in Go, Rust, Javascript... and build WASM files
* WASM binary need to be small

## Terminology

In this document I did some simplifications in terminology to avoid never ending explanations.

* **WASM**: either a binary format or a software with .wasm extension
* **WASM VM**: the execution model for a WASM file (CPU, Memory, ...)
* **VM instance** or **Instance**: a process that is running a **WASM VM** that itself is executing a WASM file
* **Runtime**: a software that is able to run a **WASM VM** 

## Overview

WASM is an intermediate binary format that need to be built and instantiated for a given WASM VM before execution in order to have a valid running software. 

The VM instance comes with a linear memory that is isolated with respect to external world. Here's an example of runtime setup with [WasmTime](https://wasmtime.dev/):

```
    engine := wasmtime.NewEngine()
    store := wasmtime.NewStore(engine)
    module, _ := wasmtime.NewModuleFromFile(engine, "test.wat    ")
    instance, _ := wasmtime.NewInstance(store, module, []wasmtime.AsExtern{})
```

here the same with [Wasmer.io](https://wasmer.io/):

```
    wasmBytes, _ := ioutil.ReadFile(fileName)
    store := wasmer.NewStore(wasmer.NewEngine())
    module, _ := wasmer.NewModule(store, wasmBytes)
    importObject := wasmer.NewImportObject()
    instance, _ = wasmer.NewInstance(module, importObject)
```

> :warning: **Performances**: Since a instance is stateful, different scripts need to create different instances in order to be evaluated, leading to performance issues!

WASM computing model (VM) is composed of a VM that read instructions and work with a stack and a linear memory.
The linear memory is a memory that is isolated with respect to the external world/runtime.

![](https://i.imgur.com/ONIr3hl.png)

Since Linear Memory is not shared with runtime *WASM execution* is isolated and secure. However, some interaction with external world are required.

## Wasi

A way to let WASM VM to interoperate with external Runtime is via [Wasi](https://wasi.dev/) standard. This standard is however neither complete, nor stable so in my research I found more than a reasons to avoid that (for example the support for Wasi is different in different languages leading to incompatibilities or that the Wasi extension are mainly written in rust or C, so creating another one could be difficult...). 

Common Wasi interfaces allows methods execution, file system access...

## Shared Memory and Externs

Second way to let Runtime integrate the WASM VM is via shared memory:

![](https://i.imgur.com/TTNVPct.png)

WASM runtime can inject *Extern* methods that can be called from within the WASM VM: this allow to WASM execution to ask for *external* methods.

On the other side runtime can access to methods exported from WASM binary and invoke them from outside.

External Runtime can, moreover copy data from/to the *Linear Memory*.

## Runtimes (Comparison)

I explored two main Golang runtimes: [WasmTime](https://wasmtime.dev/) and [Wasmer.io](https://wasmer.io/).
While both runtimes are, from my point of view, comparable and with a similar approach to build and instantiate WASM binaries (and are targeted for speed), here a list of pros/cons I found:

*[WasmTime](https://wasmtime.dev/)*
- [pros] It is already widely used in IOTA SC (Wasp)
- [pros] It has features like timeout and gas to limit the execution of software (and avoid endless loops) that is very important in this context.
- [pros] it is supported by the [Bytecode Alliance](https://bytecodealliance.org/) (nonprofit)

*[Wasmer.io](https://wasmer.io/)*
- [pros] Better compatibity with other programming languages (yes, not all languages produces the same Wasm binary)
- [pros] It declares to be faster that WasmTime...
- [cons] It is owned by a startup (commercial in future?)

## Compilers differences

*Theoretically* each language has some support to produce WASM binary.The problem is not in the ability to create a WASM out of the language you choose, but:
- **how** the WASM binary manage translation of *peculiarities* of each language (for example *concurrency*...)
- **how** the WASM binary define its datatype for interoperability

Here an example on how different builder define different requirements in term of methods to import:

| Compiler | Size | List of Imports |
| -------- | -------- | -------- |
| Pure Go | 1.2Mb | ![](https://i.imgur.com/1jWbCxV.png) |
| TinyGo | 5.3Kb | ![](https://i.imgur.com/1Sxbgy1.png) |
| AssemlyScript | 12Kb | ![](https://i.imgur.com/ahQyMTR.png) |

### Externs

As example we show a simple method that get a string as parameter and return an integer is translated in AssemblyScript and TinyGo

**AssemblyScript**

```export declare function GetInput(a: string): i32```

![](https://i.imgur.com/5jBejsd.png)

**TinyGO**

```func GetInput(string) int```

![](https://i.imgur.com/sh9yxal.png)

Both methods need to allocate memory for the string and then ask an Extern method that need to get this string from linear memory. While AssemblyScript passes to the external method the pointer to the first byte of the string and the length, TinyGo pass also a third parameter (the terminator byte) 

## Challenges

Following by previous analysis here a list of challenges that implementing a Script runtime for IOTA need to take in account:

* Complex Type standards (do they exist?)
* Memory management (copy to/from memory to execute methods)
* Compression of Binary (to save memory and speed up process)
* Having a lib in all the languages that need to be built to produce WASM binary so that Runtime can have same methods to use and interact with binary.
* Overhead of running WASM (compilation, instantiation...)
* Binary size (even simple example goes from Kilobytes to Megabytes)

## Wasp solution

All those challenges was already took in consideration during the development of [Wasp](https://github.com/iotaledger/wasp). 

This leaded to the creation of a general purpose library for Wasm interoperability: [WasmLib](https://github.com/iotaledger/wasp/tree/master/packages/wasmvm/wasmlib).

Basically there exist a WasmLib for Rust, Go and Javascript to be included in the corresponding code that is going to be built to produce WASM binary. 

This library produces a set of method that is omogeneous with respect of the language and provide support for complex data type (serialization/deserialization through memory) within the corresponding language. In this way runtime can find in WASM the same methods independently from the compiler and language used.

The library comes also with a schema creation tools that basically helps in incrementing the set of methods that WASM binary can invoke and that are represented in the Runtime.

**Cons**

* WasmLib is enough general purpose and has references to SC structures and globals: for those reasons currently seems to be an overkill for IOTA Scripting
* Even if optimized, the WasmLib has an overhead (tenths of Kilobyte) that could be reduced removing some unnecessary general purpose mechanisms
* It has not zero-copy approach 

## Roadmap

In order to realyze IOTA script in WASM the following tasks need to be addressed:

- Find the best runtime between WasmTime (best support, knowledge and VM control) or Wasmer (speed, compatibility)

- Find a standard and compact way to serialize/deserialize data in Wasm linear memory during extern method invocations (WasmLib is generic with respect to Map, Arrays and does not use compression or zero-copy) to take in acocunt performances. Solutions like [protobuf](https://developers.google.com/protocol-buffers) or [flatbuffers](https://google.github.io/flatbuffers/) or something more lightweight should be explored

- Create a lighter WasmLib that supports only the methods and globals that are required by IOTA Scripts (to gain in performances).

## PoC

To perform my analysis I created some small tests.
Some WASM binaries are created using Go, TinyGo and AssemblyScript.
The main Runtime method instantiates a WASM VM and invoke a "Validate" method exported by WASM binary.
The "Validate" method requires to invoke an external method called "GetInput" passing a string as parameter.
The "GetInput" methods need to be able to get the complex data (string) out of the memory and returns a value (42).

![](https://i.imgur.com/yNwQbuN.png)

https://github.com/iotaledger/poc-iota-scripts-wasm

In the PoC you can find:

- **build.sh**: script to build all the scripts in WASM (and add them in directory `wasm`)
- **explorer** (created using WasmTime): to show export and import of a WASM file
- **wasmtime** example: invoke a method in Wasm that require an external method (passing a string) to return a value
- **wasmer/simple** example: invoke a method in Wasm that require an external method (passing a string) to return a value
- **wasmer/wasi** example: demostrate how Wasmer is able to inject Wasi imports (to manage stdout) from builder phase (inherith output stream)

## Resources

* [WASM Explorer](https://wasdk.github.io/wasmcodeexplorer/): to check exported/imported functions in WASM
