mirror of
https://github.com/fluencelabs/examples
synced 2024-12-04 19:20:17 +00:00
Changes to reflect using fluence cli:
+ changed project structure; + added fluence cli related files; + updated aqua code; + updated readme.
This commit is contained in:
parent
d90315d0c8
commit
b8a700f745
7
aqua-examples/ts-oracle/.gitignore
vendored
Normal file
7
aqua-examples/ts-oracle/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
.idea
|
||||
.DS_Store
|
||||
.fluence
|
||||
**/node_modules
|
||||
**/target/
|
||||
.vscode/settings.json
|
||||
.repl_history
|
@ -10,94 +10,147 @@ Getting accurate timestamps can be problematic in various contexts including blo
|
||||
|
||||
Fluence provides an open Web3 protocol, framework and associated tooling to develop and host applications, interfaces and backends on permissionless peer-to-peer networks. An integral part of the Fluence solution is the Aquamarine stack comprised of Aqua and Marine. Aqua is a new programming language and paradigm purpose-built to program distributed networks and compose applications from distributed services. For more information on Aqua, see
|
||||
|
||||
* [Aqua Book](https://app.gitbook.com/@fluence/s/aqua-book/)
|
||||
* [Aqua Book](https://doc.fluence.dev/aqua-book/)
|
||||
* [Aqua Playground](https://github.com/fluencelabs/aqua-playground)
|
||||
* [Aqua repo](https://github.com/fluencelabs/aqua)
|
||||
|
||||
|
||||
Marine is a general purpose Wasm runtime and toolkit, allows developers to build distributed services that can be composed into distributed applications by Aqua. For more information on Marine, see
|
||||
Marine is a general-purpose Wasm runtime and toolkit, it allows developers to build distributed services that can be composed into distributed applications by Aqua. For more information on Marine, see
|
||||
|
||||
* [Marine Book](https://doc.fluence.dev/marine-book/)
|
||||
* [Marine repo](https://github.com/fluencelabs/marine)
|
||||
* [Marine SDK](https://github.com/fluencelabs/marine-rs-sdk)
|
||||
|
||||
### Setup
|
||||
|
||||
*Note that we already deployed the service to node `12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf` with service id `ed657e45-0fe3-4d6c-b3a4-a2981b7cadb9`, which is all what's needed to use the service in Aqua.*
|
||||
In order to run the entire code base, Rust and Node are required. If necessary see [Install Rust](https://www.rust-lang.org/tools/install) and [NVM](https://github.com/nvm-sh/nvm) for details.
|
||||
|
||||
In order to run the entire code base, Rust and Node required. If necessary see [Install Rust](https://www.rust-lang.org/tools/install) and [NVM](https://github.com/nvm-sh/nvm) for details.
|
||||
|
||||
Install the following tools:
|
||||
Install the Fluence CLI by running the following command:
|
||||
|
||||
```bash
|
||||
cargo install mrepl
|
||||
cargo install marine
|
||||
|
||||
npm -g install @fluencelabs/aqua
|
||||
```
|
||||
|
||||
To compile the code to the wasi target:
|
||||
|
||||
```bash
|
||||
./scripts/build.sh
|
||||
npm install -g @fluencelabs/cli
|
||||
```
|
||||
|
||||
You can use the REPL to locally test the services:
|
||||
|
||||
```bash
|
||||
mrepl Config.toml
|
||||
fluence service repl tsOracle
|
||||
```
|
||||
|
||||
test the code with
|
||||
You will get the following:
|
||||
|
||||
```bash
|
||||
Making sure service and modules are downloaded and built... done
|
||||
Welcome to the Marine REPL (version 0.18.0)
|
||||
Minimal supported versions
|
||||
sdk: 0.6.0
|
||||
interface-types: 0.20.0
|
||||
|
||||
app service was created with service id = 5e21f219-0482-49d8-bc99-bd2c05bbfefa
|
||||
elapsed time 131.785135ms
|
||||
|
||||
1> i
|
||||
Loaded modules interface:
|
||||
exported data types (combined from all modules):
|
||||
data Oracle:
|
||||
n: u32
|
||||
mode: u64
|
||||
freq: u32
|
||||
err_str: string
|
||||
|
||||
exported functions:
|
||||
ts_oracle:
|
||||
fn point_estimate(tstamps: []u64, min_points: u32) -> Oracle
|
||||
|
||||
2> call ts_oracle point_estimate [ [1661345701640, 1661345701640, 1661345701662, 1661345701642, 1661345701643, 1661345701637, 1661345701642, 1661345701663, 1661345701678, 1661345701641, 1661345701647], 5]
|
||||
result: Object({"err_str": String(""), "freq": Number(2), "mode": Number(1661345701640), "n": Number(11)})
|
||||
elapsed time: 8.608047ms
|
||||
|
||||
3> q
|
||||
```
|
||||
|
||||
On the 1st step, we explored our module interface and data using the `i` command, on the 2nd we made a call of the `point_estimate` function from the `ts_oracle` interface.
|
||||
|
||||
If you navigate to our module directory `ts-oracle/modules/ts-oracle`, you can also unit test the code with
|
||||
|
||||
```bash
|
||||
cargo +nightly test --release
|
||||
```
|
||||
|
||||
and deploy the service to a peer of your choice with the `aqua` cli tool:
|
||||
It generates the output similar to:
|
||||
|
||||
```
|
||||
....
|
||||
running 6 tests
|
||||
test tests::test_mean_good ... ok
|
||||
test tests::test_mean_bad ... ok
|
||||
test tests::test_mode ... ok
|
||||
test tests::test_point_estimate_bad_2 ... ok
|
||||
test tests::test_point_estimate_bad ... ok
|
||||
test tests::test_point_estimate_good ... ok
|
||||
|
||||
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.35s
|
||||
```
|
||||
|
||||
For deploying the service to a peer with the `fluence` CLI tool, you need to run from the project root:
|
||||
|
||||
```bash
|
||||
aqua remote deploy_service \
|
||||
--addr krasnodar-06 \
|
||||
--config-path configs/ts_oracle_deploy_cfg.json \
|
||||
--service ts-oracle \
|
||||
--sk <your secret key>
|
||||
fluence deploy
|
||||
```
|
||||
|
||||
which results in:
|
||||
|
||||
```bash
|
||||
Your peerId: 12D3KooWHS2Eys5GmmdBGZHwUGRM4TRvcaW4m7dgGAShxbMEcKms
|
||||
"Going to upload a module..."
|
||||
2022.02.08 14:30:48 [INFO] created ipfs client to /ip4/161.35.212.85/tcp/5001
|
||||
2022.02.08 14:30:48 [INFO] connected to ipfs
|
||||
2022.02.08 14:30:50 [INFO] file uploaded
|
||||
"Now time to make a blueprint..."
|
||||
"Blueprint id:"
|
||||
"5617e051fbd7f8e18dfc16cace62f71f0ff090d576ee09e3520c196ef44f6a79"
|
||||
"And your service id is:"
|
||||
"388d8717-b547-43c3-af05-b4713893d453"
|
||||
Making sure all services are downloaded... done
|
||||
Making sure all modules are downloaded and built... done
|
||||
|
||||
Going to deploy services described in <path-to-examples>/aqua-examples/ts-oracle/fluence.yaml:
|
||||
|
||||
tsOracle:
|
||||
get: ./ts-oracle
|
||||
deploy:
|
||||
- deployId: default
|
||||
|
||||
|
||||
? Do you want to deploy all of these services? Yes
|
||||
Deploying:
|
||||
service: tsOracle
|
||||
deployId: default
|
||||
on: 12D3KooWCMr9mU894i8JXAFqpgoFtx6qnV1LFPSfVc3Y34N4h4LS
|
||||
... done
|
||||
Compiling <path-to-examples>/aqua-examples/ts-oracle/.fluence/aqua/deployed.app.aqua... done
|
||||
|
||||
Currently deployed services listed in <path-to-examples>/aqua-examples/ts-oracle/.fluence/app.yaml:
|
||||
|
||||
tsOracle:
|
||||
default:
|
||||
- blueprintId: b2325812dc59af855ac7d1ea45ce12fb033822dc88c8a26425007bfd4eafae1c
|
||||
serviceId: 605b227c-e67a-4888-a65a-60df6a1fb862
|
||||
peerId: 12D3KooWCMr9mU894i8JXAFqpgoFtx6qnV1LFPSfVc3Y34N4h4LS
|
||||
|
||||
```
|
||||
|
||||
As always, take note of the service id!
|
||||
The tool asked us if we want to deploy the service, and after confirming it, we have our service successfully deployed. Please also note in the output that we got the wrapper for our service environment generated in the `./.fluence/aqua/deployed.app.aqua` file.
|
||||
|
||||
### Approach
|
||||
|
||||
We implemented a custom service that returns the mode and frequency for an array of timestamps, see `src/` that can be deployed on to any node of the peer-to-peer network and, once deployed, used to in an Aqua script. Moreover, network peers have builtin services including Kademlia and timestamp services. Both custom and bultin services are accessible by Aqua and ready for composition into an application.
|
||||
We implemented a custom service that returns the mode and frequency for an array of timestamps, see `ts-oracle/modules/ts-oracle/src` that can be deployed onto any node of the peer-to-peer network and, once deployed, used in an Aqua script. Moreover, network peers have built-in services including Kademlia and timestamp services. Both custom and bultin services are accessible by Aqua and ready for composition into an application.
|
||||
|
||||
Our oracle solution is implemented in Aqua and utilizes timestamps from peers selected from our Kademlia neighborhood and, for illustrative purposes, use the deployed service to arrive at the point estimate for our oracle. See `src/main.rs`. There certanly are better ways to process the timestamps into an oracle but for our purposes, mode works.
|
||||
Our oracle solution is implemented in Aqua and utilizes timestamps from peers selected from our Kademlia neighborhood and, for illustrative purposes, uses the deployed service to arrive at the point estimate for our oracle. See `ts-oracle/modules/ts-oracle/src/main.rs`. There certainly are better ways to process the timestamps into an oracle but for our purposes mode works.
|
||||
|
||||
In our Aqua script, `aqua-scripts/ts_getter.aqua`, we separate the timestamp collections from the subsequent oracle processing. That is, if a peer-client wants to process the timestamps locally, all that's needed are the timestamps, which can be obtained by calling the `ts_getter` function. Alternatively, the timestamps may be processed by calling one or more `ts-oracle` services deployed on the network.
|
||||
In our Aqua script, `src/aqua/main.aqua`, we separate the timestamp collections from the subsequent oracle processing. That is, if a peer-client wants to process the timestamps locally, all that's needed are the timestamps, which can be obtained by calling the `ts_getter` function. Alternatively, the timestamps may be processed by calling one or more `ts-oracle` services deployed on the network.
|
||||
|
||||
```aqua
|
||||
-- aqua-scripts/ts_getter.aqua
|
||||
import "builtin.aqua"
|
||||
```rust
|
||||
-- src/aqua/main.aqua
|
||||
module Main
|
||||
|
||||
service Op2("op"):
|
||||
identity(s: u64)
|
||||
array(a: string, b: u64) -> string
|
||||
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||
import App from "deployed.app.aqua"
|
||||
|
||||
export App, ts_getter, ts_oracle
|
||||
|
||||
-- the data struct from the Wasm file
|
||||
-- marine aqua artifacts/ts_oracle.wasm
|
||||
-- marine aqua ts-oracle/modules/ts-oracle/target/wasm32-wasi/release/ts_oracle.wasm
|
||||
data Oracle:
|
||||
n: u32
|
||||
mode: u64
|
||||
@ -106,84 +159,140 @@ data Oracle:
|
||||
raw_data: []u64
|
||||
|
||||
-- the point_estimate function from the Wasm file wrapped as a service
|
||||
-- marine aqua artifacts/ts_oracle.wasm
|
||||
-- marine aqua ts-oracle/modules/ts-oracle/target/wasm32-wasi/release/ts_oracle.wasm
|
||||
service TSOracle("service-id"):
|
||||
point_estimate: []u64, u32 -> Oracle
|
||||
|
||||
-- the timestamp getter drawing from the Kademlia neighborhood
|
||||
-- function collects the neighborhood peers
|
||||
-- retrieves and returns the timestamps
|
||||
func ts_getter(node: string) -> []u64:
|
||||
-- the peer id gets from the wrapper of our service interface
|
||||
-- for handling timeouts from the neighborhood peers, the race pattern is used
|
||||
func ts_getter() -> []u64:
|
||||
rtt = 1000 -- millis
|
||||
msg = "timeout"
|
||||
res: *u64
|
||||
on node:
|
||||
k <- Op.string_to_b58(node)
|
||||
|
||||
services <- App.services()
|
||||
|
||||
on services.tsOracle.default!.peerId:
|
||||
k <- Op.string_to_b58(services.tsOracle.default!.peerId)
|
||||
nodes <- Kademlia.neighborhood(k, nil, nil)
|
||||
for n <- nodes par:
|
||||
on n:
|
||||
try:
|
||||
res <- Peer.timestamp_ms()
|
||||
Op2.identity(res!9)
|
||||
if Op.array_length(nodes) > 0:
|
||||
for n <- nodes par:
|
||||
on n:
|
||||
try:
|
||||
res <- Peer.timestamp_ms()
|
||||
-- whether we get res from all the nodes or rtt milliseconds passed, return res
|
||||
join res[Op.array_length(nodes) - 1]
|
||||
par Peer.timeout(rtt, msg)
|
||||
|
||||
<- res
|
||||
|
||||
-- oracle function operating on array of timestamps utilizing a distributed
|
||||
-- service to calculate the point_estimate
|
||||
-- see src/main.rs for the service implementation
|
||||
func ts_oracle(node: string, oracle_service_id: string, min_points:u32) -> Oracle:
|
||||
res <- ts_getter(node)
|
||||
|
||||
on node:
|
||||
TSOracle oracle_service_id
|
||||
-- see ts-oracle/modules/ts-oracle/src/main.rs for the service implementation
|
||||
-- the running service environment is injected using the generated
|
||||
-- wrapper defined in .fluence/aqua/deployed.app.aqua and
|
||||
-- imported in the beginning of the src/aqua/main.aqua
|
||||
func ts_oracle(min_points: u32) -> Oracle:
|
||||
services <- App.services()
|
||||
res <- ts_getter()
|
||||
on services.tsOracle.default!.peerId:
|
||||
TSOracle services.tsOracle.default!.serviceId
|
||||
oracle <- TSOracle.point_estimate(res, min_points) -- calculate mode
|
||||
<- oracle -- and return to initiating peer
|
||||
```
|
||||
|
||||
We can run our Aqua `ts_oracle` script against the deployed processing service to get our oracle point estimate using `aqua run` (Note that you can replace the service id with the one you obtained from your deployment):
|
||||
We can run our Aqua `ts_oracle` script against the deployed processing service to get our oracle point estimate using `fluence run`:
|
||||
|
||||
```bash
|
||||
aqua run \
|
||||
-i aqua \
|
||||
-a /dns4/kras-02.fluence.dev/tcp/19001/wss/p2p/12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf \
|
||||
-f 'ts_oracle("12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf", "ed657e45-0fe3-4d6c-b3a4-a2981b7cadb9", 5)'
|
||||
fluence run \
|
||||
-i src/aqua \
|
||||
-f 'ts_oracle(5)'
|
||||
```
|
||||
|
||||
Which results in below but may be different for you:
|
||||
Please note that with `fluence run` we don't provide both the peer id and service id for our service. However, this information is used implicitly by the CLI tool, and is taken from definitions located in `.fluence/app.yaml` generated upon successful deployment.
|
||||
|
||||
The run results in below but may be different for you:
|
||||
|
||||
```bash
|
||||
Running:
|
||||
function: ts_oracle(5)
|
||||
relay: /dns4/kras-08.fluence.dev/tcp/19001/wss/p2p/12D3KooWFtf3rfCDAfWwt6oLZYZbDfn9Vn7bv7g6QjjQxUUEFVBt
|
||||
... done
|
||||
|
||||
Result:
|
||||
|
||||
```text
|
||||
Your peerId: 12D3KooWDB6bCvW7vim4iw88TRyyuouLiyZz2Cr2ZdaaBZKJ916d
|
||||
{
|
||||
"err_str": "",
|
||||
"freq": 3,
|
||||
"mode": 1644352733889,
|
||||
"n": 12
|
||||
"mode": 1661337641208,
|
||||
"n": 11
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, we can run the `ts_getter` functions just for the timestamps:
|
||||
|
||||
```bash
|
||||
aqua run \
|
||||
-i aqua \
|
||||
-a /dns4/kras-02.fluence.dev/tcp/19001/wss/p2p/12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf \
|
||||
-f 'ts_getter("12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf")'
|
||||
fluence run \
|
||||
-i src/aqua \
|
||||
-f 'ts_getter()'
|
||||
```
|
||||
|
||||
Which gives us just the timestamps, which wil be different for you:
|
||||
It gives us just the timestamps, which will be different for you:
|
||||
|
||||
```text
|
||||
Your peerId: 12D3KooWEdPZCXxSEDMLMTR8doUVhjcZ6YB5CUJnq36QwU65XVFH
|
||||
Running:
|
||||
function: ts_getter()
|
||||
relay: /dns4/kras-07.fluence.dev/tcp/19001/wss/p2p/12D3KooWEFFCZnar1cUJQ3rMWjvPQg6yMV2aXWs2DkJNSRbduBWn
|
||||
... done
|
||||
|
||||
Result:
|
||||
|
||||
[
|
||||
1644352807634,
|
||||
1644352807632,
|
||||
1644352807630,
|
||||
1644352807629,
|
||||
1644352807650,
|
||||
1644352807630,
|
||||
1644352807633,
|
||||
1644352807633,
|
||||
1644352807634,
|
||||
1644352807631,
|
||||
1644352807631
|
||||
1661342995428,
|
||||
1661342995429,
|
||||
1661342995432,
|
||||
1661342995424,
|
||||
1661342995428,
|
||||
1661342995429,
|
||||
1661342995424,
|
||||
1661342995473,
|
||||
1661342995480,
|
||||
1661342995433,
|
||||
1661342995430
|
||||
]
|
||||
```
|
||||
|
||||
Instead of `aqua run`, you can use the Typescript stub and integrate it into a TS client. See [Aqua Playground](https://github.com/fluencelabs/aqua-playground) for more information.
|
||||
To do the housekeeping, we need to remove the service we deployed. It can be easily done with:
|
||||
|
||||
```bash
|
||||
fluence remove
|
||||
```
|
||||
|
||||
It results in the following output:
|
||||
|
||||
```bash
|
||||
? Are you sure you want to remove app described in <path-to-examples>/aqua-examples/ts-oracle/.fluence/app.yaml? Yes
|
||||
?
|
||||
|
||||
Currently deployed services described in <path-to-examples>/aqua-examples/ts-oracle/.fluence/app.yaml:
|
||||
|
||||
tsOracle:
|
||||
default:
|
||||
- blueprintId: b2325812dc59af855ac7d1ea45ce12fb033822dc88c8a26425007bfd4eafae1c
|
||||
serviceId: 605b227c-e67a-4888-a65a-60df6a1fb862
|
||||
peerId: 12D3KooWCMr9mU894i8JXAFqpgoFtx6qnV1LFPSfVc3Y34N4h4LS
|
||||
|
||||
|
||||
Do you want to remove all of them? Yes
|
||||
Removing:
|
||||
service: tsOracle
|
||||
deployId: default
|
||||
peerId: 12D3KooWCMr9mU894i8JXAFqpgoFtx6qnV1LFPSfVc3Y34N4h4LS
|
||||
serviceId: 605b227c-e67a-4888-a65a-60df6a1fb862
|
||||
... done
|
||||
```
|
||||
|
||||
Please notice instead of `fluence run`, you can use the Typescript stub and integrate it into a TS client. See [Aqua Playground](https://github.com/fluencelabs/aqua-playground) for more information.
|
||||
|
@ -1,233 +0,0 @@
|
||||
-- Default public interface of Fluence nodes
|
||||
|
||||
alias Field : []string
|
||||
alias Argument : []string
|
||||
alias Bytes : []u8
|
||||
alias PeerId : string
|
||||
alias Pairs : [][]string
|
||||
alias Base58String : string
|
||||
alias Hash : string
|
||||
|
||||
-- There are two types of dependencies: named and by-hash.
|
||||
-- name:foobar – specifies dependency by module name, points to a module with import name 'foobar'
|
||||
-- hash:04dc884... – specifies dependency by module hash
|
||||
-- By-hash dependencies are preffered since they are determenistic
|
||||
-- while by-name dependency can yield different modules at different points in time
|
||||
alias Dependency : string
|
||||
|
||||
data Service:
|
||||
id: string
|
||||
blueprint_id: string
|
||||
owner_id: string
|
||||
|
||||
data FunctionSignature:
|
||||
arguments: []Argument
|
||||
name: string
|
||||
output_types: []string
|
||||
|
||||
data RecordType:
|
||||
fields: []Field
|
||||
id: u64
|
||||
name: string
|
||||
|
||||
data Interface:
|
||||
function_signatures: []FunctionSignature
|
||||
record_types: []RecordType
|
||||
|
||||
data Info:
|
||||
external_addresses: []string
|
||||
|
||||
data ModuleConfig:
|
||||
name: string
|
||||
|
||||
data Module:
|
||||
name: string
|
||||
hash: string
|
||||
config: ModuleConfig
|
||||
|
||||
data AddBlueprint:
|
||||
name: string
|
||||
dependencies: []Dependency
|
||||
|
||||
data Blueprint:
|
||||
id: string
|
||||
name: string
|
||||
dependencies: []Dependency
|
||||
|
||||
data ScriptInfo:
|
||||
id: string
|
||||
src: string
|
||||
failures: u32
|
||||
interval: string
|
||||
owner: string
|
||||
|
||||
data Contact:
|
||||
peer_id: string
|
||||
addresses: []string
|
||||
|
||||
service Op("op"):
|
||||
-- does nothing
|
||||
noop()
|
||||
-- returns length of the passed array
|
||||
array_length(array: []string) -> u32
|
||||
-- takes any number of arguments and wraps them into a single array
|
||||
array(a: string, b: ?string, c: ?string, d: ?string) -> []string
|
||||
-- takes any number of arrays and flattens them by concatenating
|
||||
concat(a: []string, b: ?[]string, c: ?[]string, d: ?[]string) -> []string
|
||||
-- takes a single argument and returns it back
|
||||
identity(s: ?string) -> ?string
|
||||
string_to_b58(s: string) -> Base58String
|
||||
string_from_b58(b: Base58String) -> string
|
||||
bytes_to_b58(bs: []u8) -> Base58String
|
||||
bytes_from_b58(b: Base58String) -> []u8
|
||||
-- Applies SHA256 to the given string
|
||||
-- Argument: s - string to apply sha256 to (hash is applied to utf8 bytes of s)
|
||||
-- Returns: returns sha256 multihash encoded as base58
|
||||
sha256_string(s: string) -> Base58String
|
||||
|
||||
-- concatenate strings (in AIR it takes any number of arguments)
|
||||
concat_strings(a: string, b: string) -> string
|
||||
|
||||
service Peer("peer"):
|
||||
-- Checks if there is a direct connection to the peer identified by a given PeerId
|
||||
-- Argument: PeerId – id of the peer to check if there's a connection with
|
||||
-- Returns: bool - true if connected to the peer, false otherwise
|
||||
is_connected(peer: PeerId) -> bool
|
||||
|
||||
-- Initiates a connection to the specified peer
|
||||
-- Arguments:
|
||||
-- id - id of the target peer
|
||||
-- multiaddrs – an array of target peer's addresses
|
||||
-- Returns: bool - true if connection was successful
|
||||
connect(id: PeerId, multiaddrs: ?[]string) -> bool
|
||||
-- Resolves the contact of a peer via Kademlia
|
||||
-- Argument: PeerId – id of the target peer
|
||||
-- Returns: Contact - true if connection was successful
|
||||
get_contact(peer: PeerId) -> Contact
|
||||
|
||||
-- Get information about the peer
|
||||
identify() -> Info
|
||||
|
||||
-- Get Unix timestamp in milliseconds
|
||||
timestamp_ms() -> u64
|
||||
|
||||
-- Get Unix timestamp in seconds
|
||||
timestamp_sec() -> u64
|
||||
|
||||
service Kademlia("kad"):
|
||||
-- Instructs node to return the locally-known nodes
|
||||
-- in the Kademlia neighborhood for a given key
|
||||
-- Arguments:
|
||||
-- key – base58 string
|
||||
-- already_hashed – default false; if set to true, key is considered to be a SHA256 multihash
|
||||
-- count – default 20; limits number of returned nodes
|
||||
neighborhood(key: Base58String, already_hashed: ?bool, count: ?u32) -> []PeerId
|
||||
-- Merges given lists and sorts them by distance to target
|
||||
-- Arguments:
|
||||
-- target – base58 string; result is sorted by XOR distance to target
|
||||
-- left – list of base58 strings
|
||||
-- right – list of base58 strings
|
||||
-- count – how many items to return; default 20
|
||||
-- Returns: list of base58 strings sorted by distance to target; list will contain at most count elements
|
||||
merge(target: Base58String, left: []string, right: []string, count: ?u32) -> []string
|
||||
|
||||
service Srv("srv"):
|
||||
-- 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.
|
||||
create(blueprint_id: string) -> string
|
||||
|
||||
-- Used to remove a service from a certain node
|
||||
-- Arguments:
|
||||
-- service_id – ID of the service to remove
|
||||
remove(service_id: string)
|
||||
|
||||
-- Returns a list of services running on a peer
|
||||
list() -> []Service
|
||||
|
||||
-- Adds an alias on service, so, service could be called
|
||||
-- not only by service_id but by alias as well.
|
||||
-- Arguments:
|
||||
-- alias - settable service name
|
||||
-- service_id – ID of the service whose interface you want to name.
|
||||
add_alias(alias: string, service_id: string)
|
||||
|
||||
-- Resolves given alias to a service id
|
||||
-- If there's no such alias, throws an error
|
||||
-- Returns: service id associated with the given alias
|
||||
resolve_alias(alias: string) -> string
|
||||
|
||||
-- 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.
|
||||
get_interface(service_id: string) -> Interface
|
||||
|
||||
service Dist("dist"):
|
||||
-- Constructs a ModuleConfig structure
|
||||
-- Arguments:
|
||||
-- module_name - import name of the module
|
||||
-- max_heap_size - Maximum memory size accessible by a module in Wasm pages (64 Kb)
|
||||
-- logger_enabled - Defines whether Marine should provide a special host log_utf8_string function for this module
|
||||
-- preopened_files - Files available for this module. Module can access only files from this list
|
||||
-- envs - environment variables available for this module
|
||||
-- mapped_dirs - Directory mapping, e.g. [["/sites", "./web/data"]] so all
|
||||
-- reads & writes to /sites will actually to go ./web/data
|
||||
-- mounted_binaries - Mapping of host binaries available to call from module,
|
||||
-- e.g. [["curl", "/usr/bin/curl"]] will allow module to
|
||||
-- call /usr/bin/curl binary as function 'curl'
|
||||
-- logging_mask - Binary mask to enable & disable logging targets. Targets are
|
||||
-- configured in WasmLoggerBuilder::with_target_map
|
||||
-- max_heap_size - Maximum memory size accessible by a module in Wasm pages (64 Kb)
|
||||
make_module_config(name: string, max_heap_size: ?u32, logger_enabled: ?bool, preopened_files: ?[]string, envs: ?Pairs, mapped_dirs: ?Pairs, mounted_binaries: ?Pairs, logging_mask: ?i32) -> ModuleConfig
|
||||
|
||||
-- Constructs a ModuleConfig structure
|
||||
-- Arguments:
|
||||
-- module_name - import name of the module
|
||||
default_module_config(module_name: string) -> ModuleConfig
|
||||
|
||||
|
||||
-- Used to add modules to the node specified in the service call
|
||||
-- Arguments:
|
||||
-- bytes – a base64 string containing the .wasm module to add.
|
||||
-- config – module info
|
||||
-- Returns: blake3 hash of the module
|
||||
add_module(wasm_b56_content: Bytes, conf: ModuleConfig) -> string
|
||||
|
||||
-- Adds module by copying it from Particle Vault directory
|
||||
-- Arguments:
|
||||
-- path – path or a filename
|
||||
-- config - module config
|
||||
add_module_from_vault(path: string, config: ModuleConfig) -> Hash
|
||||
|
||||
-- Get a list of modules available on the node
|
||||
list_modules() -> []Module
|
||||
|
||||
-- Get the interface of a module
|
||||
get_interface(module_id: string) -> Interface
|
||||
|
||||
-- Creates Blueprint structure from from blueprint name and dependencies (modules)
|
||||
make_blueprint(name: string, dependencies: []Dependency) -> AddBlueprint
|
||||
-- Add a blueprint to the node
|
||||
add_blueprint(blueprint: AddBlueprint) -> string
|
||||
|
||||
-- Used to get the blueprints available on the node specified in the service call.
|
||||
-- A blueprint is an object of the following structure
|
||||
list_blueprints() -> []Blueprint
|
||||
|
||||
service Script("script"):
|
||||
-- Adds the given script to a node
|
||||
-- Arguments:
|
||||
-- air_script - raw AIR script without any undefined variables
|
||||
-- interval - if not set, script will be ran only once
|
||||
-- if set, script will be ran once in the interval
|
||||
-- (NOTE: actual interval may vary by up to 3 seconds)
|
||||
-- TODO: change interval to ?u64 when node API is updated
|
||||
add(air_script: string, interval: ?string) -> string
|
||||
|
||||
-- Removes recurring script from a node. Only a creator of the script can delete it
|
||||
remove(script_id: string) -> bool
|
||||
|
||||
-- Returns a list of existing scripts on the node.
|
||||
-- Each object in the list is of the following structure
|
||||
list() -> ScriptInfo
|
@ -1,37 +0,0 @@
|
||||
import "builtin.aqua"
|
||||
|
||||
service Op2("op"):
|
||||
identity(s: u64)
|
||||
array(a: string, b: u64) -> string
|
||||
|
||||
data Oracle:
|
||||
n: u32
|
||||
mode: u64
|
||||
freq: u32
|
||||
err_str: string
|
||||
raw_data: []u64
|
||||
|
||||
service TSOracle("service-id"):
|
||||
point_estimate: []u64, u32 -> Oracle
|
||||
|
||||
|
||||
func ts_getter(node: string) -> []u64:
|
||||
res: *u64
|
||||
on node:
|
||||
k <- Op.string_to_b58(node)
|
||||
nodes <- Kademlia.neighborhood(k, nil, nil)
|
||||
if Op.array_length(nodes) > 0:
|
||||
for n <- nodes par:
|
||||
on n:
|
||||
try:
|
||||
res <- Peer.timestamp_ms()
|
||||
join res[9]
|
||||
<- res
|
||||
|
||||
func ts_oracle(node: string, oracle_service_id: string, min_points:u32) -> Oracle:
|
||||
res <- ts_getter(node)
|
||||
on node:
|
||||
TSOracle oracle_service_id
|
||||
oracle <- TSOracle.point_estimate(res, min_points)
|
||||
<- oracle
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "ts_oracle",
|
||||
"mem_page_count": 5,
|
||||
"logger_enabled": false
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"ts-oracle": {
|
||||
"modules": [
|
||||
{
|
||||
"name": "ts_oracle",
|
||||
"path": "./artifacts/ts_oracle.wasm",
|
||||
"logger_enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
31
aqua-examples/ts-oracle/fluence.yaml
Normal file
31
aqua-examples/ts-oracle/fluence.yaml
Normal file
@ -0,0 +1,31 @@
|
||||
# yaml-language-server: $schema=.fluence/schemas/fluence.yaml.json
|
||||
|
||||
# EXAMPLES:
|
||||
# services:
|
||||
# someService: # Service name. It must start with a lowercase letter and contain only letters, numbers, and underscores.
|
||||
# get: https://github.com/fluencelabs/services/blob/master/adder.tar.gz?raw=true # URL or path
|
||||
# deploy:
|
||||
# - deployId: default # must start with a lowercase letter and contain only letters, numbers, and underscores.
|
||||
# # Used in aqua to access deployed service ids
|
||||
# # You can access deployment info in aqua like this:
|
||||
# # services <- App.services()
|
||||
# # on services.someService.default!.peerId:
|
||||
# distribution: even # Deploy strategy. Can also be 'random'. Default: 'even'
|
||||
# peerId: MY_PEER # Peer id or peer id name to deploy on. Default: Random peer id is selected for each deploy
|
||||
# peerIds: # Overrides peerId property. Can be used to deploy on multiple peers.
|
||||
# - 12D3KooWR4cv1a8tv7pps4HH6wePNaK6gf1Hww5wcCMzeWxyNw51
|
||||
# - MY_PEER
|
||||
# count: 1 # How many times to deploy. Default: 1 or if peerIds is provided - exactly the number of peerIds
|
||||
# # overrideModules: # Override modules from service.yaml
|
||||
# # facade:
|
||||
# # get: ./relative/path # Override facade module
|
||||
# peerIds: # A map of named peerIds. Optional.
|
||||
# MY_PEER: 12D3KooWCMr9mU894i8JXAFqpgoFtx6qnV1LFPSfVc3Y34N4h4LS
|
||||
# relays: kras # Array of relay multi-addresses or keywords: kras, testnet, stage. Default: kras
|
||||
|
||||
version: 1
|
||||
services:
|
||||
tsOracle:
|
||||
get: ./ts-oracle
|
||||
deploy:
|
||||
- deployId: default
|
@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit -o nounset -o pipefail
|
||||
|
||||
mkdir -p artifacts
|
||||
rm -f artifacts/*.wasm
|
||||
marine build --release
|
||||
cp target/wasm32-wasi/release/ts_oracle.wasm artifacts/
|
45
aqua-examples/ts-oracle/src/aqua/main.aqua
Normal file
45
aqua-examples/ts-oracle/src/aqua/main.aqua
Normal file
@ -0,0 +1,45 @@
|
||||
module Main
|
||||
|
||||
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||
import App from "deployed.app.aqua"
|
||||
|
||||
export App, ts_getter, ts_oracle
|
||||
|
||||
data Oracle:
|
||||
n: u32
|
||||
mode: u64
|
||||
freq: u32
|
||||
err_str: string
|
||||
raw_data: []u64
|
||||
|
||||
service TSOracle("service-id"):
|
||||
point_estimate: []u64, u32 -> Oracle
|
||||
|
||||
func ts_getter() -> []u64:
|
||||
rtt = 1000 -- millis
|
||||
msg = "timeout"
|
||||
res: *u64
|
||||
|
||||
services <- App.services()
|
||||
|
||||
on services.tsOracle.default!.peerId:
|
||||
k <- Op.string_to_b58(services.tsOracle.default!.peerId)
|
||||
nodes <- Kademlia.neighborhood(k, nil, nil)
|
||||
if Op.array_length(nodes) > 0:
|
||||
for n <- nodes par:
|
||||
on n:
|
||||
try:
|
||||
res <- Peer.timestamp_ms()
|
||||
-- whether we get res from all the nodes or rtt milliseconds passed, return res
|
||||
join res[Op.array_length(nodes) - 1]
|
||||
par Peer.timeout(rtt, msg)
|
||||
|
||||
<- res
|
||||
|
||||
func ts_oracle(min_points: u32) -> Oracle:
|
||||
services <- App.services()
|
||||
res <- ts_getter()
|
||||
on services.tsOracle.default!.peerId:
|
||||
TSOracle services.tsOracle.default!.serviceId
|
||||
oracle <- TSOracle.point_estimate(res, min_points)
|
||||
<- oracle
|
@ -0,0 +1,31 @@
|
||||
# yaml-language-server: $schema=../../../.fluence/schemas/module.yaml.json
|
||||
|
||||
# EXAMPLES:
|
||||
# name: facade
|
||||
# type: rust # use this for modules written in rust and expected to be built with marine
|
||||
# maxHeapSize: "100" # 100 bytes
|
||||
# # maxHeapSize: 100K # 100 kilobytes
|
||||
# # maxHeapSize: 100 Ki # 100 kibibytes
|
||||
# # Max size of the heap that a module can allocate in format: <number><whitespace?><specificator?>
|
||||
# # where ? is an optional field and specificator is one from the following (case-insensitive):
|
||||
# # K, Kb - kilobyte; Ki, KiB - kibibyte; M, Mb - megabyte; Mi, MiB - mebibyte; G, Gb - gigabyte; Gi, GiB - gibibyte;
|
||||
# # Current limit is 4 GiB
|
||||
# loggerEnabled: true # true, if it allows module to use the Marine SDK logger.
|
||||
# loggingMask: 0 # manages the logging targets, described in here: https://doc.fluence.dev/marine-book/marine-rust-sdk/developing/logging#using-target-map
|
||||
# mountedBinaries:
|
||||
# curl: /usr/bin/curl # a map of mounted binary executable files
|
||||
# preopenedFiles: # a list of files and directories that this module could access with WASI
|
||||
# - ./dir
|
||||
# volumes: # a map of accessible files and their aliases.
|
||||
# # Aliases should be normally used in Marine module development because it's hard to know the full path to a file.
|
||||
# aliasForSomePath: ./some/path
|
||||
# envs: # environment variables accessible by a particular module with standard Rust env API like this std::env::var(IPFS_ADDR_ENV_NAME).
|
||||
# # Please note that Marine adds three additional environment variables. Module environment variables could be examined with repl
|
||||
# ENV1: arg1
|
||||
# ENV2: arg2
|
||||
|
||||
version: 0
|
||||
type: rust
|
||||
name: ts_oracle
|
||||
loggerEnabled: true
|
||||
maxHeapSize: 320K
|
@ -93,7 +93,7 @@ mod tests {
|
||||
assert_eq!(freq, 3);
|
||||
}
|
||||
|
||||
#[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts")]
|
||||
#[marine_test(config_path = "../Config.toml", modules_dir = "../target/wasm32-wasi/release/")]
|
||||
fn test_point_estimate_good(ts_oracle: marine_test_env::ts_oracle::ModuleInterface) {
|
||||
let data = vec![1u64, 1u64, 3u64, 3u64, 3u64, 5u64];
|
||||
let min_points = 2u32;
|
||||
@ -102,7 +102,7 @@ mod tests {
|
||||
assert_eq!(res.freq, 3u32);
|
||||
}
|
||||
|
||||
#[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts")]
|
||||
#[marine_test(config_path = "../Config.toml", modules_dir = "../target/wasm32-wasi/release/")]
|
||||
fn test_point_estimate_bad(ts_oracle: marine_test_env::ts_oracle::ModuleInterface) {
|
||||
let data = vec![1u64, 1u64, 3u64, 3u64, 3u64, 5u64];
|
||||
let n = data.len();
|
||||
@ -117,7 +117,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[marine_test(config_path = "../Config.toml", modules_dir = "../artifacts")]
|
||||
#[marine_test(config_path = "../Config.toml", modules_dir = "../target/wasm32-wasi/release/")]
|
||||
fn test_point_estimate_bad_2(ts_oracle: marine_test_env::ts_oracle::ModuleInterface) {
|
||||
let data = vec![];
|
||||
let n = data.len();
|
34
aqua-examples/ts-oracle/ts-oracle/service.yaml
Normal file
34
aqua-examples/ts-oracle/ts-oracle/service.yaml
Normal file
@ -0,0 +1,34 @@
|
||||
# yaml-language-server: $schema=../.fluence/schemas/service.yaml.json
|
||||
|
||||
# EXAMPLES:
|
||||
# modules:
|
||||
# facade:
|
||||
# get: modules/facade
|
||||
#
|
||||
# # Overrides for module:
|
||||
# maxHeapSize: "100" # 100 bytes
|
||||
# # maxHeapSize: 100K # 100 kilobytes
|
||||
# # maxHeapSize: 100 Ki # 100 kibibytes
|
||||
# # Max size of the heap that a module can allocate in format: <number><whitespace?><specificator?>
|
||||
# # where ? is an optional field and specificator is one from the following (case-insensitive):
|
||||
# # K, Kb - kilobyte; Ki, KiB - kibibyte; M, Mb - megabyte; Mi, MiB - mebibyte; G, Gb - gigabyte; Gi, GiB - gibibyte;
|
||||
# # Current limit is 4 GiB
|
||||
# loggerEnabled: true # true, if it allows module to use the Marine SDK logger.
|
||||
# loggingMask: 0 # manages the logging targets, described in here: https://doc.fluence.dev/marine-book/marine-rust-sdk/developing/logging#using-target-map
|
||||
# mountedBinaries:
|
||||
# curl: /usr/bin/curl # a map of mounted binary executable files
|
||||
# preopenedFiles: # a list of files and directories that this module could access with WASI
|
||||
# - ./dir
|
||||
# volumes: # a map of accessible files and their aliases.
|
||||
# # Aliases should be normally used in Marine module development because it's hard to know the full path to a file.
|
||||
# aliasForSomePath: ./some/path
|
||||
# envs: # environment variables accessible by a particular module with standard Rust env API like this std::env::var(IPFS_ADDR_ENV_NAME).
|
||||
# # Please note that Marine adds three additional environment variables. Module environment variables could be examined with repl
|
||||
# ENV1: arg1
|
||||
# ENV2: arg2
|
||||
|
||||
version: 0
|
||||
name: tsOracle
|
||||
modules:
|
||||
facade:
|
||||
get: modules/ts-oracle
|
Loading…
Reference in New Issue
Block a user