Changes to reflect using fluence cli:

+ changed project structure;
+ added fluence cli related files;
+ updated aqua code;
+ updated readme.
This commit is contained in:
igor 2022-08-24 17:40:45 +02:00
parent d90315d0c8
commit b8a700f745
15 changed files with 348 additions and 385 deletions

7
aqua-examples/ts-oracle/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.idea
.DS_Store
.fluence
**/node_modules
**/target/
.vscode/settings.json
.repl_history

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -1,5 +0,0 @@
{
"name": "ts_oracle",
"mem_page_count": 5,
"logger_enabled": false
}

View File

@ -1,11 +0,0 @@
{
"ts-oracle": {
"modules": [
{
"name": "ts_oracle",
"path": "./artifacts/ts_oracle.wasm",
"logger_enabled": true
}
]
}
}

View 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

View File

@ -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/

View 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

View File

@ -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

View File

@ -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();

View 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