-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FLIP 255: Cadence WebAssembly API #256
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
--- | ||
status: proposed | ||
flip: 255 | ||
authors: darkdrag00n (darkdrag00n@proton.me) | ||
sponsor: Bastian Müller (bastian.mueller@flowfoundation.org) | ||
updated: 2024-02-25 | ||
--- | ||
|
||
# FLIP 255: Cadence Webassembly API | ||
|
||
## Objective | ||
|
||
This FLIP proposes addition of a WebAssembly (WASM) execution engine API to Cadence. | ||
|
||
## Motivation | ||
|
||
More and more projects are putting non-trivial computationally expensive logic in Cadence. Examples of this are games or zero-knowledge proof checks. | ||
|
||
WASM could offer a great algorithmic throughput for such workloads by providing a way to offload the pure logic calculation to it without exposing Resource objects to it. | ||
|
||
The motivation is explained in further details in [this forum post](https://forum.flow.com/t/idea-wasm-execution-engine-in-cadence/5164) by Dete. | ||
|
||
## User Benefit | ||
|
||
Users can benefit from the raw algorithmic throughput of WASM by offloading computationally expensive pure logic calculations to it while still keeping the security benefits of Cadence. | ||
|
||
## Proposal | ||
|
||
### Scope | ||
|
||
As mentioned earlier, the feature is targeted towards utilizing algorithmic throughput provided by WASM. So the scope of this proposal is as follows: | ||
|
||
1. Allow instantiating a WebAssembly module, without imports (i.e. the WebAssembly module has no access to Cadence) | ||
2. Function exports with argument and return value types as i32 or i64. Floats (f32 or f64) will not be supported | ||
3. The function exports could be called from Cadence, and receive the results back | ||
|
||
### Design | ||
|
||
The WebAssembly API will be implemented as a Cadence contract and will expose functions and structs to use them. | ||
|
||
There are two main structs that will be exposed via the contract: | ||
1. InstantiatedSource: Represents a source that has been compiled and instantiated via the Webassembly module. | ||
2. Instance: Represents an instance of an instantiated webassebmly module that can be used to get the exports and use them in Cadence code. | ||
|
||
The `InstantiatedSource` can be created using the `compileAndInstantiate` function which takes the WebAssembly binary code and compiles it into a Module. | ||
|
||
```cadence | ||
access(all) | ||
contract WebAssembly { | ||
|
||
/// Compile WebAssembly binary code into a Module | ||
/// and instantiate it. Imports are not supported. | ||
access(all) | ||
view fun compileAndInstantiate(bytes: [UInt8]): &WebAssembly.InstantiatedSource | ||
|
||
access(all) | ||
struct InstantiatedSource { | ||
|
||
/// The instance. | ||
access(all) | ||
let instance: &WebAssembly.Instance | ||
} | ||
|
||
struct Instance { | ||
|
||
/// Get the exported value. | ||
access(all) | ||
view fun getExport<T: AnyStruct>(name: String): T | ||
} | ||
} | ||
``` | ||
|
||
#### Example | ||
|
||
Example usage of the WASM API to load a simple `add(i32, i32) -> i32` function, calling it with Cadence `Int32` values, and getting back the result as a Cadence `Int32`: | ||
|
||
```cadence | ||
|
||
access(all) | ||
func main() { | ||
// A simple program which exports a function `add` with type `(i32, i32) -> i32`, | ||
// which sums the arguments and returns the result: | ||
// | ||
// (module | ||
// (type (;0;) (func (param i32 i32) (result i32))) | ||
// (func (;0;) (type 0) (param i32 i32) (result i32) | ||
// local.get 0 | ||
// local.get 1 | ||
// i32.add) | ||
// (export "add" (func 0))) | ||
// | ||
let addProgram: [UInt8] = [ | ||
0, 97, 115, 109, 1, 0, 0, 0, 1, 7, 1, 96, | ||
2, 127, 127, 1, 127, 3, 2, 1, 0, 7, 7, 1, | ||
3, 97, 100, 100, 0, 0, 10, 9, 1, 7, 0, 32, | ||
0, 32, 1, 106, 11, | ||
] | ||
|
||
let instance = WebAssembly.compileAndInstantiate(bytes: program).instance | ||
let addFn = instance.getExport<fun(Int32, Int32): Int32>(name: "add") | ||
|
||
let sum1 = addFn(1, 2) // 3 | ||
let sum2 = addFn(1, -100) // -99 | ||
} | ||
``` | ||
|
||
#### Metering | ||
|
||
Like regular Cadence programs, code executed within the WASM engine will be metered and the memory consumption will be limited. | ||
|
||
##### Computation | ||
|
||
Popular WASM engines support some form of limits on the execution of the code. They will be utilized to implement the metering. | ||
|
||
While the specifics are dependant on the exact engine used, the high level idea is the following: | ||
1. FVM should provide a way to remaining computation units: A new function `RemainingComputation(kind)` will be added in FVM. | ||
2. Computation units should be converted to its WASM engine equivalent. For example, in wasmtime, it is called `Fuel`. | ||
3. The user code must be executed with the computed amount of wasm engine computation units. | ||
|
||
##### Memory | ||
To limit the memory being consumed, some constraints will be defined on the various ways that memory is used in the wasm-engine. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll need to define limits. Currently the PoC defines these: https://github.com/onflow/cadence/pull/2760/files#diff-e898ac8338568dfcac62c2017b09e956e5f99c1cda6eedb3f7c48b830595f9d1R76-R90 |
||
|
||
While the specifics are dependant on the exact engine used, the following memory usage areas can usually be limited by all popular engines: | ||
1. Stack size | ||
2. Linear memory | ||
3. Tables | ||
|
||
### Drawbacks | ||
|
||
None | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll need to define which WebAssembly features will be supported. Currently the PoC specifies: https://github.com/onflow/cadence/pull/2760/files#diff-e898ac8338568dfcac62c2017b09e956e5f99c1cda6eedb3f7c48b830595f9d1R43-R70 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll need to point out that deterministic execution is required. WebAssmbly allows non-determinism through NaNs. However, some WebAssembly runtiumes allow determinism by enforcing NaN-normalization. |
||
### Alternatives Considered | ||
None | ||
|
||
### Performance Implications | ||
|
||
None | ||
|
||
### Dependencies | ||
|
||
None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll likely won't implement a new WebAssembly runtime and use an existing one |
||
|
||
### Engineering Impact | ||
|
||
It would require 3-4 weeks of engineering effort to implement, review & test the feature. | ||
|
||
### Compatibility | ||
|
||
This change has no impact on compatibility between systems (e.g. SDKs). | ||
|
||
### User Impact | ||
|
||
The proposed feature is a purely additive. | ||
There is no impact on existing contracts and new transactions. | ||
|
||
## Related Issues | ||
|
||
None | ||
|
||
## Questions and Discussion Topics | ||
|
||
None | ||
|
||
## Implementation | ||
Will be done as part of https://github.com/onflow/cadence/issues/2853. | ||
|
||
A proof of concept has already been done as part of https://github.com/onflow/cadence/pull/2760. | ||
turbolent marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need to define how fuel translates to computation usage in Flow