From 10b9cad438104ff4c27b5ac4ec154fb1a1b17e63 Mon Sep 17 00:00:00 2001 From: boneyard93501 <4523011+boneyard93501@users.noreply.github.com> Date: Mon, 19 Jul 2021 12:08:38 -0500 Subject: [PATCH] Price oracle (#11) * update services --- .../get_crypto_prices.get_price.air | 61 +++++ .../get_crypto_prices.get_prices_par.air | 47 ++++ .../get_crypto_prices.get_prices_par_2.air | 39 ++++ .../get_crypto_prices.get_prices_seq.air | 47 ++++ .../price-oracle/aqua-scripts/builtin.aqua | 217 ++++++++++++++++++ .../aqua-scripts/get_crypto_prices.aqua | 49 ++++ .../price-oracle/configs/Config.toml | 19 ++ .../configs/curl_adapter_cfg.json | 8 + .../configs/mean_service_cfg.json | 5 + .../configs/price_getter_service_cfg.json | 5 + .../price-oracle/curl_adapter/Cargo.toml | 14 ++ .../price-oracle/curl_adapter/src/main.rs | 37 +++ .../price-oracle/data/deployed_services.json | 16 ++ .../price-oracle/mean_service/Cargo.toml | 22 ++ .../price-oracle/mean_service/src/main.rs | 53 +++++ .../price_getter_service/Cargo.toml | 26 +++ .../price_getter_service/src/main.rs | 95 ++++++++ aqua-examples/price-oracle/scripts/build.sh | 24 ++ 18 files changed, 784 insertions(+) create mode 100644 aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_price.air create mode 100644 aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_par.air create mode 100644 aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_par_2.air create mode 100644 aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_seq.air create mode 100644 aqua-examples/price-oracle/aqua-scripts/builtin.aqua create mode 100644 aqua-examples/price-oracle/aqua-scripts/get_crypto_prices.aqua create mode 100644 aqua-examples/price-oracle/configs/Config.toml create mode 100644 aqua-examples/price-oracle/configs/curl_adapter_cfg.json create mode 100644 aqua-examples/price-oracle/configs/mean_service_cfg.json create mode 100644 aqua-examples/price-oracle/configs/price_getter_service_cfg.json create mode 100644 aqua-examples/price-oracle/curl_adapter/Cargo.toml create mode 100644 aqua-examples/price-oracle/curl_adapter/src/main.rs create mode 100644 aqua-examples/price-oracle/data/deployed_services.json create mode 100644 aqua-examples/price-oracle/mean_service/Cargo.toml create mode 100644 aqua-examples/price-oracle/mean_service/src/main.rs create mode 100644 aqua-examples/price-oracle/price_getter_service/Cargo.toml create mode 100644 aqua-examples/price-oracle/price_getter_service/src/main.rs create mode 100755 aqua-examples/price-oracle/scripts/build.sh diff --git a/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_price.air b/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_price.air new file mode 100644 index 0000000..8857b16 --- /dev/null +++ b/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_price.air @@ -0,0 +1,61 @@ +(xor + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "coin") [] coin) + ) + (call %init_peer_id% ("getDataSrv" "currency") [] currency) + ) + (call %init_peer_id% ("getDataSrv" "node") [] node) + ) + (call %init_peer_id% ("getDataSrv" "pg_sid") [] pg_sid) + ) + (call %init_peer_id% ("getDataSrv" "mean_sid") [] mean_sid) + ) + (call -relay- ("op" "noop") []) + ) + (xor + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (call node ("op" "string_to_b58") [node] k) + (call node ("peer" "timestamp_ms") [] ts_ms0) + ) + (call node (pg_sid "price_getter") [coin currency ts_ms0] res0) + ) + (call node ("op" "identity") [res0.$.result!] $prices) + ) + (call node ("peer" "timestamp_ms") [] ts_ms1) + ) + (call node (pg_sid "price_getter") [coin currency ts_ms1] res1) + ) + (call node ("op" "identity") [res1.$.result!] $prices) + ) + (call node (mean_sid "mean") [$prices] result) + ) + (seq + (call -relay- ("op" "noop") []) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1]) + ) + ) + ) + (call -relay- ("op" "noop") []) + ) + (xor + (call %init_peer_id% ("callbackSrv" "response") [result]) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2]) + ) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3]) +) diff --git a/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_par.air b/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_par.air new file mode 100644 index 0000000..5185697 --- /dev/null +++ b/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_par.air @@ -0,0 +1,47 @@ +(xor + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "payload") [] payload) + ) + (call %init_peer_id% ("getDataSrv" "ns_pairs") [] ns_pairs) + ) + (fold ns_pairs p + (par + (seq + (seq + (call -relay- ("op" "noop") []) + (xor + (seq + (call p.$.node! ("op" "string_to_b58") [p.$.node!] k) + (fold payload d + (seq + (seq + (call p.$.node! ("peer" "timestamp_ms") [] ts_ms) + (call p.$.node! (p.$.service_id! "price_getter") [d.$.coin! d.$.currency! ts_ms] $prices) + ) + (next d) + ) + ) + ) + (seq + (call -relay- ("op" "noop") []) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1]) + ) + ) + ) + (call %init_peer_id% ("op" "noop") []) + ) + (next p) + ) + ) + ) + (xor + (call %init_peer_id% ("callbackSrv" "response") [$prices]) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2]) + ) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3]) +) diff --git a/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_par_2.air b/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_par_2.air new file mode 100644 index 0000000..a124f90 --- /dev/null +++ b/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_par_2.air @@ -0,0 +1,39 @@ +(xor + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "payload") [] payload) + ) + (fold payload p + (par + (seq + (seq + (call -relay- ("op" "noop") []) + (xor + (seq + (seq + (call p.$.node! ("op" "string_to_b58") [p.$.node!] k) + (call p.$.node! ("peer" "timestamp_ms") [] ts_ms) + ) + (call p.$.node! (p.$.service_id! "price_getter") [p.$.coin! p.$.currency! ts_ms] $prices) + ) + (seq + (call -relay- ("op" "noop") []) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1]) + ) + ) + ) + (call %init_peer_id% ("op" "noop") []) + ) + (next p) + ) + ) + ) + (xor + (call %init_peer_id% ("callbackSrv" "response") [$prices]) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2]) + ) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3]) +) diff --git a/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_seq.air b/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_seq.air new file mode 100644 index 0000000..c05841c --- /dev/null +++ b/aqua-examples/price-oracle/air-scripts/get_crypto_prices.get_prices_seq.air @@ -0,0 +1,47 @@ +(xor + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "payloads") [] payloads) + ) + (call %init_peer_id% ("getDataSrv" "ns_pairs") [] ns_pairs) + ) + (fold ns_pairs p + (seq + (seq + (seq + (call -relay- ("op" "noop") []) + (xor + (seq + (call p.$.node! ("op" "string_to_b58") [p.$.node!] k) + (fold payloads d + (seq + (seq + (call p.$.node! ("peer" "timestamp_ms") [] ts_ms) + (call p.$.node! (p.$.service_id! "price_getter") [d.$.coin! d.$.currency! ts_ms] $prices) + ) + (next d) + ) + ) + ) + (seq + (call -relay- ("op" "noop") []) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1]) + ) + ) + ) + (call -relay- ("op" "noop") []) + ) + (next p) + ) + ) + ) + (xor + (call %init_peer_id% ("callbackSrv" "response") [$prices]) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2]) + ) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3]) +) diff --git a/aqua-examples/price-oracle/aqua-scripts/builtin.aqua b/aqua-examples/price-oracle/aqua-scripts/builtin.aqua new file mode 100644 index 0000000..0bb0297 --- /dev/null +++ b/aqua-examples/price-oracle/aqua-scripts/builtin.aqua @@ -0,0 +1,217 @@ +-- Default public interface of Fluence nodes + +alias Field : []string +alias Argument : []string +alias Bytes : []u8 +alias PeerId : string +alias Pairs : [][]string +alias Base58String : 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 deteremenistic +-- 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 + +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, unlimited by default + -- 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 + -- mem_pages_count - 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 + -- mem_pages_count - Maximum memory size accessible by a module in Wasm pages (64 Kb) + make_module_config(name: string, mem_pages_count: ?u32, logger_enabled: ?bool, preopened_files: ?[]string, envs: ?Pairs, mapped_dirs: ?Pairs, mounted_binaries: ?Pairs, logging_mask: ?i32) -> 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 + + -- 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 \ No newline at end of file diff --git a/aqua-examples/price-oracle/aqua-scripts/get_crypto_prices.aqua b/aqua-examples/price-oracle/aqua-scripts/get_crypto_prices.aqua new file mode 100644 index 0000000..aeea4f9 --- /dev/null +++ b/aqua-examples/price-oracle/aqua-scripts/get_crypto_prices.aqua @@ -0,0 +1,49 @@ +-- import "@fluencelabs/aqua-lib/builtin.aqua" +import "builtin.aqua" + +data Result: + result: f64 + success: bool + error_msg: string + +service PriceGetterService: + price_getter(coin: string, currency: string, timestamp_ms: u64) -> Result + +service MeanService: + mean(data: []f64) -> Result + +service F64Op("op"): + identity(x:f64) -> f64 + +data Payload: + coin: string + currency: string + +data NodeServicePair: + node: string + service_id: string + +-- func get_price(coin: string, currency: string, node: string, service_id: string, logError:string, string -> ()) -> Result: +func get_price(coin: string, currency: string, node: string, pg_sid: string, mean_sid: string) -> Result: + prices: *f64 + on node: + k <- Op.string_to_b58(node) + + PriceGetterService pg_sid + MeanService mean_sid + + ts_ms0 <- Peer.timestamp_ms() + res0 <- PriceGetterService.price_getter(coin, currency, ts_ms0) + -- if price.success: + prices <- F64Op.identity(res0.result) + + ts_ms1 <- Peer.timestamp_ms() + res1 <- PriceGetterService.price_getter(coin, currency, ts_ms1) + -- if price.success: + prices <- F64Op.identity(res1.result) + + result <- MeanService.mean(prices) + -- else: + -- logError("price_getter: ", price.error_msg) + <- result + diff --git a/aqua-examples/price-oracle/configs/Config.toml b/aqua-examples/price-oracle/configs/Config.toml new file mode 100644 index 0000000..d4f7806 --- /dev/null +++ b/aqua-examples/price-oracle/configs/Config.toml @@ -0,0 +1,19 @@ +modules_dir = "artifacts/" + +[[module]] + name = "curl_adapter" + logger_enabled = false + mem_pages_count = 1 + + [module.mounted_binaries] + curl = "/usr/bin/curl" + +[[module]] + name = "price_getter_service" + logger_enabled = false + mem_pages_count = 1 + +[[module]] + name = "mean_service" + logger_enabled = false + mem_pages_count = 1 \ No newline at end of file diff --git a/aqua-examples/price-oracle/configs/curl_adapter_cfg.json b/aqua-examples/price-oracle/configs/curl_adapter_cfg.json new file mode 100644 index 0000000..6bdef17 --- /dev/null +++ b/aqua-examples/price-oracle/configs/curl_adapter_cfg.json @@ -0,0 +1,8 @@ +{ + "name": "curl_adapter", + "mountedBinaries": { + "curl": "/usr/bin/curl" + }, + "mem_page_count": 1, + "logger_enabled": false +} \ No newline at end of file diff --git a/aqua-examples/price-oracle/configs/mean_service_cfg.json b/aqua-examples/price-oracle/configs/mean_service_cfg.json new file mode 100644 index 0000000..827b226 --- /dev/null +++ b/aqua-examples/price-oracle/configs/mean_service_cfg.json @@ -0,0 +1,5 @@ +{ + "name": "mean_service", + "mem_page_count": 1, + "logger_enabled": false +} \ No newline at end of file diff --git a/aqua-examples/price-oracle/configs/price_getter_service_cfg.json b/aqua-examples/price-oracle/configs/price_getter_service_cfg.json new file mode 100644 index 0000000..1c82f8a --- /dev/null +++ b/aqua-examples/price-oracle/configs/price_getter_service_cfg.json @@ -0,0 +1,5 @@ +{ + "name": "price_getter_service", + "mem_page_count": 1, + "logger_enabled": false +} \ No newline at end of file diff --git a/aqua-examples/price-oracle/curl_adapter/Cargo.toml b/aqua-examples/price-oracle/curl_adapter/Cargo.toml new file mode 100644 index 0000000..8c20ed4 --- /dev/null +++ b/aqua-examples/price-oracle/curl_adapter/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "curl_adapter" +version = "0.1.0" +authors = ["Fluence Labs"] +edition = "2018" +publish = false + +[[bin]] +path = "src/main.rs" +name = "curl_adapter" + +[dependencies] +marine-rs-sdk = "0.6.11" +log = "0.4.8" diff --git a/aqua-examples/price-oracle/curl_adapter/src/main.rs b/aqua-examples/price-oracle/curl_adapter/src/main.rs new file mode 100644 index 0000000..861cbf8 --- /dev/null +++ b/aqua-examples/price-oracle/curl_adapter/src/main.rs @@ -0,0 +1,37 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#![allow(improper_ctypes)] + +use marine_rs_sdk::marine; +use marine_rs_sdk::module_manifest; + +use marine_rs_sdk::MountedBinaryResult; + +module_manifest!(); + +pub fn main() {} + +#[marine] +pub fn curl_request(cmd: Vec) -> MountedBinaryResult { + curl(cmd) +} + +#[marine] +#[link(wasm_import_module = "host")] +extern "C" { + fn curl(cmd: Vec) -> MountedBinaryResult; +} diff --git a/aqua-examples/price-oracle/data/deployed_services.json b/aqua-examples/price-oracle/data/deployed_services.json new file mode 100644 index 0000000..a2ff24c --- /dev/null +++ b/aqua-examples/price-oracle/data/deployed_services.json @@ -0,0 +1,16 @@ +[ + { + "service": "price_getter", + "deployed": [ + "12D3KooWCMr9mU894i8JXAFqpgoFtx6qnV1LFPSfVc3Y34N4h4LS", + "c315073d-4311-4db3-be57-8f154f032d28" + ] + }, + { + "service": "mean", + "deployed": [ + "12D3KooWCMr9mU894i8JXAFqpgoFtx6qnV1LFPSfVc3Y34N4h4LS", + "dd47389f-25d9-4870-a2a9-909359e73580" + ] + } +] \ No newline at end of file diff --git a/aqua-examples/price-oracle/mean_service/Cargo.toml b/aqua-examples/price-oracle/mean_service/Cargo.toml new file mode 100644 index 0000000..0a203ae --- /dev/null +++ b/aqua-examples/price-oracle/mean_service/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "price_getter_service" +version = "0.1.0" +authors = ["boneyard93501 <4523011+boneyard93501@users.noreply.github.com>"] +edition = "2018" +description = "price-getter-service, a Marine wasi module" +license = "Apache-2.0" + +[[bin]] +name = "mean_service" +path = "src/main.rs" + +[dependencies] +marine-rs-sdk = "0.6.11" +log = "0.4.14" + +[dev-dependencies] +marine-rs-sdk-test = "0.1.11" + +[dev] +[profile.release] +opt-level = "s" diff --git a/aqua-examples/price-oracle/mean_service/src/main.rs b/aqua-examples/price-oracle/mean_service/src/main.rs new file mode 100644 index 0000000..8588520 --- /dev/null +++ b/aqua-examples/price-oracle/mean_service/src/main.rs @@ -0,0 +1,53 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use marine_rs_sdk::{marine, module_manifest}; + +module_manifest!(); + +fn main() {} + +#[marine] +pub struct Result { + pub result: f64, + pub success: bool, + pub error_msg: String, +} + +#[marine] +pub fn mean(data: Vec) -> Result { + match calc_mean(data.iter()) { + Some(res) => Result { + result: res, + success: true, + error_msg: "".to_string(), + }, + None => Result { + result: -1f64, + success: false, + error_msg: "Failure to calculate mean. Check your inputs.".to_string(), + }, + } +} + +fn calc_mean<'a>(data: impl ExactSizeIterator) -> Option { + let n = data.len() as u64; + if n < 1 { + return None; + } + let res = (data.sum::() / n as f64) as f64; + let res = format!("{:.2}", res).parse::().unwrap(); + Some(res) +} diff --git a/aqua-examples/price-oracle/price_getter_service/Cargo.toml b/aqua-examples/price-oracle/price_getter_service/Cargo.toml new file mode 100644 index 0000000..da1328f --- /dev/null +++ b/aqua-examples/price-oracle/price_getter_service/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "price_getter_service" +version = "0.1.0" +authors = ["boneyard93501 <4523011+boneyard93501@users.noreply.github.com>"] +edition = "2018" +description = "price-getter-service, a Marine wasi module" +license = "Apache-2.0" + +[[bin]] +name = "price_getter_service" +path = "src/main.rs" + +[dependencies] +marine-rs-sdk = "0.6.11" +log = "0.4.14" +picorand = "0.1.1" +fstrings = "0.2.3" +hex = "0.4.3" +serde_json = "1.0.57" + +[dev-dependencies] +marine-rs-sdk-test = "0.1.11" + +[dev] +[profile.release] +opt-level = "s" diff --git a/aqua-examples/price-oracle/price_getter_service/src/main.rs b/aqua-examples/price-oracle/price_getter_service/src/main.rs new file mode 100644 index 0000000..7fb37e7 --- /dev/null +++ b/aqua-examples/price-oracle/price_getter_service/src/main.rs @@ -0,0 +1,95 @@ +/* + * Copyright 2021 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use hex; +use marine_rs_sdk::{marine, module_manifest, MountedBinaryResult}; +use picorand::{PicoRandGenerate, WyRand, RNG}; +use serde_json; + +#[macro_use] +extern crate fstrings; + +module_manifest!(); + +pub fn main() {} + +#[marine] +pub struct Result { + pub result: f64, + pub success: bool, + pub error_msg: String, +} + +#[marine] +pub fn price_getter(coin: String, currency: String, timestamp_ms: u64) -> Result { + let url = + f!("https://api.coingecko.com/api/v3/simple/price?ids={coin}&vs_currencies={currency}"); + let curl_cmd = vec![ + "-X".to_string(), + "GET".to_string(), + "-H".to_string(), + "Accept: application/json".to_string(), + url, + ]; + let response = curl_request(curl_cmd); + let result = String::from_utf8(response.stdout); + + match result { + Ok(res) => { + let json_res = serde_json::from_str(&res.clone()); + if json_res.is_err() { + return Result { + result: -1f64, + success: false, + error_msg: "Failure to complete call".to_string(), + }; + } + let json_res: serde_json::Value = json_res.unwrap(); + let value = json_res[coin.to_lowercase()][currency.to_lowercase()].as_f64(); + if value.is_none() { + return Result { + result: -1f64, + success: false, + error_msg: + "No price value from source available. Check your coin and currency values." + .to_string(), + }; + } + + let value: f64 = value.unwrap(); + + let mut rng = RNG::::new(timestamp_ms); + let multiplier = rng.generate_range(5, 20) as f64; + let rnd_value = value * (1f64 + multiplier / 100f64); + Result { + result: format!("{:.2}", rnd_value).parse::().unwrap(), + success: true, + error_msg: "".to_string(), + } + } + Err(_) => Result { + result: -1f64, + success: false, + error_msg: String::from_utf8(response.stderr).unwrap(), + }, + } +} + +#[marine] +#[link(wasm_import_module = "curl_adapter")] +extern "C" { + pub fn curl_request(cmd: Vec) -> MountedBinaryResult; +} diff --git a/aqua-examples/price-oracle/scripts/build.sh b/aqua-examples/price-oracle/scripts/build.sh new file mode 100755 index 0000000..5ca0f1a --- /dev/null +++ b/aqua-examples/price-oracle/scripts/build.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash -o errexit -o nounset -o pipefail + +mkdir -p artifacts + +cd curl_adapter +cargo update --aggressive +marine build --release +cd .. + +cd price_getter_service +cargo update --aggressive +marine build --release +cd .. + +cd mean_service +cargo update --aggressive +marine build --release +cd .. + +rm -f artifacts/*.wasm + +cp curl_adapter/target/wasm32-wasi/release/curl_adapter.wasm artifacts/ +cp price_getter_service/target/wasm32-wasi/release/price_getter_service.wasm artifacts/ +cp mean_service/target/wasm32-wasi/release/mean_service.wasm artifacts/ \ No newline at end of file