Introducing WASI for IC

Hi all,

My name is Stan. I wanted to announce that I have started working on the WASI support for the IC under the Developer Grant Program.

Background

WebAssembly System Interface (WASI) is emerging as the standard target for compiling WebAssembly (Wasm) programs. WASI provides a set of functions to access system resources such as the file system, network, and environment variables regardless of the underlying operating system. This allows Wasm programs to be run in a variety of environments, including the web, cloud, and embedded devices.

Many popular programming languages, including JavaScript (SpiderMonkey engine), Python (CPython), C++, Go (TinyGo), Haskell (GHC), Swift, and Ruby, support WASI as a compilation target. A popular database Sqlite can also be compiled to WASI.

Project Description

The goal of the project is to use the IC’s System API to implement the core WASI functions, effectively polyfilling WASI for the IC. This would allow developers to take advantage of the WASI ecosystem and run WASI-compatible programs on the Internet Computer.

The set of WASI functions consists of the following groups:

  1. Accessing the program arguments and environment variables:
    • args_get(), args_sizes_get(), environ_get(), environ_sizes_get().
  2. Getting the current time:
    • clock_res_get(), clock_time_get().
  3. Working with files and paths:
    • fd_advise(), fd_allocate(), fd_close(), fd_datasync(), fd_fdstat_get(), fd_fdstat_set_flags(), fd_fdstat_set_rights(), fd_filestat_get(), fd_filestat_set_size(), fd_filestat_set_times(), fd_pread(), fd_prestat_get(), fd_prestat_dir_name(), fd_pwrite(), fd_read(), fd_readdir(), fd_renumber(), fd_seek(), fd_sync(), fd_tell(), fd_write(), path_create_directory(), path_filestat_get(), path_filestat_set_times(), path_link(), path_open(), path_readlink(), path_remove_directory(), path_rename(), path_symlink(), path_unlink_file()
  4. Working with sockets:
    • sock_accept(), sock_recv(), sock_send(), sock_shutdown().
  5. Polling on time and file events:
    • poll_oneoff()
  6. Getting randomness:
    • random_get()
  7. Other:
    • proc_exit(), proc_raise(), sched_yield().

Polyfilling groups 1, 2, 5, 6, 7 should be straightforward with a caveat that currently randomness is not available at canister installation time, which will make random_get() pseudorandom in the initial version. True randomness would require protocol changes to provide randomness during canister installation time. The plan for group 3 is to implement the simplest possible file system on top of IC’s stable memory and stable-structures. This is where most of the work is expected to happen. Group 4 will not be supported because sockets are not required to run most of the WASI programs mentioned in the Background section.

Additionally, I am planning to write a command line tool to parse Wasm binaries and substitute the WASI imports with their implementations from the polyfill library.

The workflow for an IC developer utilizing WASI will look as follows:
1. Write a WASI program in any language that supports wasm32-wasi.
2. Incorporate a build step to my command line tool to replace WASI dependencies.
3. Run the Wasm binary on the IC.

I’ll post more updates in this thread once I have the initial prototype implementation.

40 Likes

That would be great! Will it be a problem for the implementation that some services can only be accessed asynchronously on the IC?

8 Likes

This is great. I’ve been so excited for wasm in general to take over.
This would be a great opportunity for Motoko to expand it’s wasi support.

4 Likes

In order to use the asynchronous features of the IC, the program would need to use the System API in addition to WASI. The primary benefit of the project is to allow the usage of WASI-based libraries in the IC canisters.

5 Likes

Interesting idea! I think group 1 is also tricky to implement. If the goal is to replace the whole WASI binary to run on the IC, how do you decide their Candid types and the endpoint API? If we plan to use the WASI binary as a library, we don’t have a good story for Wasm linking yet.

4 Likes

The concrete goal is to support the wasm32-wasi compilation target for canisters. This will allow a canister to use a library that requires WASI, which is currently impossible with the target wasm32-unknown-unknown. Note that the library here means not a Wasm file but rather an actual library defined in the source language of the canister. For example, a Rust canister could embed a rust port of the sqlite library rusqlite that supports the WASI target.

In theory it might be possible to support libraries defined in Wasm files, which, as you have mentioned, would require some Wasm linker, this is out of scope in this project.

For now I am aiming for the simplest possible implementation of the polyfill functions, which means the args_get() and environ_get() would return empty results.

3 Likes

For the fd_* stuff you might be interested in GitHub - codebase-labs/icfs: Internet Computer File System

2 Likes

Good work! I have a question here:

Run the wasm binary on the IC, do you mean a canister? if so, how do you generate the candid file?

Would this even work with motoko? Seems the referenced library could do something unexpected…maybe yield flow to something that never returns?

Thank you! I will take a look, does it support folders?

2 Likes

Yes. There’s an example showing how you can provide a POSIX-style API:

There’s also an issue for making a more idiomatic Rust API: icfs-fatfs: provide similar functionality to std::fs · Issue #23 · codebase-labs/icfs · GitHub

3 Likes

@sgaflv ,

This is great news!

We use the wasi-sdk to compile C++ to wasm, and we currently stub those methods you mentioned.

Even when the file system is not used at all, the wasi-sdk linker still might include calls to fd_* functions. For extra safety we also trap the execution with a clear message.

Looking forward to be able to use the full wasi-sdk capabilities!

5 Likes

wasm-snip can sometimes help simplify things when imports show up but those code paths aren’t used.

4 Likes

The goal is to allow building canisters that contain wasm32-wasi target dependencies, but otherwise you are developing the canister by the usual canister development flow.

3 Likes

As far as I know Motoko does not compile to wasm32-wasi (it also doesn’t have FFI). My initial focus is low-level languages like Rust and C++.

There is definitely a --wasi-system-api flag for outputting to wasi. I’d say that @claudio or @matthewhammer would have more info.

If there are a few small things that could be added to get motoko into the mix it would be really helpful.

Great! Many thanks! I might have more questions when I start working on the fd_ functions.

I also don’t know if this has any crossover with what @v1ctor has done on Assigned: ICDevs.org - Bounty #34 - Wasmer Motoko - $10,000, but if it does it would be nice to build on the work that has already been done over there. I think this is likely the other side of the coin from this work, but I want to make sure you guy know about each other and can compare notes.

1 Like

Thank you for the information, I will take a look at that project and see if there is something in common that can be reused. At the moment I don’t have a clear picture how it would work in Motoko, but supporting wasm32-wasi is a good sign!

One important detail I’m still missing from the workflow described in the OP is this: at the end of the day, any program useful on the IC will need to interact with the IC’s system API in some way, i.e., at least be able to receive and respond to ingress messages. That is rather different from how programs interact with more traditional environments. What is the plan for bridging that gap?

1 Like