docs: README re-organized (#477)

* docs: README re-organized

* add repository structure

* fix docs

* docs: A few minor fixes following an earlier discussion

---------

Co-authored-by: vms <michail.vms@gmail.com>
This commit is contained in:
mikhail-1e20 2023-02-22 19:54:47 +03:00 committed by GitHub
parent c9eb5cd47a
commit 9d739906ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 239 additions and 191 deletions

13
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,13 @@
## Contribute Code
You are welcome to contribute to Fluence!
Things you need to know:
1. You need to **agree to the [Contributor License Agreement](https://gist.github.com/fluencelabs-org/3f4cbb3cc14c1c0fb9ad99d8f7316ed7) (CLA)**. This is a common practice in all major Open Source projects. At the current moment, we are unable to accept contributions made on behalf of a company. Only individual contributions will be accepted.
2. **Not all proposed contributions can be accepted**. Some features may, e.g., just fit a third-party add-on better. The contribution must fit the overall direction of Fluence and really improve it. The more effort you invest, the better you should clarify in advance whether the contribution fits: the best way would be to just open an issue to discuss the contribution you plan to make.
### Contributor License Agreement
When you contribute, you have to be aware that your contribution is covered by **[Apache License 2.0](./LICENSE)**, but might relicensed under few other software licenses mentioned in the **Contributor License Agreement**. In particular, you need to agree to the Contributor License Agreement. If you agree to its content, you simply have to click on the link posted by the CLA assistant as a comment to the pull request. Click it to check the CLA, then accept it on the following screen if you agree to it. The CLA assistant will save this decision for upcoming contributions and will notify you if there is any change to the CLA in the meantime.

217
README.md
View File

@ -1,214 +1,65 @@
# AquaVM
[![crates.io version](https://img.shields.io/crates/v/air-interpreter-wasm?style=flat-square)](https://crates.io/crates/air-interpreter-wasm)
[![npm version](https://img.shields.io/npm/v/@fluencelabs/avm)](https://www.npmjs.com/package/@fluencelabs/avm)
# AquaVM
AquaVM executes compiled [Aqua](https://github.com/fluencelabs/aqua), i.e., Aqua Intermediate Representation (AIR) scripts, and plays an integral part in the implementation of the Fluence peer-to-peer compute protocol. Specifically, AquaVM allows expressing network choreography in scripts and composing distributed, peer-to-peer hosted services. Moreover, AquaVM plays a significant role in facilitating *function addressability* in the Fluence network. Figure 1.
**Figure 1: Stylized AquaVM And AIR Model**
AquaVM executes compiled [Aqua](https://github.com/fluencelabs/aqua), i.e., Aqua Intermediate Representation (AIR) scripts, and plays an integral role in the implementation of the [Fluence](https://fluence.network) peer-to-peer compute protocol. Specifically, AquaVM allows expressing network choreography in scripts and composing distributed, peer-to-peer hosted services. Moreover, AquaVM plays a significant role in facilitating *function addressability* in the Fluence network, see Figure&nbsp;1.
<img alt="AquaVM & AIR model" src="images/aquavm_air_model.png" />
Since AquaVM compiles to Wasm, it can run in both client, such as browsers and nodejs apps, and server environments.
**Figure 1: Stylized AquaVM And AIR Model**
Since AquaVM compiles to Wasm, it can run in both client, such as browsers and Node.js apps, and server environments.
## AquaVM: Interpreter Execution Model
AquaVM's execution model facilitates Fluence protocol's data push model implemented as a *particle*, i.e., a smart packet comprised of data, AIR, and some metadata. In this context, AquaVM can be viewed as a pure state transition function that facilitates particle updates, which includes state management of particle data by taking previous and current state to produce a new state and an updated list of peers and call requests in the remaining AIR workflow. In addition to local service call execution, AquaVM handles requests from remote peers, e.g. as part of a parallel execution block, to call local services and handle the future response. See Figure 2.
**Figure 2: AquaVM Interpreter Execution Model**
AquaVM's execution model facilitates Fluence protocol's data push model implemented as a *particle*, i.e., a smart packet comprised of data, AIR, and some metadata. In this context, AquaVM can be viewed as a pure state transition function that facilitates particle updates, which includes state management of particle data by taking previous and current state to produce a new state and an updated list of peers and call requests in the remaining AIR workflow. In addition to local service call execution, AquaVM handles requests from remote peers, e.g., as a part of a parallel execution block, to call local services and handle the future response, see Figure&nbsp;2.
<img alt="interpreter execution model" src="images/interpreter_execution_model.png"/>
**Figure 2: AquaVM Interpreter Execution Model**
In summary, the AquaVM execution model handles the topological hops for simple and advanced composition patters, such as (async) parallel service execution on one or multiple peers.
## Aquamarine Intermediate Representation (AIR): IR For P2P Systems
AIR scripts control the Fluence peer-to-peer network, its peers and, through Marine adapter services, even resources on other (p2p) networks, such as IPFS and Filecoin, e.g., [Fluence IPFS library](https://fluence.dev/docs/aqua-book/libraries/aqua-ipfs).
## Aquamarine Intermediate Representation (AIR): IR for P2P Systems
### What is AIR?
AIR is an [S-expression](https://www.s-expressions.org/home)-based low-level language with binary form to come. It currently consists of fourteen instructions with more instructions to come. Semantics of AIR is inspired by [π-calculus](https://en.wikipedia.org/wiki/%CE%A0-calculus), [λ-calculus](https://en.wikipedia.org/wiki/Lambda_calculus), and [category theory](https://en.wikipedia.org/wiki/Category_theory). Its syntax is inspired by [Wasm Text Format](https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format) (WAT) and [Lisp](https://en.wikipedia.org/wiki/Lisp_(programming_language)).
AIR scripts control the Fluence peer-to-peer network, its peers and even resources on other (p2p) networks, such as IPFS and Filecoin, e.g., [Fluence IPFS library](https://fluence.dev/docs/aqua-book/libraries/aqua-ipfs) through Marine adapter services.
- S-expression-based low-level language with binary form to come
- Consists of fourteen (14) instructions with more instructions to come
- Semantics are inspired by [π-calculus](https://en.wikipedia.org/wiki/%CE%A0-calculus), [λ-calculus](https://en.wikipedia.org/wiki/Lambda_calculus) and [category theory](https://en.wikipedia.org/wiki/Category_theory)
- Syntax is inspired by [Wasm Text Format](https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format) (WAT) and [Lisp](https://en.wikipedia.org/wiki/Lisp_(programming_language))
A description of AIR values and a list of AIR instructions together with examples of their usage can be found [here](./docs/AIR.md). The main properties of AIR and its interface are discussed [here](./air/README.md).
### AIR: Instructions
A complete list of AIR instructions with examples of their usage can be found [here](./docs/AIR.md). The fundamental contracts of the AquaVM interface, along with a more detailed interaction scheme can be found [here](./air/README.md).
#### call
```wasm
(call <peer_id> (<service name> <service function>) [<arguments list>] <output name>)
```
## Repository Structure
- moves execution to the `peer_id` specified
- the peer is expected to host Wasm service with the specified `service name`
- the `service function` is expected to contain the specified function
- the `arguments list` is given to the function and may be empty
- the result of the function execution is saved and returned by it's `output name`
- [**air**](./air) is the core of AquaVM
- [**air-interpreter**](./air-interpreter) is a crate to support different compilation targets (Marine and wasm-bindgen)
- [**avm**](./avm)
- [client](./avm/client) is an AquaVM launcher for browser and Node.js targets
- [server](./avm/server) is an AquaVM launcher for server-side targets
- [**crates** ](./crates)
- [air-lib](./crates/air-lib) contains all main crates for the core of AquaVM
- [beautifier](./crates/beautifier) is an implementation of AIR-beautifier
- [data-store](./crates/data-store) defines a `DataStore` trait used by the Fluence node
- [interpreter-wasm](./crates/interpreter-wasm) is a crate for better integration of compiled Wasm code into the Fluence node
- [testing-framework](./crates/testing-framework) is an implementation of the framework that improves test writing experience
- [**tools**](./tools) contains AquaVM-related tools
Example:
```wasm
(call "peer_id" ("dht" "put") [key value] result)
```
#### seq
## Support
```wasm
(seq <left_instruction> <right_instruction>)
```
Please, file an [issue](https://github.com/fluencelabs/aquavm/issues) if you find a bug. You can also contact us at [Discord](https://discord.com/invite/5qSnPZKh7u) or [Telegram](https://t.me/fluence_project). We will do our best to resolve the issue ASAP.
- executes instructions sequentially: `right_instruction` will be executed iff `left_instruction` finished successfully
#### par
## Contributing
```wasm
(par <left_instruction> <right_instruction>)
```
Any interested person is welcome to contribute to the project. Please, make sure you read and follow some basic [rules](./CONTRIBUTING.md).
- executes instructions in parallel: `right_instruction` will be executed independently of the completion of `left_instruction`
#### ap
## License
```wasm
(ap <literal> <dst_variable>)
(ap <src_variable>.$.<lambda> <dst_variable>)
```
All software code is copyright (c) Fluence Labs, Inc. under the [Apache-2.0](./LICENSE) license.
- puts `literal` into `dst_variable`
- or applies `lambda` to `src_variable` and saves the result in `dst_variable`
Example:
```wasm
(seq
(call "peer_id" ("user-list" "get_users") [] users)
(ap users.$.[0].peer_id user_0)
)
```
#### canon
```wasm
(canon "peer_id" <$stream> <#canon_stream>)
```
- executes on peer_id, takes $stream as it is on the moment of first canonicalization
- every next execution #canon_stream will be the same — as first seen by peer_id
Example:
```wasm
(seq
(ap user $users)
(canon "peer_id" $stream #canon_stream)
)
```
#### match/mismath
```wasm
(match <variable> <variable> <instruction>)
(mismatch <variable> <variable> <instruction>)
```
- executes the instruction iff variables are equal/notequal
Example:
```wasm
(seq
(call "peer_id" ("user-list" "get_users") [] users)
(mismatch users.$.length 0
(ap users.$.[0].peer_id user_0)
)
)
```
#### fold/next
```wasm
(fold <iterable> <iterator> <instruction>)
```
- is a form of a fixed-point combinator
- iterates through the `iterable`, assigning each element to the `iterator`
- on each iteration `instruction` is executed
- `next` triggers next iteration
Example:
```wasm
(fold users user
(seq
(call user.$.peer_id ("chat" "display") [msg])
(next user)
)
)
```
#### xor
```wasm
(xor <left_instruction> <right_instruction>)
```
- `right_instruction` is executed iff `left_instruction` failed
#### new
```wasm
(new <variable>)
```
- creates a new scoped variable with the provided name (it's similar to \mu operator from pi-calculus that creates an anonymous channel)
#### fail
```wasm
(fail <variable>)
(fail <error code> <error message>)
```
- throws an exception with provided `error code` and `error message` or construct it from a provided `variable`]
Example
```wasm
(fail 1337 "error message")
```
#### never
```wasm
(never)
```
- marks a subgraph as incomplete, useful for code generation
#### null
```wasm
(null)
```
- does nothing, useful for code generation
### AIR: values
#### Scalars
- scalars are fully consistent - have the same value on each peer during a script execution
- could be an argument of any instruction
- JSON-based (fold could iterate only over array-based value)
#### Streams
- streams are CRDT-like (locally-consistent) - have deterministic execution wrt one peer
- versioned
- could be used only by call and fold instructions (more instructions for streams to come)
- could be turned to scalar (canonicalized)
#### Canonicalized streams
- contains an array of elements that was in a stream at the moment of canonicalization
- canonicalized streams are imutable and fully consistent as scalars
- has the same algebra as a stream for `match`/`mismatch` and `call` argument
- has the same algebra as a scalar for `new`
- has mixed behaviour for with other instructions

View File

@ -1,12 +1,13 @@
# AIR
### Overview
## Overview
This crates defines the core of the AIR interpreter intended to execute scripts to control execution flow in the Fluence network. From the high level point of view the interpreter could be considered as a state transition function that takes two states, merge them and then produce a new state.
This crate defines the core of the AIR interpreter intended to execute scripts to control execution flow in the [Fluence](https://fluence.network) network. From a high level point of view, the interpreter can be considered as a state transition function that takes two states, merges them, and then produces a new state.
### Interpreter interface
## Interpreter interface
This interpreter has only one export function called `invoke` and no import functions. The export function has the following signature:
This interpreter has only one export function called `invoke` and no import functions. The export function has the following signature:
```rust
pub fn executed_air(
/// AIR script to execute.
@ -44,9 +45,9 @@ pub struct InterpreterOutcome {
}
```
As it was already mentioned in the previous section, `invoke` takes two states (`prev_data` and `current_data`) and returns a new state (`new_data`). Additionally, it takes AIR script that should be executed, some run parameters (such as `init_peer_id` and `current_peer_id`), and `call_results`, results of services calling. As a result it provides the `IntepreterOutcome` structure described in the upper code snippet.
As it was already mentioned in the previous section, `invoke` takes two states (`prev_data` and `current_data`) and returns a new state (`new_data`). Additionally, it takes AIR script that should be executed, some run parameters (such as `init_peer_id` and `current_peer_id`), and `call_results`, results of services calling. As a result it provides the `IntepreterOutcome` structure described in the code snippet above.
### Main properties
## Main properties
Let's consider the interpreter with respect to data first, because previous, current and resulted data are the most interesting parts of arguments and the outcome. Assuming `X` is a set of all possible values that data could have, we'll denote `executed_air` export function as `f: X * X -> X`. It could be seen that with respect to data `f` forms a magma.
@ -59,7 +60,7 @@ Even more, `f` is an idempotent non-commutative monoid, because:
1. `forall x from X: f(x, x) = x`
2. `forall a, b from X: f(a, b) = c, f(c, b) = c, f(c, a) = c`
### Interaction with the interpreter
## Interaction with the interpreter
The interpreter allows a peer (either a node or a browser) to call service asynchronously by collecting all arguments and other necessary stuff from each `call` instruction that could be called during the execution and return them in `InterpreterOutcome`. A host should then execute them at any time and call back the interpreter providing executed service results as the `call_results` argument.
@ -78,4 +79,4 @@ Then this flow should be repeated starting from point 2.
4. If `call_requests` was empty, the whole execution is completed, `new_data` must be preserved and particle send for all `new_peer_pks` as usual.
An example of interaction could be found in [tests](https://github.com/fluencelabs/aquavm/blob/async/crates/test-utils/src/test_runner.rs).
An example of interaction can be found in [tests](https://github.com/fluencelabs/aquavm/blob/async/crates/test-utils/src/test_runner.rs).

183
docs/AIR.md Normal file
View File

@ -0,0 +1,183 @@
## AIR: Instructions
### call
```wasm
(call <peer_id> (<service name> <service function>) [<arguments list>] <output name>)
```
- moves execution to the `peer_id` specified
- the peer is expected to host Wasm service with the specified `service name`
- the `service function` is expected to contain the specified function
- the `arguments list` is given to the function and may be empty
- the result of the function execution is saved and returned by it's `output name`
Example:
```wasm
(call "peer_id" ("dht" "put") [key value] result)
```
### seq
```wasm
(seq <left_instruction> <right_instruction>)
```
- executes instructions sequentially: `right_instruction` will be executed iff `left_instruction` finished successfully
### par
```wasm
(par <left_instruction> <right_instruction>)
```
- executes instructions in parallel: `right_instruction` will be executed independently of the completion of `left_instruction`
### ap
```wasm
(ap <literal> <dst_variable>)
(ap <src_variable>.$.<lambda> <dst_variable>)
```
- puts `literal` into `dst_variable`
- or applies `lambda` to `src_variable` and saves the result in `dst_variable`
Example:
```wasm
(seq
(call "peer_id" ("user-list" "get_users") [] users)
(ap users.$.[0].peer_id user_0)
)
```
### canon
```wasm
(canon "peer_id" <$stream> <#canon_stream>)
```
- executes on peer_id, takes $stream as it is on the moment of first canonicalization
- every next execution #canon_stream will be the same — as first seen by peer_id
Example:
```wasm
(seq
(ap user $users)
(canon "peer_id" $stream #canon_stream)
)
```
### match/mismatch
```wasm
(match <variable> <variable> <instruction>)
(mismatch <variable> <variable> <instruction>)
```
- executes the instruction iff variables are equal/notequal
Example:
```wasm
(seq
(call "peer_id" ("user-list" "get_users") [] users)
(mismatch users.$.length 0
(ap users.$.[0].peer_id user_0)
)
)
```
### fold/next
```wasm
(fold <iterable> <iterator> <instruction>)
```
- is a form of a fixed-point combinator
- iterates through the `iterable`, assigning each element to the `iterator`
- on each iteration `instruction` is executed
- `next` triggers next iteration
Example:
```wasm
(fold users user
(seq
(call user.$.peer_id ("chat" "display") [msg])
(next user)
)
)
```
### xor
```wasm
(xor <left_instruction> <right_instruction>)
```
- `right_instruction` is executed iff `left_instruction` failed
### new
```wasm
(new <variable>)
```
- creates a new scoped variable with the provided name (it's similar to \mu operator from pi-calculus that creates an anonymous channel)
### fail
```wasm
(fail <variable>)
(fail <error code> <error message>)
```
- throws an exception with provided `error code` and `error message` or construct it from a provided `variable`]
Example
```wasm
(fail 1337 "error message")
```
### never
```wasm
(never)
```
- marks a subgraph as incomplete, useful for code generation
### null
```wasm
(null)
```
- does nothing, useful for code generation
## AIR: values
### Scalars
- scalars are fully consistent - have the same value on each peer during a script execution
- could be an argument of any instruction
- JSON-based (fold could iterate only over array-based value)
### Streams
- streams are CRDT-like (locally-consistent) - have deterministic execution wrt one peer
- versioned
- can be used only by call and fold instructions (more instructions for streams to come)
- can be turned to scalar (canonicalized)
### Canonicalized streams
- contains an array of elements that was in a stream at the moment of canonicalization
- canonicalized streams are imutable and fully consistent as scalars
- has the same algebra as a stream for `match`/`mismatch` and `call` argument
- has the same algebra as a scalar for `new`
- has mixed behaviour for with other instructions