Skip to content

Commit

Permalink
refactor: more guest-wasm examples/docs
Browse files Browse the repository at this point in the history
  • Loading branch information
zeeshanlakhani committed Mar 23, 2023
1 parent ef28cab commit daad1a8
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 0 deletions.
107 changes: 107 additions & 0 deletions homestar-guest-wasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<div align="center">
<a href="https://github.com/ipvm-wg/homestar" target="_blank">
<img src="https://raw.githubusercontent.com/ipvm-wg/homestar/main/assets/a_logo.png" alt="homestar Logo" width="100"></img>
</a>

<h1 align="center">homestar-guest-wasm</h1>

<p>
<a href="https://github.com/ipvm-wg/homestar/blob/main/LICENSE">
<img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License">
</a>
<a href="https://discord.gg/fissioncodes">
<img src="https://img.shields.io/static/v1?label=Discord&message=join%20us!&color=mediumslateblue" alt="Discord">
</a>
</p>
</div>

<div align="center"><sub>:warning: Work in progress :warning:</sub></div>

##

This is a template-like, example-driven crate (non-published) used for building
Wasm components in order to run and test them on the latest [wasmtime][wasmtime]
runtime, with the `component-model` feature turned on.

We use the components compiled from this crate as fixtures for our
execution-and-[IPLD][ipld]-focused [homestar-wasm crate](../homestar-wasm). We
currently rely on the [WIT format][wit-mvp] IDL to describe exports, for example:

```wit
default world homestar {
export add-one: func(a: s32) -> s32
export append-string: func(a: string) -> string
export transpose: func(matrix: list<list<u8>>) -> list<list<u8>>
}
```

We then implement these functions in [lib.rs](./src/lib.rs) using
[wit-bindgen][wit-bindgen], a guest language bindings generator for
[WIT][wit-mvp] and the [Component Model][component-model].

## Build

Once functions are implemented, we can build the component in release-mode,
targetting [`wasm32-unknown-unknown`][wasm32]:

```console
# from this directory:
cargo build -p homestar-guest-wasm --target wasm32-unknown-unknown --release

# or from the top-level workspace:
cargo build -p homestar-guest-wasm --target wasm32-unknown-unknown --release
```

The guest Wasm module will be generated at
`../target/wasm32-unknown-unknown/release/homestar_guest_wasm.wasm`.

Sadly, this module is **not yet** an actual `component`. But, we can leverage
the [wasm-tools][wasm-tools] tooling ([wit-component][wit-component] in
particular) to convert the core Wasm binary to a Wasm component and place
it in a different directory:

```console
wasm-tools component new /
../target/wasm32-unknown-unknown/release/homestar_guest_wasm.wasm -o ../homestar-wasm/fixtures/
```

*Of note*, [homestar-wasm's](../homestar-wasm) execution model will do
[this conversion at runtime][conversion-code]!

### Other Helpful Repos

* [keyvalue-component-model-demo][kv-demo]
* [SpiderLightning][spiderlightning] - defines a set of `*.wit` files that
abstract distributed application capabilities, such as key-value, messaging,
http-server/client and more.

### Coming soon

* [WASI][wasi] examples

## License

This project is licensed under the [Apache License 2.0](./LICENSE), or
[http://www.apache.org/licenses/LICENSE-2.0][apache].

### Contribution

Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.


[apache]: https://www.apache.org/licenses/LICENSE-2.0
[component-model]: https://github.com/WebAssembly/component-model
[conversion-code]: https://github.com/ipvm-wg/homestar/blob/main/homestar-wasm/src/wasmtime/world.rs#L277
[ipld]: https://ipld.io/
[kv-demo]: https://github.com/Mossaka/keyvalue-component-model-demo
[spiderlightning]: https://github.com/deislabs/spiderlightning
[wasi]: https://github.com/WebAssembly/WASI
[wasm32]: https://doc.rust-lang.org/rustc/platform-support/wasm64-unknown-unknown.html
[wasmtime]: https://github.com/bytecodealliance/wasmtime
[wasm-tools]: https://github.com/bytecodealliance/wasm-tools
[wit-bindgen]: https://github.com/bytecodealliance/wit-bindgen
[wit-component]: https://crates.io/crates/wit-component
[wit-mvp]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md
36 changes: 36 additions & 0 deletions homestar-guest-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ wit_bindgen::generate!("test" in "./wits");

struct Component;

type Matrix = Vec<Vec<u8>>;

impl Homestar for Component {
fn add_one(a: i32) -> i32 {
a + 1
Expand All @@ -11,6 +13,40 @@ impl Homestar for Component {
let b = "world";
[a, b.to_string()].join("\n")
}

fn transpose(matrix: Matrix) -> Matrix {
assert!(!matrix.is_empty());
let len = matrix[0].len();
(0..len)
.map(|i| matrix.iter().map(|row| row[i]).collect())
.collect()
}
}

export_homestar!(Component);

#[cfg(test)]
mod test {
use super::*;

#[test]
fn add_one() {
assert_eq!(Component::add_one(42), 43);
}

#[test]
fn append_string() {
assert_eq!(
Component::append_string("jimmy eat".to_string()),
"jimmy eat\nworld".to_string()
);
}

#[test]
fn transpose() {
let matrix: Matrix = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
let transposed = Component::transpose(matrix.clone());
assert_ne!(transposed, matrix);
assert_eq!(Component::transpose(transposed), matrix);
}
}
1 change: 1 addition & 0 deletions homestar-guest-wasm/wits/test.wit
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
default world homestar {
export add-one: func(a: s32) -> s32
export append-string: func(a: string) -> string
export transpose: func(matrix: list<list<u8>>) -> list<list<u8>>
}
Binary file modified homestar-wasm/fixtures/homestar_guest_wasm.wasm
Binary file not shown.
41 changes: 41 additions & 0 deletions homestar-wasm/tests/execute_wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,47 @@ async fn test_append_string() {
);
}

#[tokio::test]
async fn test_matrix_transpose() {
let ipld_inner = Ipld::List(vec![
Ipld::List(vec![Ipld::Integer(1), Ipld::Integer(2), Ipld::Integer(3)]),
Ipld::List(vec![Ipld::Integer(4), Ipld::Integer(5), Ipld::Integer(6)]),
Ipld::List(vec![Ipld::Integer(7), Ipld::Integer(8), Ipld::Integer(9)]),
]);
let ipld = Input::Ipld(Ipld::Map(BTreeMap::from([(
"args".into(),
Ipld::List(vec![ipld_inner.clone()]),
)])));

let wasm = fs::read(fixtures("homestar_guest_wasm.wasm")).unwrap();
let mut env = World::instantiate(wasm.clone(), "transpose".to_string(), State::default())
.await
.unwrap();

let transposed = env
.execute(ipld.parse().unwrap().try_into().unwrap())
.await
.unwrap();

let transposed_ipld = Ipld::try_from(transposed).unwrap();

assert_ne!(transposed_ipld, ipld_inner);

let ipld_transposed_map = Input::Ipld(Ipld::Map(BTreeMap::from([(
"args".into(),
Ipld::List(vec![transposed_ipld.clone()]),
)])));

let retransposed = env
.execute(ipld_transposed_map.parse().unwrap().try_into().unwrap())
.await
.unwrap();

let retransposed_ipld = Ipld::try_from(retransposed).unwrap();

assert_eq!(retransposed_ipld, ipld_inner);
}

#[tokio::test]
async fn test_execute_wasms_in_seq() {
let ipld_int = Input::Ipld(Ipld::Map(BTreeMap::from([(
Expand Down

0 comments on commit daad1a8

Please sign in to comment.