Merge pull request #18 from fluencelabs/2.0.0

2.0.0
This commit is contained in:
boneyard93501 2021-07-04 20:33:33 -05:00 committed by GitHub
commit 0912df3a9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1592 additions and 135 deletions

View File

@ -4,58 +4,20 @@
* [Thinking In Aquamarine](p2p.md)
* [Concepts](concepts.md)
* [Quick Start](quick-start.md)
* [Quick Start](quick_start/README.md)
* [Setup](quick_start/quick_start_setup.md)
* [Using a Service](quick_start/quick_start_using_a_service.md)
* [Building An Application From Multiple Services](quick_start/quick_start_building_from_multiple_services.md)
* [Adding A Storage Service](quick_start/quick_start_add_persistence/README.md)
* [Setting Up](quick_start/quick_start_add_persistence/quick_start_persistence_setup.md)
* [CRUD All the Way](quick_start/quick_start_add_persistence/quick_start_persistence_crud.md)
* [What's Next](quick_start/quick_start_summary.md)
* [Developing Modules And Services](development_development/README.md)
* [Overview](development_development/development_overview.md)
* [From Module To Service](development_development/developmet_build_modules.md)
* [Building The Reward Block Application](development_development/development_reward_block_app/README.md)
* [Ethereum Request Service](development_development/development_reward_block_app/development_eth_calls.md)
* [SQLite Service](development_development/development_reward_block_app/development_sqlite.md)
* [Blocks To Database](development_development/development_reward_block_app/development_persisting_blocks.md)
* [Additional Concepts](development_development/development_reward_block_app/development_additional_concepts.md)
* [Summary](development_development/summary.md)
* [Knowledgebase](knowledge_knowledge/README.md)
* [Overview](knowledge_knowledge/knowledge_overview.md)
* [Concepts](knowledge_knowledge/knowledge_concepts.md)
* [Tools](knowledge_knowledge/knowledge_tools.md)
* [Aquamarine](knowledge_knowledge/knowledge_aquamarine/README.md)
* [Aqua](knowledge_knowledge/knowledge_aquamarine/hll/README.md)
* [Aqua VM](knowledge_knowledge/knowledge_aquamarine/hll/vm.md)
* [AIR](knowledge_knowledge/knowledge_aquamarine/hll/knowledge_aquamarine_air.md)
* [Marine](knowledge_knowledge/knowledge_aquamarine/marine/README.md)
* [Marine CLI](knowledge_knowledge/knowledge_aquamarine/marine/marine-cli.md)
* [Marine Repl](knowledge_knowledge/knowledge_aquamarine/marine/marine-repl.md)
* [Marine Rust SDK](knowledge_knowledge/knowledge_aquamarine/marine/marine-rs-sdk.md)
* [Node](knowledge_knowledge/node/README.md)
* [Overview](knowledge_knowledge/node/overview.md)
* [Services](knowledge_knowledge/node/knowledge_node_services.md)
* [TrustGraph](knowledge_knowledge/trustgraph.md)
* [Security](knowledge_knowledge/knowledge_security.md)
* [Aquamarine](knowledge_aquamarine/README.md)
* [Aqua](knowledge_aquamarine/hll.md)
* [Marine](knowledge_aquamarine/marine/README.md)
* [Marine CLI](knowledge_aquamarine/marine/marine-cli.md)
* [Marine Repl](knowledge_aquamarine/marine/marine-repl.md)
* [Marine Rust SDK](knowledge_aquamarine/marine/marine-rs-sdk.md)
* [Tools](knowledge_tools.md)
* [Node](node.md)
* [Security](knowledge_security.md)
* [Tutorials](tutorials_tutorials/README.md)
* [Setting Up Your Environment](tutorials_tutorials/recipes_setting_up.md)
* [Deploy A Local Fluence Node](tutorials_tutorials/tutorial_run_local_node.md)
* [Deploy A Private Fluence Network](tutorials_tutorials/running-a-fluence-network.md)
* [cUrl As A Service](tutorials_tutorials/curl-as-a-service.md)
* [Add Your Own Builtins](tutorials_tutorials/add-your-own-builtin.md)
* [TrustGraph In Action](tutorials_tutorials/tutorial_trustgraph.md)
* [Securing Services](tutorials_tutorials/securing-services.md)
* [Developing a Frontend Application with JS-SDK](tutorials_tutorials/developing-a-frontend-application-with-js-sdk.md)
* [Building a Chat Appplication](tutorials_tutorials/building-a-chat-appplication.md)
* [Building a Collaborative Editor](tutorials_tutorials/building-a-collaborative-editor.md)
* [Recipes](recipes_recipes/README.md)
* [Setting Up Your Environment](recipes_recipes/recipes_setting_up.md)
* [cUrl as a Service](recipes_recipes/recipes_curl.md)
* [Error Management and Testing Of Services](recipes_recipes/error-management-and-testing-of-services.md)
* [Sqlite](recipes_recipes/sqlite.md)
* [Redis](recipes_recipes/redis.md)
* [Local Filesystem](recipes_recipes/local-filesystem.md)
* [IPFS](recipes_recipes/ipfs.md)
* [Data Replication](recipes_recipes/data-replication.md)
* [Building a Frontend with JS-SDK](tutorials_tutorials/building-a-frontend-with-js-sdk.md)
* [Research, Papers And References](research-papers-and-references.md)
* [FAQ](faq_faq.md)

View File

@ -124,7 +124,11 @@ The Fluence protocol offers an alternative to node selection, i.e. connection an
[TrustGraph](https://github.com/fluencelabs/trust-graph) is currently under active development. Please check the repo for progress.
{% endhint %}
## **Scaling Applications**
# **Application**
An application is the "frontend" to one or more services and their execution sequence. Applications are developed by coordinating one or more services into a logical compute unit and tend to live outside the Fluence network**,** e.g., the browser as a peer-client. They can be executed in various runtime environments ranging from browsers to backend daemons.
### **Scaling Applications**
As discussed previously, decoupling at the network and business logic levels is at the core of the Fluence protocol and provides the major entry points for scaling solutions.

View File

@ -1,20 +0,0 @@
# FAQ
Coming soon. If you really need this section, contact us through any of the social media channels or Github.
## Setting Up The Development Environment
## Deploying A Node
## Accessing A Network
## Developing Modules & Services
## Securing Services
## AIR
## HLL
## TrustGraph

View File

@ -0,0 +1,8 @@
# Aquamarine
Fluence's Aquamarine stack is comprised of Aqua and Marine. Aqua is a programming language and runtime environment for peer-to-peer workflows. Marine, on the other hand, is a general purpose runtime that executes hosted code on nodes, whereas Aqua facilitates the programming of workflows composed from hosted code. In combination, Aqua and Marine enable any distributed application.
At the core of Aqua is the design ideal and idea to pair concurrent systems, and especially decentralized networks, with a programming and execution tool chain to avoid centralized bottlenecks commonly introduced with workflow engines and business rule engines. To this end, Aqua manages the communication and coordination between services, devices, and APIs without introducing a centralized gateway and can be used to express various distributed systems: from simple request-response models to comprehensive network consensus algorithms.

View File

@ -0,0 +1,6 @@
# Aqua
At the core of Fluence is the open-source language **Aqua** that allows for the programming of peer-to-peer scenarios separately from the computations on peers.
Please see the[ Aqua book ](https://doc.fluence.dev/aqua-book/)for an introduction to the language and reference materials.

View File

@ -0,0 +1,6 @@
# Aqua
## Aquamarine High Level Language
_**Stay Tuned -- Coming Soon To A Repo Near You**_

View File

@ -0,0 +1,55 @@
# AIR
The Aquamarine Intermediate Representation \(AIR\) is a low level language to program both distributed networks and the services deployed on them. The language is comprised of a small number of instructions:
* _**call**_: : execution
* _**seq**_ : sequential
* _**par** :_ parallel
* _**fold**_ : iteration
* _**xor** :_ branching & error handling
* _**null**_ : empty instruction
which operate on _peer-id_ \(location\), _service-id_, and _service method_ over an argument list, see Figure 1.
**Figure 1: AIR Instruction Definition** ![Execution](../../.gitbook/assets/air_call_execution_1.png)
## Instructions
AIR instructions are intended to launch the execution of a service method as follows:
1. The method is executed on the peer specified by the peer id \(location\) parameter
2. The peer is expected to have the Wasm service specified by the service id parameter
3. The service must have a callable method specified be the method parameter
4. The arguments specified by the argument list are passed to the method
5. The result of the method returned under the name output name
**Figure 2: Sequential Instruction** ![Execution](../../.gitbook/assets/air_sequential_2%20%281%29%20%281%29%20%281%29%20%281%29%20%281%29%20%282%29%20%283%29%20%284%29%20%284%29%20%284%29%20%281%29.png)
The _**seq**_ instruction takes two instructions at most as its arguments and executes them sequentially, one after the other.
**Figure 3: Parallel Instruction** ![Execution](../../.gitbook/assets/air_par_3.png)
The _**par**_ instruction takes two instructions at most as its arguments and particles may execute on parallel paths iff each service referenced is hosted on a different node otherwise particles execute sequentially
TODO: add better graphic showing the disticntion of branching vs seq.
**Figure 4: Fold Instruction** ![Execution](https://github.com/fluencelabs/gitbook-docs/tree/84e814d02d9299034c9c031adf7f081bb59898b9/.gitbook/assets/air_fold_4%20%281%29%20%282%29%20%281%29.png)
The _**fold**_ instruction iterates over the elements of an array and workds as follows:
* _**fold**_ instruction takes three arguments: an array, a variable and an instruction
* At each iteration, the variable is assigned an element of the array and the argument-instruction is executed
* The argument-instruction can access the variable and uses the next statement to trigger the next iteration
Figure 5: Branching Instruction ![Execution](../../.gitbook/assets/air_xor_5.png)
This instruction is intended for organizing branches in the flow of execution as well as for handling errors:
* The _**XOR**_ instruction takes two instructions as its arguments
* The first instruction is executed and if the execution is successful, then the second instruction is ignored
* If the first instruction fails, then the second one is executed.
**Figure 6: Null Instruction** ![Execution](https://github.com/fluencelabs/gitbook-docs/tree/84e814d02d9299034c9c031adf7f081bb59898b9/.gitbook/assets/air_null_6%20%281%29%20%282%29.png)
This is an empty instruction: it takes no arguments and does nothing. The _**null**_ instruction is useful for generating code.

View File

@ -0,0 +1,2 @@
# Aqua VM

View File

@ -0,0 +1,14 @@
# Marine
[Marine](https://github.com/fluencelabs/marine) is a general purpose WebAssembly runtime favoring Wasm modules based on the [ECS](https://en.wikipedia.org/wiki/Entity_component_system) pattern or plugin architecture and uses Wasm [Interface Types](https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.mdhttps://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md) \( IT\) to implement a [shared-nothing](https://en.wikipedia.org/wiki/Shared-nothing_architecture) linking scheme. Fluence [nodes](https://github.com/fluencelabs/fluence) use Marine to host the Aqua VM and execute hosted Wasm services.
The [Marine Rust SDK](https://github.com/fluencelabs/marine-rs-sdk) allows to hide the IT implementation details behind a handy procedural macro `[marine]` and provides the scaffolding for unit tests.

View File

@ -0,0 +1,35 @@
# Marine CLI
The [Marine command line tool](https://github.com/fluencelabs/marine) provides the project `marine build` functionality, analogous to `cargo build`, that results in the Rust code to be compiled to _wasm32-wasi_ modules. In addition, `marine` provides utilities to inspect Wasm modules, expose Wasm module attributes or manually set module properties.
```rust
mbp16~(:|✔) % marine --help
Fluence Marine command line tool 0.6.7
Fluence Labs
USAGE:
marine [SUBCOMMAND]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
SUBCOMMANDS:
aqua Shows data types of provided module in a format suitable for Aqua
build Builds provided Rust project to Wasm
help Prints this message or the help of the given subcommand(s)
info Shows manifest and sdk version of the provided Wasm file
it Shows IT of the provided Wasm file
repl Starts Fluence application service REPL
set Sets interface types and version to the provided Wasm file
mbp16~(:|✔) %
```

View File

@ -0,0 +1,35 @@
# Marine Repl
[`mrepl`](https://crates.io/crates/mrepl) is a command line tool to locally run a Marine instance to inspect, run, and test Wasm modules and service configurations. We can run the Repl either with `mrepl` or `marine repl`
```text
mbp16~(:|✔) % mrepl
Welcome to the Marine REPL (version 0.7.2)
Minimal supported versions
sdk: 0.6.0
interface-types: 0.20.0
New version is available! 0.7.2 -> 0.7.4
To update run: cargo +nightly install mrepl --force
app service was created with service id = d81a4de5-55c3-4cb7-935c-3d5c6851320d
elapsed time 486.234µs
1> help
Commands:
n/new [config_path] create a new service (current will be removed)
l/load <module_name> <module_path> load a new Wasm module
u/unload <module_name> unload a Wasm module
c/call <module_name> <func_name> [args] call function with given name from given module
i/interface print public interface of all loaded modules
e/envs <module_name> print environment variables of a module
f/fs <module_name> print filesystem state of a module
h/help print this message
q/quit/Ctrl-C exit
2>
```

View File

@ -0,0 +1,531 @@
# Marine Rust SDK
The [marine-rs-sdk](https://github.com/fluencelabs/marine-rs-sdk) empowers developers to write services suitable for peer hosting in peer-to-peer networks using the Marine Virtual Machine by enabling the wasm32-wasi compile target for Marine. For an introduction to writing services with the marine-rs-sdk, see the [Developing Modules And Services]() section.
### API
The procedural macros `[marine]` and `[marine_test]` are the two primary features provided by the SDK. The `[marine]` macro can be applied to a function, external block or structure. The `[marine_test]` macro, on the other hand, allows the use of the familiar `cargo test` to execute tests over the actual Wasm module generated from the service code.
#### Function Export
Applying the `[marine]` macro to a function results in its export, which means that it can be called from other modules or AIR scripts. For the function to be compatible with this macro, its arguments must be of the `ftype`, which is defined as follows:
`ftype` = `bool`, `u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`, `f32`, `f64`, `String`
`ftype` = `ftype` \| `Vec`&lt;`ftype`&gt;
`ftype` = `ftype` \| `Record`&lt;`ftype`&gt;
In other words, the arguments must be one of the types listed below:
* one of the following Rust basic types: `bool`, `u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`, `f32`, `f64`, `String`
* a vector of elements of the above types
* a vector composed of vectors of the above type, where recursion is acceptable, e.g. the type `Vec<Vec<Vec<u8>>>` is permissible
* a record, where all fields are of the basic Rust types
* a record, where all fields are of any above types or other records
The return type of a function must follow the same rules, but currently only one return type is possible.
See the example below of an exposed function with a complex type signature and return value:
```rust
// export TestRecord as a public data structure bound by
// the IT type constraints
#[marine]
pub struct TestRecord {
pub field_0: i32,
pub field_1: Vec<Vec<u8>>,
}
// export foo as a public function bound by the
// IT type contraints
#[marine] #
pub fn foo(arg_1: Vec<Vec<Vec<Vec<TestRecord>>>>, arg_2: String) -> Vec<Vec<Vec<Vec<TestRecord>>>> {
unimplemented!()
}
```
{% hint style="info" %}
Function Export Requirements
* wrap a target function with the `[marine]` macro
* function arguments must by of `ftype`
* the function return type also must be of `ftype`
{% endhint %}
#### Function Import
The `[marine]` macro can also wrap an [`extern` block](https://doc.rust-lang.org/std/keyword.extern.html). In this case, all functions declared in it are considered imported functions. If there are imported functions in some module, say, module A, then:
* There should be another module, module B, that exports the same functions. The name of module B is indicated in the `link` macro \(see examples below\).
* Module B should be loaded to `Marine` by the moment the loading of module A starts. Module A cannot be loaded if at least one imported function is absent in `Marine`.
See the examples below for wrapped `extern` block usage:
{% tabs %}
{% tab title="Example 1" %}
```rust
#[marine]
pub struct TestRecord {
pub field_0: i32,
pub field_1: Vec<Vec<u8>>,
}
// wrap the extern block with the marine macro to expose the function
// as an import to the Marine VM
#[marine]
#[link(wasm_import_module = "some_module")]
extern "C" {
pub fn foo(arg: Vec<Vec<Vec<Vec<TestRecord>>>>, arg_2: String) -> Vec<Vec<Vec<Vec<TestRecord>>>>;
}
```
{% endtab %}
{% tab title="Example 2" %}
```rust
[marine]
#[link(wasm_import_module = "some_module")]
extern "C" {
pub fn foo(arg: Vec<Vec<Vec<Vec<u8>>>>) -> Vec<Vec<Vec<Vec<u8>>>>;
}
```
{% endtab %}
{% endtabs %}
{% hint style="info" %}
#### Function import requirements
* wrap an extern block with the function\(s\) to be imported with the `[marine]` macro
* all function\(s\) arguments must be of the `ftype` type
* the return type of the function\(s\) must be `ftype`
{% endhint %}
####
#### Structures
Finally, the `[marine]` macro can wrap a `struct` making possible to use it as a function argument or return type. Note that
* only macro-wrapped structures can be used as function arguments and return types
* all fields of the wrapped structure must be public and of the `ftype`.
* it is possible to have inner records in the macro-wrapped structure and to import wrapped structs from other crates
See the example below for wrapping `struct`:
{% tabs %}
{% tab title="Example 1" %}
```rust
#[marine]
pub struct TestRecord0 {
pub field_0: i32,
}
#[marine]
pub struct TestRecord1 {
pub field_0: i32,
pub field_1: String,
pub field_2: Vec<u8>,
pub test_record_0: TestRecord0,
}
#[marine]
pub struct TestRecord2 {
pub test_record_0: TestRecord0,
pub test_record_1: TestRecord1,
}
#[marine]
fn foo(mut test_record: TestRecord2) -> TestRecord2 { unimplemented!(); }
```
{% endtab %}
{% tab title="Example 2" %}
```rust
#[fce]
pub struct TestRecord0 {
pub field_0: i32,
}
#[fce]
pub struct TestRecord1 {
pub field_0: i32,
pub field_1: String,
pub field_2: Vec<u8>,
pub test_record_0: TestRecord0,
}
#[fce]
pub struct TestRecord2 {
pub test_record_0: TestRecord0,
pub test_record_1: TestRecord1,
}
#[fce]
#[link(wasm_import_module = "some_module")]
extern "C" {
fn foo(mut test_record: TestRecord2) -> TestRecord2;
}
```
{% endtab %}
{% tab title="Example 3" %}
```rust
mod data_crate {
use fluence::marine;
#[marine]
pub struct Data {
pub name: String,
pub data: f64,
}
}
use data_crate::Data;
use fluence::marine;
fn main() {}
#[marine]
fn some_function() -> Data {
Data {
name: "example".into(),
data: 1.0,
}
}
```
{% endtab %}
{% endtabs %}
{% hint style="info" %}
> #### Structure passing requirements
>
> * wrap a structure with the `[marine]` macro
> * all structure fields must be of the `ftype`
> * the structure must be pointed to without preceding package import in a function signature, i.e`StructureName` but not `package_name::module_name::StructureName`
> * wrapped structs can be imported from crates
{% endhint %}
####
#### Call Parameters
There is a special API function `fluence::get_call_parameters()` that returns an instance of the [`CallParameters`](https://github.com/fluencelabs/marine-rs-sdk/blob/master/fluence/src/call_parameters.rs#L35) structure defined as follows:
```rust
pub struct CallParameters {
/// Peer id of the AIR script initiator.
pub init_peer_id: String,
/// Id of the current service.
pub service_id: String,
/// Id of the service creator.
pub service_creator_peer_id: String,
/// Id of the host which run this service.
pub host_id: String,
/// Id of the particle which execution resulted a call this service.
pub particle_id: String,
/// Security tetraplets which described origin of the arguments.
pub tetraplets: Vec<Vec<SecurityTetraplet>>,
}
```
CallParameters are especially useful in constructing authentication services:
```text
// auth.rs
use fluence::{marine, CallParameters};
use::marine;
pub fn is_owner() -> bool {
let meta = marine::get_call_parameters();
let caller = meta.init_peer_id;
let owner = meta.service_creator_peer_id;
caller == owner
}
#[marine]
pub fn am_i_owner() -> bool {
is_owner()
}
```
####
#### MountedBinaryResult
Due to the inherent limitations of Wasm modules, such as a lack of sockets, it may be necessary for a module to interact with its host to bridge such gaps, e.g. use a https transport provider like _curl_. In order for a Wasm module to use a host's _curl_ capabilities, we need to provide access to the binary, which at the code level is achieved through the Rust `extern` block:
```rust
// Importing a linked binary, curl, to a Wasm module
#![allow(improper_ctypes)]
use fluence::marine;
use fluence::module_manifest;
use fluence::MountedBinaryResult;
module_manifest!();
pub fn main() {}
#[marine]
pub fn curl_request(curl_cmd: Vec<String>) -> MountedBinaryResult {
let response = curl(curl_cmd);
response
}
#[marine]
#[link(wasm_import_module = "host")]
extern "C" {
fn curl(cmd: Vec<String>) -> MountedBinaryResult;
}
```
The above code creates a "curl adapter", i.e., a Wasm module that allows other Wasm modules to use the the `curl_request` function, which calls the imported _curl_ binary in this case, to make http calls. Please note that we are wrapping the `extern` block with the `[marine]`macro and introduce a Marine-native data structure [`MountedBinaryResult`](https://github.com/fluencelabs/marine/blob/master/examples/url-downloader/curl_adapter/src/main.rs) as the linked-function return value.
Please not that if you want to use `curl_request` with testing, see below, the curl call needs to be marked unsafe, e.g.:
```rust
let response = unsafe { curl(curl_cmd) };
```
since cargo does not access to the marine macro to handle unsafe.
MountedBinaryResult itself is a Marine-compatible struct containing a binary's return process code, error string and stdout and stderr as byte arrays:
```rust
#[marine]
#[derive(Clone, PartialEq, Default, Eq, Debug, Serialize, Deserialize)]
pub struct MountedBinaryResult {
/// Return process exit code or host execution error code, where SUCCESS_CODE means success.
pub ret_code: i32,
/// Contains the string representation of an error, if ret_code != SUCCESS_CODE.
pub error: String,
/// The data that the process wrote to stdout.
pub stdout: Vec<u8>,
/// The data that the process wrote to stderr.
pub stderr: Vec<u8>,
}
```
MountedBinaryResult then can be used on a variety of match or conditional tests.
#### Testing
Since we are compiling to a wasm32-wasi target with `ftype` constrains, the basic `cargo test` is not all that useful or even usable for our purposes. To alleviate that limitation, Fluence has introduced the [`[marine-test]` macro ](https://github.com/fluencelabs/marine-rs-sdk/tree/master/crates/marine-test-macro)that does a lot of the heavy lifting to allow developers to use `cargo test` as intended. That is, `[marine-test]` macro generates the necessary code to call Marine, one instance per test function, based on the Wasm module and associated configuration file so that the actual test function is run against the Wasm module not the native code.
Let's have a look at an implementation example:
```rust
use fluence::marine;
use fluence::module_manifest;
module_manifest!();
pub fn main() {}
#[marine]
pub fn greeting(name: String) -> String { # 1
format!("Hi, {}", name)
}
#[cfg(test)]
mod tests {
use fluence_test::marine_test; # 2
#[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts")] # 3
fn empty_string() {
let actual = greeting.greeting(String::new()); # 4
assert_eq!(actual, "Hi, ");
}
#[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts")]
fn non_empty_string() {
let actual = greeting.greeting("name".to_string());
assert_eq!(actual, "Hi, name");
}
}
```
1. We wrap a basic _greeting_ function with the `[marine`\] macro which results in the greeting.wasm module
2. We wrap our tests as usual with `[cfg(test)]` and import the fluence_test crate._ Do **not** import _super_ or the _local crate_.
3. Instead, we apply the `[marine_test]` to each of the test functions by providing the path to the config file, e.g., Config.toml, and the directory containing the Wasm module we obtained after compiling our project with `marine build`. It is imperative that project compilation proceeds the test runner otherwise there won't be the required Wasm file.
4. The target of our tests is the `pub fn greeting` function. Since we are calling the function from the Wasm module we must prefix the function name with the module namespace -- `greeting` in this example case.
Now that we have our Wasm module and tests in place, we can proceed with `cargo test --release.` Note that using the `release`vastly improves the import speed of the necessary Wasm modules.
### Features
The SDK has two useful features: `logger` and `debug`.
#### Logger
Using logging is a simple way to assist in debugging without deploying the module\(s\) to a peer-to-peer network node. The `logger` feature allows you to use a special logger that is based at the top of the [log](https://crates.io/crates/log) crate.
To enable logging please specify the `logger` feature of the Fluence SDK in `Config.toml` and add the [log](https://docs.rs/log/0.4.11/log/) crate:
```rust
[dependencies]
log = "0.4.14"
fluence = { version = "0.6.9", features = ["logger"] }
```
The logger should be initialized before its usage. This can be done in the `main` function as shown in the example below.
```rust
use fluence::marine;
use fluence::WasmLogger;
pub fn main() {
WasmLogger::new()
// with_log_level can be skipped,
// logger will be initialized with Info level in this case.
.with_log_level(log::Level::Info)
.build()
.unwrap();
}
#[marine]
pub fn put(name: String, file_content: Vec<u8>) -> String {
log::info!("put called with file name {}", file_name);
unimplemented!()
}
```
In addition to the standard log creation features, the Fluence logger allows the so-called target map to be configured during the initialization step. This allows you to filter out logs by `logging_mask`, which can be set for each module in the service configuration. Let's consider an example:
```rust
const TARGET_MAP: [(&str, i64); 4] = [
("instruction", 1 << 1),
("data_cache", 1 << 2),
("next_peer_pks", 1 << 3),
("subtree_complete", 1 << 4),
];
pub fn main() {
use std::collections::HashMap;
use std::iter::FromIterator;
let target_map = HashMap::from_iter(TARGET_MAP.iter().cloned());
fluence::WasmLogger::new()
.with_target_map(target_map)
.build()
.unwrap();
}
#[marine]
pub fn foo() {
log::info!(target: "instruction", "this will print if (logging_mask & 1) != 0");
log::info!(target: "data_cache", "this will print if (logging_mask & 2) != 0");
}
```
Here, an array called `TARGET_MAP` is defined and provided to a logger in the `main` function of a module. Each entry of this array contains a string \(a target\) and a number that represents the bit position in the 64-bit mask `logging_mask`. When you write a log message request `log::info!`, its target must coincide with one of the strings \(the targets\) defined in the `TARGET_MAP` array. The log will be printed if `logging_mask` for the module has the corresponding target bit set.
{% hint style="info" %}
REPL also uses the log crate to print logs from Wasm modules. Log messages will be printed if`RUST_LOG` environment variable is specified.
{% endhint %}
#### Debug
The application of the second feature is limited to obtaining some of the internal details of the IT execution. Normally, this feature should not be used by a backend developer. Here you can see example of such details for the greeting service compiled with the `debug` feature:
```bash
# running the greeting service compiled with debug feature
~ $ RUST_LOG="info" fce-repl Config.toml
Welcome to the Fluence FaaS REPL
app service's created with service id = e5cfa463-ff50-4996-98d8-4eced5ac5bb9
elapsed time 40.694769ms
1> call greeting greeting "user"
[greeting] sdk.allocate: 4
[greeting] sdk.set_result_ptr: 1114240
[greeting] sdk.set_result_size: 8
[greeting] sdk.get_result_ptr, returns 1114240
[greeting] sdk.get_result_size, returns 8
[greeting] sdk.get_result_ptr, returns 1114240
[greeting] sdk.get_result_size, returns 8
[greeting] sdk.deallocate: 0x110080 8
result: String("Hi, user")
elapsed time: 222.675µs
```
The most important information these logs relates to the `allocate`/`deallocate` function calls. The `sdk.allocate: 4` line corresponds to passing the 4-byte `user` string to the Wasm module, with the memory allocated inside the module and the string is copied there. Whereas `sdk.deallocate: 0x110080 8` refers to passing the 8-byte resulting string `Hi, user` to the host side. Since all arguments and results are passed by value, `deallocate` is called to delete unnecessary memory inside the Wasm module.
#### Module Manifest
The `module_manifest!` macro embeds the Interface Type \(IT\), SDK and Rust project version as well as additional project and build information into Wasm module. For the macro to be usable, it needs to be imported and initialized in the _main.rs_ file:
```text
// main.rs
use fluence::marine;
use fluence::module_manifest; // import manifest macro
module_manifest!(); // initialize macro
fn main() {}
#[marine]
fn some_function() {}
}
```
Using the Marine CLI, we can inspect a module's manifest with `marine info`:
```rust
mbp16~/localdev/struct-exp(main|…) % marine info -i artifacts/*.wasm
it version: 0.20.1
sdk version: 0.6.0
authors: The Fluence Team
version: 0.1.0
description: foo-wasm, a Marine wasi module
repository:
build time: 2021-06-11 21:08:59.855352 +00:00 UTC
```

View File

@ -1,2 +0,0 @@
# Knowledgebase

View File

@ -1,44 +0,0 @@
# Concepts
Distributed, peer-to-peer networks serve a wide variety of constituents and causes but generally with the common goal of providing decentralization benefits: no single point of failure or control. Alas, the programming of and developing for peer-to-peer networks is hard. Distributed service deployment and composition are quite different from the current REST, or GraphQL, microservices model, where discovery tends to be an inherently centralized process. In distributed, peer-to-peer networks, endpoint-based service requests are discovery-based searches across peers comprising the network. The Fluence solution makes peer-to-peer development accessible and productive and empowers developers to quickly and efficiently develop, deploy, manage, and monetize distributed applications and backends.
At the core of the Fluence solution are Aquamarine, a new, open-source language and runtime to choreograph distributed services into robust applications, and the Fluence Compute Engine \(FCE\), an open-source Wasm runtime. These Fluence innovations combine with [libp2p](https://libp2p.io/), [Kademlia DHT](https://en.wikipedia.org/wiki/Kademlia) at the peer-to-peer network level with the portability advantages of Wasm modules to provide developers with a modern, ergonomic p2p network and development model.
For the purpose of mastering peer-to-peer application development and the Fluence solution, developers need to be aware of the following concepts in addition to the Fluence tools and runtimes.
## Module
Modules are logical units of code that can be used individually or in combination to create a service. In Fluence, modules are written in Rust and compiled to [Wasm IT](https://wasi.dev/) for eventual execution by the Fluence Compute Engine \(FCE\). Modules fall into three conceptual categories, which developers need to be aware of as the module type has a direct impact on service configuration:
* Facade Modules
* Pure Modules
* Effector modules
Facade modules expose the API of the services comprised of one or more modules. Every service has exactly one facade module.
Pure modules perform computations without side-effects.
Effector modules contain at least one computation with a side-effect, e.g., write to file or database, access eternal binaries, etc.
## Service
Services are logical compute units derived from linking of one or more Wasm modules. The linking of modules is facilitated by the Fluence Compute Engine \(FCE\) using a specially prepared configuration file. The configuration file which governs the
* Order of instantiation
* Permission to resources
* Maximum memory allocation
FCE uses a [shared nothing](https://en.wikipedia.org/wiki/Shared-nothing_architecture) linking scheme meaning that modules only expose functions explicitly marked to be publicly available while not sharing memory or any other resources. It should be further noted that services can **not** call on other services directly.
## Blueprint
The configuration map associating modules with service instantiations. This is a higher level construct than the low-level FCE linking described in the services section. Blueprints capture module names, blueprint name, and blueprint id. That allows the tracking and management of modules and services on a per-peer basis.
## Particle
A particle is a data structure combining data, service execution sequence, by means of an AIR script, and additional metadata. According to the AIR script specification, a particle travels through the network triggering execution at pre-defined service stops and peer nodes updating its data at every hop. Not surprisingly, the notion, implementation and processing of particles are a salient aspect of the Fluence solution.
## Application
An application in the Fluence solution is the "frontend" to one or more services and their execution sequence. Applications are developed by coordinating one or more services into a logical compute unit and tend to live outside the Fluence network. They can be executed in various runtime environments ranging from browsers to backend daemons with the help of Fluence command-line tools or the Fluence JS-SDK.

View File

@ -1,4 +0,0 @@
# Overview
Developing peer-to-peer networks and applications are pretty much the only way to decentralize control and curtail censorship. In addition, public peer-to-peer networks can be designed and augmented to offer decentralized fail-over protection and network-level data replication. while proving resistant to several attacks, including Sybil and Eclipse attacks.

View File

@ -1,2 +0,0 @@
# Node

View File

@ -1,2 +0,0 @@
# Overview

View File

@ -1,4 +0,0 @@
# TrustGraph
Coming soon. If you really need this section, contact us through any of the social media channels or Github.

403
knowledge_node_services.md Normal file
View File

@ -0,0 +1,403 @@
# Builtin Services
## Overview
Each Fluence peer is equipped with a set of "built-in" services that can be called from Aquamarine and fall into the following namespaces:
1. _peer_ - operations related to connectivity or state of a given peer
2. _kad_ - Kademlia API
3. _srv_ management and information about services on a node
4. _dist_ distribution and inspection of modules and blueprints
5. _script_ to manage recurring scripts
6. _op_ basic operations on data deprecated - namespace for deprecated API Below is the reference documentation for all the existing built-in services. Please refer to the JS SDK documentation to learn how to easily use them from the JS SDK
7. _deprecated_ - namespace for deprecated API
Please note that the [`fldist`](knowledge_tools.md#fluence-proto-distributor-fldist) CLI tool, as well as the [JS SDK](knowledge_tools.md#fluence-js-sdk), provide access to node-based services.
## API
### peer is\_connected
Checks if there is a direct connection to the peer identified by a given PeerId
* **Arguments**:
* PeerId id of the peer to check if there's a connection with
* **Returns**: bool - true if connected to the peer, false otherwise
Example of a service call:
```scheme
(call node ("peer" "is_connected") ["123D..."] ok)
```
Initiates a connection to the specified peer
* **Arguments**
* _PeerId_ id of the target peer
* [_Multiaddr_](https://crates.io/crates/multiaddr) an array of target peer's addresses
* **Returns**: bool - true if connection was successful
Example of a service call:
```scheme
(seq
(call node ("op" "identity") ["/ip4/1.2.3.4/tcp/7777" "/ip4/1.2.3.4/tcp/9999"] addrs)
(call node ("peer" "connect") ["123D..." addrs] ok)
)
```
### peer get\_contact
Resolves the contact of a peer via [Kademlia](https://en.wikipedia.org/wiki/Kademlia)
* **Arguments**
* _PeerId_ id of the target peer
* **Returns**: Contact - true if connection was successful
```rust
// get_contact return struct
Contact {
peer_id: PeerId,
addresses: [Multiaddr]
}
```
Example of a service call:
```scheme
(call node ("peer" "get_contact") ["123D..."] contact)
```
#### peer identify
Get information about the peer
* **Arguments**: None
* **Returns:** _external address_
```javascript
{ "external_addresses": [ "/ip4/1.2.3.4/tcp/7777", "/dns4/stage.fluence.dev/tcp/19002" ] }
```
Example of service call:
```scheme
(call node ("peer" "identify") [] info) peer timestamp_ms
```
### peer timestamp\_ms
Get Unix timestamp in milliseconds
* **Arguments**: None
* **Returns**: _u128_ - number of milliseconds since 1970
Example of service call:
```scheme
(call node ("peer" "timestamp_ms") [] ts_ms)
```
### peer timestamp\_sec
Get Unix timestamp in seconds
* **Arguments**: None
* **Returns**: _u64_ - number of seconds since 1970
Example of service call:
```scheme
(call node ("peer" "timestamp_sec") [] ts_sec)
```
### kad neighborhood
Instructs node to return the locally-known nodes in the Kademlia neighborhood for a given key
* **Arguments**: _key_ the peer ID \(PeerId\) of the node
* **Returns**: _peers_ an array of PeerIds of the nodes that are in the Kademlia neighborhood for the given hash\(key\)
Example of service call:
```scheme
(call node ("dht" "neighborhood") [key] peers)
```
Please note that this service does _not_ traverse the network and may yield incomplete neighborhood.
### srv create
Used to create a service on a certain node.
* **Arguments**:
* blueprint\_id ID of the blueprint that has been added to the node specified in the service call by the dist add\_blueprint service.
* **Returns**: service\_id the service ID of the created service.
Example of service call:
```scheme
(call node ("srv" "create") [blueprint_id] service_id)
```
### srv list
Used to enumerate services deployed to a peer.
* **Arguments**: None
* **Returns**: a list of services running on a peer
Example of service call:
```scheme
(call node ("srv" "list") [] services)
```
### srv add\_alias
Adds an alias on service, so service could be called not only by service\_id but by alias.
* **Argument**: alias - settable service name service\_id ID of the service whose interface you want to name.
* **Returns**: alias id
Example of service call:
```scheme
(call node ("srv" "add_alias") [alias service_id])
```
### srv get\_interface
Retrieves the functional interface of a service running on the node specified in the service call.
* Argument: service\_id ID of the service whose interface you want to retrieve.
* Returns : an interface object of the following structure:
```typescript
{
interface: { function_signatures, record_types },
blueprint_id: "uuid-1234...",
service_id: "uuid-1234..."
}
```
Example of service call:
```scheme
(call node ("srv" "get_interface") [service_id] interface)
```
### dist add\_module
Used to add modules to the node specified in the service call.
* Arguments:
* bytes a base64 string containing the .wasm module to add.
* config an object of the following structure
```javascript
{
"name": "my_module_name"
}
```
Example of service call:
```scheme
(call node ("dist" "add_module") [bytes config] hash)
```
### dist list\_modules
Get a list of modules available on the node
* Arguments: None
* Returns: an array of objects containing module descriptions
```javascript
[
{
"name": "moduleA",
"hash": "6ebff28c",
"config": { "name": "moduleA" }
}
]
```
Example of service call:
```scheme
(call node ("dist" "list_modules") [] modules)
```
### dist get\_module\_interface
Get the interface of a module
* Arguments: hash of a module
* Returns: an interface of the module \( see _srv get\_interface \)_ mple of service call:
```scheme
(call node ("dist" "get_interface") [hash] interface)
```
### dist add\_blueprint
Used to add a blueprint to the node specified in the service call.
* Arguments: blueprint an object of the following structure
```javascript
{
"name": "good_service",
"dependencies": [ "hash:6ebff28c...", "hash:1e59875a...", "hash:d164a07..." ]
}
```
```text
Where module dependencies are specified as [_blake3_](https://crates.io/crates/blake3) hashes of modules
```
* Returns: Generated blueprint id
Example of service call:
```scheme
(call node ("dist" "add_blueprint") [blueprint] blueprint_id)
```
### dist list\_blueprints
Used to get the blueprints available on the node specified in the service call.
* Arguments: None
* Returns: an array of blueprint structures.
A blueprint is an object of the following structure:
```javascript
{
"id": "uuid-1234-...",
"name": "good_service",
"dependencies": [ "hash:6ebff28c...", "hash:1e59875a...", "hash:d164a07..." ]
}
```
Example of service call:
```scheme
(call node ("dist" "list_blueprints") [] blueprints)
```
#### script add
Adds a given script to a node. That script will be called with a fixed interval with the default setting at approx. three \(3\) seconds.
Recurring scripts can't read variables from data, they must be literal. That means that every address or value must be specified as a literal: \(call "QmNode" \("service\_id-1234-uuid" "function"\) \["arg1" "arg2"\]\).
* Arguments:
* _script_ a string containing "literal" script
* _interval_ an optional string containing interval in seconds. If set, the script will be executed periodically at that interval. If omitted, the script will be executed only once. All intervals are rounded to 3 seconds. The minimum interval is 3 seconds.
* Returns: uuid script id that can be used to remove that script
Example of service call:
* Without an interval parameter value, the script executes once:
```text
(call node ("script" "add") [script] id)
```
* With an interval parameter value _k_ passed as a string, the script executes every _k_ seconds \(21 in this case\)
```scheme
(call node ("script" "add") [script "21"] id)
```
### script remove
Removes recurring script from a node. Only a creator of the script can delete it
* Arguments: _script id_ \(as received from _script add_\)
* Returns: true if the script was deleted and false otherwise
Example of service call:
```scheme
(call node ("script" "remove") [script_id] result)
```
### script list
* Arguments: None
* Returns: A list of existing scripts on the node. Each object in the list is of the following structure:
```javascript
{
"id": "uuid-1234-...",
"src": "(seq (call ...", //
"failures": 0,
"interval": "21s",
"owner": "123DKooAbcEfgh..."
}
```
Example of a service call:
```scheme
(call node ("script" "list") [] list)
```
### op identity
Acts as an identity function. This service returns exactly what was passed to it. Useful for moving the execution of some service topologically or for extracting some data and putting it into an output variable.
Example of service call:
```scheme
(call node ("op" "identity") [args] result)
```
### deprecated add\_provider
Used in service aliasing. \_\*\*\_Stores the specified service provider \(provider\) in the internal storage of the node indicated in the service call and associates it with the given key \(key\). After executing add\_provider, the provider can be accessed via the get\_providers service using this key.
* Arguments:
* key a string; usually, it is a human-readable service alias.
* provider the location of the service. It is an object of the following structure:
```javascript
{
"peer": "123D...", // PeerId of some peer in the network
"service_id": "uuid-1234-..." // Optional service_id of the service running on the peer specified by peer
}
```
Example of service call:
```scheme
(call node ("deprecated" "add_provider") [key provider])
```
### deprecated get\_providers
Used in service aliasing to retrieve providers for a given key.
* Arguments: _key_ a string; usually, it is a human-readable service alias.
* Returns: an array of objects of the following structure:
```javascript
{
"peer": "123D...", // required field
"service_id": "uuid-1234-..." // optional field
}
```
Example of service call:
```scheme
(call node ("deprecated" "get_providers") [key] providers)
```

49
knowledge_tools.md Normal file
View File

@ -0,0 +1,49 @@
# Tools
### Fluence Proto Distributor: FLDIST
\`\`[`fldist`](https://github.com/fluencelabs/proto-distributor) is a command line interface \(CLI\) to Fluence peers allowing for the lifecycle management of services and offers the fastest and most effective way to service deployment.
```text
mbp16~(:|✔) % fldist --help
Usage: fldist <cmd> [options]
Commands:
fldist completion generate completion script
fldist upload Upload selected wasm
fldist get_modules Print all modules on a node
fldist get_interfaces Print all services on a node
fldist get_interface Print a service interface
fldist add_blueprint Add a blueprint
fldist create_service Create a service from existing blueprint
fldist new_service Create service from a list of modules
fldist deploy_app Deploy application
fldist create_keypair Generates a random keypair
fldist run_air Send an air script from a file. Send arguments to
"returnService" back to the client to print them in the
console. More examples in "scripts_examples" directory.
fldist env show nodes in currently selected environment
Options:
--help Show help [boolean]
--version Show version number [boolean]
-s, --seed Client seed [string]
--env Environment to use
[required] [choices: "dev", "testnet", "local"] [default: "testnet"]
--node-id, --node PeerId of the node to use
--node-addr Multiaddr of the node to use
--log log level
[required] [choices: "trace", "debug", "info", "warn", "error"] [default:
"error"]
--ttl particle time to live in ms
[number] [required] [default: 60000]
```
### Fluence JS SDK
The Fluence [JS SDK](https://github.com/fluencelabs/fluence-js) supports developers to build full-fledged applications for a variety of targets ranging from browsers to backend apps and greatly expands on the `fldist` capabilities.
### Marine Tools
Marine offers multiple tools including the Marine CLI, REPL and SDK. Please see the [Marine section](knowledge_aquamarine/marine/) for more detail.

13
node.md Normal file
View File

@ -0,0 +1,13 @@
# Node
The Fluence protocol is implemented as the Fluence [reference node](https://github.com/fluencelabs/fluence) which includes the
* Peer-to-peer communication layer
* Marine interpreter
* Aqua VM
* Builtin services
and more.
Builtin services are available on every Fluence peer and can be programmatically accessed and composed using Aqua. For a complete list of builtin services see the builtin.aqua file in the [Aqua Lib](https://github.com/fluencelabs/aqua-lib) repo. How to create your own builtin service, see the [Add Your Own Builtins](tutorials_tutorials/add-your-own-builtin.md) tutorial.

11
node/README.md Normal file
View File

@ -0,0 +1,11 @@
# Node
The Fluence protocol is implemented as the Fluence [reference node](https://github.com/fluencelabs/fluence) which includes the
* Peer-to-peer communication layer
* Marine interpreter
* Aqua VM
* Builtin services
and more.

View File

@ -1,8 +1,8 @@
# Services
# Builtin Services
## Overview
Each Fluence peer is equipped with a set of "built-in" services that can be called from Aquamarine and fall into the following namespaces:
Each Fluence node is equipped with a set of "builtin" services that can be called from Aquamarine and fall into the following namespaces:
1. _peer_ - operations related to connectivity or state of a given peer
2. _kad_ - Kademlia API

3
p2p.md
View File

@ -60,7 +60,8 @@ func foobar(node:string, service_id:string, func_name:string) -> string:
<- res!
```
As long as foo\(\) is running, the entire FooBar service, including bar\(\), is blocked. This has implications with respect to both service granularity and redundancy.
As long as foo\(\) is running, the entire FooBar service, including bar\(\), is blocked. This has implications with respect to both service granularity and redundancy, where service granularity captures to number of functions per service and redundancy refers to the number of service instances deployed to different peers.
## Summary

View File

@ -0,0 +1,2 @@
# Untitled

View File

@ -1,6 +1,6 @@
# Add Your Own Builtins
As discussed in the [Node](../knowledge_knowledge/node/knowledge_node_services.md) section, some service functionalities have ubiquitous demand making them suitable candidates to be directly deployed to a peer node. The [Aqua distributed hash table](https://github.com/fluencelabs/fluence/tree/master/deploy/builtins/aqua-dht) \(DHT\) is an example of builtin service. The remainder of this tutorial guides you through the steps necessary to create and deploy a Builtin service.
As discussed in the [Node]() section, some service functionalities have ubiquitous demand making them suitable candidates to be directly deployed to a peer node. The [Aqua distributed hash table](https://github.com/fluencelabs/fluence/tree/master/deploy/builtins/aqua-dht) \(DHT\) is an example of builtin service. The remainder of this tutorial guides you through the steps necessary to create and deploy a Builtin service.
In order to have a service available out-of-the-box with the necessary startup and scheduling scripts, we can take advantage of the Fluence [deployer feature](https://github.com/fluencelabs/fluence/tree/master/deploy) for Node native services. This feature handles the complete deployment process including
@ -10,7 +10,7 @@ In order to have a service available out-of-the-box with the necessary startup a
Note that the deployment process is a fully automated workflow requiring you to merely submit your service assets, i.e., Wasm modules and configuration scripts, in the appropriate format as a PR to the [Fluence](https://github.com/fluencelabs/fluence) repository.
At this point you should have a solid grasp of creating service modules and their associated configuration files. See the [Developing Modules And Services](../development_development/) section for more details.
At this point you should have a solid grasp of creating service modules and their associated configuration files. See the [Developing Modules And Services]() section for more details.
Our first step is fork the [Fluence](https://github.com/fluencelabs/fluence) repo by clicking on the Fork button, upper right of the repo webpage, and follow the instructions to create a local copy. In your local repo copy, checkout a new branch with a new, unique branch name:
@ -97,7 +97,7 @@ and the associated data file:
### **Scheduling Script**
Scheduling scripts allow us to decouple service execution from the client and instead can rely on a cron-like scheduler running on a node to trigger our service\(s\). For a brief overview, see [additional concepts](../development_development/development_reward_block_app/development_additional_concepts.md)
Scheduling scripts allow us to decouple service execution from the client and instead can rely on a cron-like scheduler running on a node to trigger our service\(s\). For a brief overview, see [additional concepts]()
### Directory Structure

View File

@ -0,0 +1,242 @@
# Building a Frontend with JS-SDK
Fluence provides means to connect to the network from a javascript environment. It is currently tested to work in nodejs and modern browsers.
To create an application you will need two main building blocks: the JS SDK itself and the aqua compiler. Both of them are provided in a form npm packages. JS SDK wraps the air interpreter and provides a connection to the network. There is low-level api for executing air scripts and registering for service calls handlers. Aqua compiler allows to write code in aqua language and compile it into typescript code which can be directly used with the SDK.
Even though all the logic could be programmed by hand with raw air it is strongly recommended to use aqua.
### Basic usage
As previously said you can use Fluence with any frontend or nodejs framework. JS SDK could be as any other npm library. For the purpose of the demo we will init a bare-bones nodejs package and show you the steps needed install JS SDK and aqua compiler. Feel free to use the tool most suitable for the framework used in application, the installation process should be roughly the same
#### 1. Start with npm package
For the purpose of the demo we will use a very minimal npm package with typescript support:
```text
src
┗ index.ts (1)
package.json (2)
tsconfig.json
```
index.ts \(1\):
```typescript
async function main() {
console.log("Hello, world!");
}
main();
```
package.json \(2\):
```javascript
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"exec": "node -r ts-node/register src/index.ts"
},
"author": "",
"license": "ISC",
"devDependencies": {
"ts-node": "^9.1.1",
"typescript": "^4.2.4"
}
}
```
Let's test if it works:
```bash
$ npm install
$ npm run exec
```
The following should be printed
```bash
$ npm run exec
> demo@1.0.0 exec C:\work\demo
> node -r ts-node/register src/index.ts
Hello, world!
$ C:\work\demo>
```
#### 2. Setting JS SDK and connecting to Fluence network
Install the dependencies, you will need these two packages.
```bash
npm install @fluencelabs/fluence @fluencelabs/fluence-network-environment
```
The first one is the SDK itself and the second is a maintained list of Fluence networks and nodes to connect to.
All of the communication with the Fluence network is done by using `FluenceClient`. You can create one with `createClient` function. The client encapsulates air interpreter and connects to the network through the relay. Currently any node in the network can be used a relay.
```typescript
import { createClient } from "@fluencelabs/fluence";
import { testNet } from "@fluencelabs/fluence-network-environment";
async function main() {
const client = await createClient(testNet[1]);
console.log("Is client connected: ", client.isConnected);
await client.disconnect();
}
main();
```
Let's try it out:
```bash
$ npm run exec
> demo@1.0.0 exec C:\work\demo
> node -r ts-node/register src/index.ts
Is client connected: true
$
```
**Note**: typically you should have a single instance`FluenceClient` per application since it represents it's identity in the network. You are free to store the instance anywhere you like.
#### 3. Setting Up The Aqua Compiler
Aqua is the proffered language for the Fluence network. It can be used with javascript-based environments via npm package.
{% hint style="warning" %}
**The package requires java to be installed as it calls "java -jar ... "**
{% endhint %}
```bash
npm install --save-dev @fluencelabs/aqua-cli
```
We will also need the standard library for the language
```text
npm install --save-dev @fluencelabs/aqua-lib
```
Let's add our first aqua file:
```text
aqua (1)
┗ demo.aqua (2)
node_modules
src
┣ compiled (3)
┗ index.ts
package-lock.json
package.json
tsconfig.json
```
Add two directories, one for aqua sources \(1\) and another for the typescript output \(3\)
Create a new text file called `demo.aqua` \(2\):
```text
import "@fluencelabs/aqua-lib/builtin.aqua"
func demo(nodePeerId: PeerId) -> []string:
on nodePeerId:
res <- Peer.identify()
<- res.external_addresses
```
This script will gather the list of external addresses from some node in the network. For more information about the aqua language refer to the aqua documentation.
The aqua code can now be compiled by using the compiler CLI. We suggest adding a script to the package.json file like so:
```javascript
...
"scripts": {
"exec": "node -r ts-node/register src/index.ts",
"compile-aqua": "aqua-cli -i ./aqua/ -o ./src/compiled"
},
...
```
Run the compiler:
```bash
npm run compile-aqua
```
A typescript file should be generated like so:
```text
aqua
┗ demo.aqua
node_modules
src
┣ compiled
┃ ┗ demo.ts <--
┗ index.ts
package-lock.json
package.json
tsconfig.json
```
#### 4. Consuming The Compiled Code
Using the code generated by the compiler is as easy as calling a function. The compiler generates all the boilerplate needed to send a particle into the network and wraps it into a single call. Note that all the type information and therefore type checking and code completion facilities are there!
Let's do it!
```typescript
import { createClient } from "@fluencelabs/fluence";
import { testNet } from "@fluencelabs/fluence-network-environment";
import { demo } from "./compiled/demo"; // import the generated file
async function main() {
const client = await createClient(testNet[1]);
console.log("Is client connected: ", client.isConnected);
const otherNode = testNet[2].peerId;
const addresses = await demo(client, otherNode); // call it like a normal function in typescript
console.log(`Node ${otherNode} is connected to: ${addresses}`);
await client.disconnect();
}
main();
```
if everything is fine you have similar result:
```text
$ npm run exec
> demo@1.0.0 exec C:\work\demo
> node -r ts-node/register src/index.ts
Is client connected: true
Node 12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er is connected to: /ip4/138.197.189.50/tcp/7001,/ip4/138.197.189.50/tcp/9001/ws
$
```
### Advanced usage
Fluence JS SDK gives options to register own handlers for aqua vm service calls
**TBD**
### References
* For the list of compiler options see: [https://github.com/fluencelabs/aqua](https://github.com/fluencelabs/aqua)
* Repository with additional examples: [**https://github.com/fluencelabs/aqua-playground**](https://github.com/fluencelabs/aqua-playground)\*\*\*\*

View File

@ -0,0 +1,105 @@
# cUrl As A Service
### Overview
[Curl](https://curl.se/) is a widely available and used command-line tool to receive or send data using URL syntax. Chances are, you probably just used it when you set up your Fluence development environment. For Fluence services to be able to interact with the world, cUrl is one option to facilitate https calls. Since Fluence modules are Wasm IT modules, cUrl cannot not be a service intrinsic. Instead, the curl command-line tool needs to be made available and accessible at the node level. And for Fluence services to be able to interact with Curl, we need to code a cUrl adapter taking care of the mounted \(cUrl\) binary.
### Adapter Construction
The work for the cUrl adapter has been fundamentally done and is exposed by the Fluence Rust SDK. As a developer, the task remaining is to instantiate the adapter in the context of the module and services scope. The following code [snippet](https://github.com/fluencelabs/fce/tree/master/examples/url-downloader/curl_adapter) illustrates the implementation requirement.
```rust
use fluence::fce;
use fluence::WasmLoggerBuilder;
use fluence::MountedBinaryResult;
pub fn main() {
WasmLoggerBuilder::new().build().unwrap();
}
#[fce]
pub fn download(url: String) -> String {
log::info!("get called with url {}", url);
let result = unsafe { curl(vec![url]) };
String::from_utf8(result.stdout).unwrap()
}
#[fce]
#[link(wasm_import_module = "host")]
extern "C" {
fn curl(cmd: Vec<String>) -> MountedBinaryResult;
}
```
with the following dependencies necessary in the Cargo.toml:
```rust
fluence = { version = "=0.4.2", features = ["logger"] }
log = "0.4.8"
```
We are basically linking the [external](https://doc.rust-lang.org/std/keyword.extern.html) cUrl binary and are exposing access to it as a FCE interface called download.
### Code References
* [Mounted binaries](https://github.com/fluencelabs/fce/blob/c559f3f2266b924398c203a45863ebf2fb9252ec/fluence-faas/src/host_imports/mounted_binaries.rs)
* [cUrl](https://github.com/curl/curl)
### Service Construction
In order to create a valid Fluence service, a service configuration is required.
```text
modules_dir = "target/wasm32-wasi/release"
[[module]]
name = "curl_adapter"
logger_enabled = true
[mounted.mounted_binaries]
curl = "/usr/bin/curl"
```
We are specifying the location of the Wsasm file, the import name of the Wasm file, some logging housekeeping, and the mounted binary reference with the command-line call information.
### Remote Service Creation
```bash
cargo new curl-service
cd curl-service
# copy the above rust code into src/main
# copy the specified dependencies into Cargo.toml
# copy the above service configuration into Config.toml
fce build --release
```
You should have the Fluence module curl\_adapter.wasm in `target/wasm32-wasi/release` we can test our service with `fce-repl`.
### Service `Test`
Running the REPL, we use the `interface` command to list all available interfaces and the `call` command to run a method. Fr our purposes, we furnish the [https://duckduckgo.com/?q=Fluence+Labs](https://duckduckgo.com/?q=Fluence+Labs) url to give the the curl adapter a workout.
```bash
fce-repl Config.toml
Welcome to the FCE REPL (version 0.5.2)
app service was created with service id = 8ad81c3a-8c5c-4730-80d1-c54cd177725d
elapsed time 40.312376ms
1> interface
Loaded modules interface:
curl_adapter:
fn download(url: String) -> String
2> call curl_adapter download ["https://duckduckgo.com/?q=Fluence+Labs"]
result: String("<!DOCTYPE html><html lang=\"en-US\" class=\"no-js has-zcm no-theme\"><head><meta name=\"description\" content=\"DuckDuckGo. Privacy, Simplified.\"><meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"><title>Fluence Labs at DuckDuckGo</title><link rel=\"stylesheet\" href=\"/s1963.css\" type=\"text/css\"><link rel=\"stylesheet\" href=\"/r1963.css\" type=\"text/css\"><meta name=\"robots\" content=\"noindex,nofollow\"><meta name=\"referrer\" content=\"origin\"><meta name=\"apple-mobile-web-app-title\" content=\"Fluence Labs\"><link rel=\"preconnect\" href=\"https://links.duckduckgo.com\"><link rel=\"preload\" href=\"/font/ProximaNova-Reg-webfont.woff2\" as=\"font\" type=\"font/woff2\" crossorigin=\"anonymous\" /><link rel=\"preload\" href=\"/font/ProximaNova-Sbold-webfont.woff2\" as=\"font\" type=\"font/woff2\" crossorigin=\"anonymous\" /><link rel=\"shortcut icon\" href=\"/favicon.ico\" type=\"image/x-icon\" /><link id=\"icon60\" rel=\"apple-touch-icon\" href=\"/assets/icons/meta/DDG-iOS-icon_60x60.png?v=2\"/><link id=\"icon76\" rel=\"apple-touch-icon\" sizes=\"76x76\" href=\"/assets/icons/meta/DDG-iOS-icon_76x76.png?v=2\"/><link id=\"icon120\" rel=\"apple-touch-icon\" sizes=\"120x120\" href=\"/assets/icons/meta/DDG-iOS-icon_120x120.png?v=2\"/><link id=\"icon152\" rel=\"apple-touch-icon\"s
<snip>
ript\">DDG.index = DDG.index || {}; DDG.index.signalSummary = \"\";</script>")
elapsed time: 334.545388ms
3>
```

View File

@ -0,0 +1,51 @@
# Setting Up Your Environment
In order to develop within the Fluence solution, [Rust](https://www.rust-lang.org/tools/install) and small number of tools are required.
## Rust
Install Rust:
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
once Rust is installed, we need to expand the toolchain and include [nightly build](https://rust-lang.github.io/rustup/concepts/channels.html) and the [Wasm](https://doc.rust-lang.org/stable/nightly-rustc/rustc_target/spec/wasm32_wasi/index.html) compile target.
```bash
rustup install nightly
rustup target add wasm32-wasi
```
To keep Rust and the toolchains updated:
```bash
rustup self update
rustup update
```
There are a number of good Rust installation and IDE integration tutorials available. [DuckDuckGo](https://duckduckgo.com/) is your friend but if that's too much effort, have a look at [koderhq](https://www.koderhq.com/tutorial/rust/environment-setup/).
## Fluence Tools
Fluence provides several tools to support developers. Fluence cli, `flcli`, facilitates the compilation of modules to the necessary wasm32-wasi target. Fluence REPL, `fce-repl`, on the other hand, is a cli tool to test and experiment with FCE modules and services locally.
```bash
cargo install fcli
cargo +nightly install frepl
```
In addition, Fluence provides the [proto-distributor](https://github.com/fluencelabs/proto-distributor) tool, aka `fldist`, for service lifecyle management. From deploying services to the network to executing AIR scripts, `fldist` does it all.
```bash
npm install -g @fluencelabs/fldist
```
## Fluence SDK
For frontend development, the Fluence [JS-SDK](https://github.com/fluencelabs/fluence-js) is currently the favored, and only, tool.
```bash
npm install @fluencelabs/fluence
```