diff --git a/aqua-examples/aqua-ceramic-integration/aqua/ceramic_demo.aqua b/aqua-examples/aqua-ceramic-integration/aqua/ceramic_demo.aqua index 05c4645..d2c4d88 100644 --- a/aqua-examples/aqua-ceramic-integration/aqua/ceramic_demo.aqua +++ b/aqua-examples/aqua-ceramic-integration/aqua/ceramic_demo.aqua @@ -11,6 +11,8 @@ service CeramicAdapter("service-id"): state(stream_id: string) -> CeramicResult update(stream_id: string, payload: string) -> CeramicResult + http_streams(url:string, port: u32, stream_id:string) -> string + -- aqua function to create stream and return stream id func create(payload:string, node:string, service_id:string) -> string: on node: @@ -35,3 +37,8 @@ func roundtrip(payload:string, payload_two: string, node:string, service_id:stri <- create_res.stdout, show_res.stdout, update_res.stdout --< return the stream id, the show result, show result afer update +func http_show_stream(stream_id: string, node: string, service_id:string) -> string: + on node: + CeramicAdapter service_id + res <- CeramicAdapter.http_streams("127.0.0.1", 7007, stream_id) + <- res \ No newline at end of file diff --git a/aqua-examples/near-integration/README.md b/aqua-examples/near-integration/README.md new file mode 100644 index 0000000..e89c9b6 --- /dev/null +++ b/aqua-examples/near-integration/README.md @@ -0,0 +1,420 @@ +# NEAR + Fluence + Aqua Integrations + +**WIP: Tread With Care** + +## Overview + +We provide integration examples for both a [Fluence JS](https://github.com/fluencelabs/fluence-js) node based on the [NEAR API JS](https://docs.near.org/docs/api/javascript-library) and distributed [Wasm services](https://github.com/fluencelabs/marine) wrapping the [NEAR RPC API](https://docs.near.org/docs/api/rpc). A [NEAR CLI](https://docs.near.org/docs/tools/near-cli) integration is planned for the near future. + +## Fluence JS NEAR Signing Peer + +Signing transactions and messages is a critical operation both on- and off-chain and an integral part of most Web3 workflows. In Fluence's open, permissionless peer-to-peer network maintaining data privacy is a challenge. For example, passing the password for a keyfile or the private key itself is quite risky: while a peer-to-peer communication channel is end-to-end encrypted, the "end" of the channel is the node hosting the target service. Hence, a node can easily eavesdrop on decrypted traffic and abscond with your password or key and presumably, your funds. Of course, you can run your own node to eliminate such exploits. Rather than run a full-fledged Rust node for a limited scope requirement, a more advantageous solution might be to implement a Fluence Signing Node (FSN) with Node JS and Fluence JS, which is exactly what we have done for this example. While a Fluence JS peer does not allow for the hosting of arbitrary services at this point, it does allow to easily wrap the NEAR JS SDK and expose whatever interfaces you want to be used/composed with Aqua. + + +### Implementing a Peer Node With Fluence JS and Aqua + +As discussed in the [documentation](https://doc.fluence.dev/docs/fluence-js/5_run_in_node), we can use [Fluence JS](https://github.com/fluencelabs/fluence-js) in a Node JS application resulting in a viable peer node of the Fluence p2p network. If you haven't, have a look at the documentation before you continue. To follow along the code below: + +```bash +cd near-signing-node +``` + +In order to create our signing node, we wrap the [NEAR JS SDK](https://docs.near.org/docs/api/javascript-library) and, for a minimally viable experiment, expose the [sendMoney](https://near.github.io/near-api-js/classes/account.account-1.html#sendmoney) and a couple non-signing functions. + +In order to be able to expose `sendMoney` as an addressable service to Aqua, we need to implement the `sendMoney` interface and function in Aqua: + +```aqua +-- near_signing_node.aqua +func send_money(network_id:string, account_id:string, receiver_id:string, amount:string, password: string, node:string, relay:string) -> string: + on node via relay: + res <- NearSignerApi.send_money(network_id, account_id, receiver_id, amount) + <- res +``` + +Note that we added additional parameters to our `sendMoney` implementation: *near-api-js* specifies the `sendMoney` function with two parameters -- the receiver id and amount. Since `sendMoney` is associated with `Account`, we need to add the `from` wallet address as well as the `network_id` to be able to activate the appropriate account on the desired NEAR network. In addition, our exposed `sendMoney` service runs on a peer-to-peer node and in order to be able to locate and execute the service, we need to provide the node's `peer id` and `relay id`. Finally, we guard our service with stylized authentication for which we use the `password` parameter. That is, on the peer , which we reach via the relay and assuming the checks out, we eventually execute: + +```typescript +sendMoney(receiverId: string, amount: BN) +``` + +Once we compile Aqua with the `npm run compile aqua` command, which writes the Typescript output into the `/src/_aqua` dir, we can then use the generated code, see `src/_aqua/near_signing_node.ts`, to implement our `sendMoney` service and any of ther other interfaces specified in Fluence JS, which essentially follows the [NEAR example](https://docs.near.org/docs/api/naj-quick-reference#send-tokens): + +```typescript +// index.ts +async send_money(network_id: string, account_id: string, receiver_id: string, amount: string, password: string): Promise { + if (!this.password_checker(password)) { + return Promise.resolve("Not Authorized") + } + const config = get_config(network_id, this._keyStore); + const near = await network_connect(config); + let account = await near.account(account_id); + let tx_receipt = await account.sendMoney(receiver_id, amount) + // return Promise.resolve(tx_receipt); + let result = Promise.resolve(tx_receipt); + return result; + } +``` + + +Just like in the **quickstart example**, we implement the `send_money` method for the `class NearSigner implements NearSignerApiDef` class, where `NearSignerApiDef` is generated code from the Aqua compilation and which we register (as an exposed service) in `async main` like so: + +```typescript +// index.ts +async function main() { + // ... + registerNearSignerApi("near", new NearSigner()); + // ... +``` + +For the complete implementation details, see `src/index.ts`. Before we test our code, please note that in this implementation the wallet credentials are presumed to be in the `~/.near-credentials` directory of the machine/system that runs the Fluence Signing Node. For *testnet* wallets, see https://wallet.testnet.near.org/ and https://docs.near.org/docs/develop/basics/create-account, to get started. + +Note the implementations of `account_state` and `get_balance`, which follow the same implementation pattern discussed above but actually do not require account or wallet access. + +### Running And Interacting With The Fluence Peer + +Open two terminal windows in the `~/near-examples/near-signing-node/` directory to launch the peer and a client peer, respectively. Please note that you can use the peer with a local Fluence node or the testnet. For our purposes, we will be using Fluence's `krasnodar` testnet. + +Install the dependencies with: + +```bash +# setup the node +npm i +``` + +Then compile Aqua: + +```bash +# compile aqua +npm run compile-aqua +``` + +Which produces output similar to: + +```bash +> near-signing-node@0.1.0 compile-aqua +> aqua -i aqua/ -o src/_aqua + +2021.12.14 00:22:34 [INFO] Aqua Compiler 0.5.0-SNAPSHOT +2021.12.14 00:22:34 [INFO] Result /Users/bebo/localdev/examples/aqua-examples/near-integration/near-signing-node/src/_aqua/near_signing_node.ts: compilation OK (3 functions, 1 services) +2021.12.14 00:22:34 [INFO] Result /Users/bebo/localdev/examples/aqua-examples/near-integration/near-signing-node/src/_aqua/near_demo.ts: compilation OK (2 functions, 1 services) +``` + +You can check the generated Typescript and AIR code in the `src/_aqua` directory. With our setup complete, let's start the peer: + +```bash +# start the node +npm start +```` + +Which produces output similar to: + +```bash +> near-signing-node@0.1.0 start +> node -r ts-node/register src/index.ts + +PeerId: 12D3KooWLCaFtoq9uu1jFg38uZXU4fNAkcL5X3xoQu6UqFxRFavU +Relay id: 12D3KooWFEwNWcHqi9rtsmDhsYcDbRUCDXH84RC4FW6UfsFWaoHi +ctrl-c to exit +``` + +Please take note of the **relay id** and **peer id** for use in your client peer. In order to call the `account_state` method, open a new terminal window and navigate to the `~/examples/aqua-examples/near-integration/near-signing-node` directory and execute: + +```bash +aqua run \ + -i aqua -a "/dns4/kras-04.fluence.dev/tcp/19001/wss/p2p/12D3KooWFEwNWcHqi9rtsmDhsYcDbRUCDXH84RC4FW6UfsFWaoHi" \ + -f 'account_state("testnet", "", "lame_password", "", "relay id")' +``` + +*Replace* `` with your testnet account and `` and `` with the values provided by your peer output as discussed above. Once you've done that, the output should be similar to: + +```bash +Your peerId: 12D3KooWJYGtESBvtLiCyY1XUrXR5WYBtfAU697SqVnwi5XmqkqW +[ + { + "amount": "199998727964846286399080000", + "block_hash": "5Ves5ocsxmUSbsh6ZF5wutLiZSsPgm43H4H4zVDkacnA", + "block_height": 74936860, + "code_hash": "11111111111111111111111111111111", + "locked": "0", + "storage_paid_at": 0, + "storage_usage": 674 + } +] +``` + +Similarly, we can call our `send_money` service with Aqua: + +```aqua + aqua run \ + -i aqua \ + -a "/dns4/kras-04.fluence.dev/tcp/19001/wss/p2p/12D3KooWFEwNWcHqi9rtsmDhsYcDbRUCDXH84RC4FW6UfsFWaoHi" \ + -f 'send_money("testnet", "", "", "10000", "lame_password", "", "")' +``` + +Replace the <`from`> and <`to`> account placeholders with your respective testnet wallets and the `peer id` and `relay id` with the values provided by your peer. Executing above Aqua statement produces a transaction receipt similar to the one below: + +```bash + +Your peerId: 12D3KooWG8yhYea2x8LiWWruLqRyrbHNAyq6oAj5jkjDLZe21YK1 +[ + { + "receipts_outcome": [ + { + "block_hash": "7ZgHzMZsSirCxmJJ9tJJuHGjZBs1eG2tFWpYMBjYn9X1", + "id": "7viub5UekSHHYi8ovEt5TuQNgtqZSgPx6EgUutv5i561", + "outcome": { + "executor_id": "", + "gas_burnt": 223182562500, + "logs": [], + "metadata": { + "gas_profile": [], + "version": 1 + }, + "receipt_ids": [ + "E5ajp367DHvZcitj6eZ9nCE6rSYUmZi2njg9LnkdbZvM" + ], + "status": { + "SuccessValue": "" + }, + "tokens_burnt": "22318256250000000000" + }, + "proof": [ + { + "direction": "Right", + "hash": "AD7yhCufivqSZHRuTuqATFjjhkY9AiBHPGSqLvmADKJh" + }, + { + "direction": "Right", + "hash": "3fiJCKaok6BVoKMrgBHTedJWbeNU5nNJzpN82Kxjyw94" + } + ] + }, + { + "block_hash": "14EVqK6jpkKbkozEbdf5WrpxGAqzfq78kqj7Gv9smLBs", + "id": "E5ajp367DHvZcitj6eZ9nCE6rSYUmZi2njg9LnkdbZvM", + "outcome": { + "executor_id": "", + "gas_burnt": 223182562500, + "logs": [], + "metadata": { + "gas_profile": [], + "version": 1 + }, + "receipt_ids": [], + "status": { + "SuccessValue": "" + }, + "tokens_burnt": "0" + }, + "proof": [ + { + "direction": "Left", + "hash": "7cnaRc9PjGPHXd3fLngDQy8XQhmKtQXTPdrbq8Aas2Ti" + } + ] + } + ], + "status": { + "SuccessValue": "" + }, + "transaction": { + "actions": [ + { + "Transfer": { + "deposit": "10000" + } + } + ], + "hash": "3bmedi7erFPpwEWWgQHuMppoorvzed8x7w5mttofCVQw", + "nonce": 74253069000003, + "public_key": "ed25519:632DzcF3w7SLXJzDRcFdSHUBY2De8LECfJ9kCC4yERuj", + "receiver_id": "", + "signature": "ed25519:5zmMLczayMnnykBdn9MM3hUCWigZM5JgfAyT1E7mu7a4RNzPq9uahAKiGs1JWxNCor6zGHNbX8cpQYC59axxFtjR", + "signer_id": "" + }, + "transaction_outcome": { + "block_hash": "J4gEefXV6FM1crRxxQbHhhVWpuPdPHnEAx5Kb5coCDdj", + "id": "3bmedi7erFPpwEWWgQHuMppoorvzed8x7w5mttofCVQw", + "outcome": { + "executor_id": "", + "gas_burnt": 223182562500, + "logs": [], + "metadata": { + "gas_profile": null, + "version": 1 + }, + "receipt_ids": [ + "7viub5UekSHHYi8ovEt5TuQNgtqZSgPx6EgUutv5i561" + ], + "status": { + "SuccessReceiptId": "7viub5UekSHHYi8ovEt5TuQNgtqZSgPx6EgUutv5i561" + }, + "tokens_burnt": "22318256250000000000" + }, + "proof": [] + } + } +] +``` + +You can use the [Testnet Explorer](https://explorer.near.org/) to further investigate the money transfer you just executed. + +### Summary + +In this section, we implemented a basic Fluence peer that outlines an approach to shield our NEAR wallet keys from other network participants and to implement singing related functionality, such as the `send_money` token transfer method. Additional methods, such as the more generic `sign transaction` and `deploy contract` can be easily implemented this way and we are looking forward to your pull requests. Also note, that our simple string return can be vastly improved by adding the appropriate *interfaces*. + +In the next section, we briefly discuss how a variety of NEAR methods can be implemented as distributed, hosted services for easy deployment, re-use and scalability. + +## Fluence Wasm NEAR Services + +Operating your own node may not always be desireable for a variety of reasons ranging from costs to reuse to scalability and failover requirements. A core feature of the Fluence peer-to-peer network paradigm, of course, is the deployment of Wasm services to essentially any peer, given some hosting agreement, which allows for high portability as well as easy reuse and scalability as a "deploy and forget", low cost solution. Even if the operation of a node is deemed necessary, as outlined in our Signing Node example above, it still may make sense to split services into a self-operated peer for signing-related activities and some hosted Wasm services otherwise. Of course, Aqua allows you to seamlessly compose any one of the (exposed) services regardless of the deployment approach. + +In order to create a NEAR Wasm adapter, we wrap whatever functionality we need from the [NEAR RPC API](https://docs.near.org/docs/api/rpc) in our Wasm module(s). + +### Creating And Deploying NEAR Wasm Services + +In the `services` directory, you find a minimal Wasm adapter for [NEAR RPC API](https://docs.near.org/docs/api/rpc) to get you started. Since we are connecting to on-chain resources via JSON-RPC, we need our service module to have access to [cUrl](https://doc.fluence.dev/docs/tutorials_tutorials/curl-as-a-service), which we provide with the [cUrl adapter](../near-integration/services/curl-adapter/): + +```rust +// src/main.rs +#[marine] +#[link(wasm_import_module = "curl_adapter")] +extern "C" { + pub fn curl_request(cmd: Vec) -> MountedBinaryResult; +} +``` + +Let's have a look at the implementation of the [`network status`](https://docs.near.org/docs/api/rpc/network#node-status) method, which provides a fairly extensive snapshot of the network at the time in inquiry. Our adapter, or wrapper, implementation needs to envelope the RPC [`status`](https://docs.near.org/docs/api/rpc/network#node-status) endpoint and requires only one parameter: the `network_id`', e.g., `testnet`: + +```rust +// src.main.rs +// +#[marine] +pub struct Result { + pub stderr: String, + pub stdout: String, +} + +#[marine] +pub fn node_status(network_id: String) -> Result { + let method = "status".to_string(); + let url = url_maker(network_id); + let params = "[]".to_string(); + let curl_params: Vec = rpc_maker(url, method, params); + let response = curl_request(curl_params); + Result { + stderr: String::from_utf8(response.stderr).unwrap(), + stdout: String::from_utf8(response.stdout).unwrap(), + } +} +``` + +Note that we use the `Result` struct to capture the curl response. + +Assuming you compiled the code with `./scripts/build.sh`, we can interact with the `node_status` in `mrepl`. Open the REPL with `mrepl configs/Config.toml` and: + +```bash +mrepl configs/Config.toml +Welcome to the Marine REPL (version 0.9.1) +Minimal supported versions + sdk: 0.6.0 + interface-types: 0.20.0 + +app service was created with service id = e9ecced7-521c-4dd7-b205-61c8da3be0da +elapsed time 91.995618ms + +1> call near_rpc_services node_status ["testnet"] +result: Object({"stderr": String(" % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\r100 68 0 0 100 68 0 110 --:--:-- --:--:-- --:--:-- 110\r100 5384 100 5316 100 68 8091 103 --:--:-- --:--:-- --:--:-- 8182\n"), "stdout": String("{\"jsonrpc\":\"2.0\",\"result\":{\"version\":{\"version\":\"1.23.0-rc.1\",\"build\":\"crates-0.10.0-70-g93e8521c9\"},\"chain_id\":\"testnet\",\"protocol_version\":49,\"latest_protocol_version\":49, + +\"latest_state_root\":\"CfxNRB5SNAiCmsMLbyAi7LD6YRVak7BH8REbumeg5GvD\",\"latest_block_time\":\"2021-12-08T09:20:24.293798434Z\",\"syncing\":false,\"earliest_block_hash\":\"CTBCW2Xm1xHeVKs3R5ZSULmVPc8Gj5tVVmH6HDrwRAeF\",\"earliest_block_height\":74004638,\"earliest_block_time\":\"2021-12-06T08:48:52.331327624Z\"},\"validator_account_id\":null},\"id\":\"dontcare\"}")}) + elapsed time: 666.350943ms + +2> +... +``` + +As you can see, this is a straight mapping of the RPC response to the `Result` struct introduced above, which we can process in Aqua like so: + +```aqua +-- some example aqua file +data Result: + stderr: string + stdout: string + +service RPCService: + node_status(network_id: string, block_ref:string) -> Result + +func rpc_foo(network_id: string, block_ref:string, node_string, service_id: string ) -> string: + on node: + RPCService service_id + res <- RPCService.node_status(network_id, block_ref) + if res.stderr: + result <<- "call failed" + else: + result <<- res.stdout + <- result +``` + +Before we can use our Fluence NEAR adapter, we need to deploy our Wasm modules to one or more host peers. We can do that with Aqua or the [`fldist`](https://doc.fluence.dev/docs/knowledge_tools#fluence-proto-distributor-fldist) tool: + +```bash +fldist new_service \ + --ms artifacts/curl_adapter.wasm:configs/curl_adapter_cfg.json artifacts/near_rpc_services.wasm:configs/near_rpc_services_cfg.json \ + --name near-adapter \ + --verbose +``` + +Which gives us the deployment information: + +```bash +client seed: HuunejtheCeJueet52wqHcSCQqzYjRD1G9hyT8RiZZZ7 +client peerId: 12D3KooWAAsBJkGzSQ56XZpTsi9pDCqHDjF88AnwNuNYhhtyJpPR +relay peerId: 12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf +service id: 23ba9159-388d-41b3-b15f-2a6d5013ed4d +service created successfully +``` + +Please note the node id, "12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf", and service id "23ba9159-388d-41b3-b15f-2a6d5013ed4d" for future use in our Aqua. Let's have a look at our aqua script in 'aqua/near_adapter_demo.aqua`: + +```aqua +-- aqua/near_adapter_demo_aqua +func node_status(network_id: string, node: string, service_id: string) -> Result: + on node: + NearRpcServices service_id + res <- NearRpcServices.node_status(network_id) + <- res +``` + +Which we can run with the `aqua cli`: + +```bash +aqua run \ + -i aqua/near_adapter_demo.aqua \ + -a /dns4/kras-02.fluence.dev/tcp/19001/wss/p2p/12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf \ + -f 'node_status("testnet", "12D3KooWHLxVhUQyAuZe6AHMB29P7wkvTNMn7eDMcsqimJYLKREf", "23ba9159-388d-41b3-b15f-2a6d5013ed4d")' +``` + +Which results in the following output: + +```bash +Your peerId: 12D3KooWQ9KDy48aFG3jcrAAofUdgGa3tEUxkdEjpKiwL7Tp7Uiv +[ + { + "stderr": " % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\r100 7548 100 7480 100 68 192k 1789 --:--:-- --:--:-- --:--:-- 193k\n", + "stdout": "{\"jsonrpc\":\"2.0\",\"result\":{\"version\":{\"version\":\"1.23.0-rc.1\",\"build\":\"crates-0.10.0-70-g93e8521c9\"},\"chain_id\":\"testnet\",\"protocol_version\":49,\"latest_protocol_version\":49,\"rpc_addr\":\"0.0.0.0:4040\",\"validators\":[{\"account_id\":\"node1\",\"is_slashed\":false},{\"account_id\":\"node0\",\"is_slashed\":false},{\"account_id\":\"node2\", + \"is_slashed\":false}],\"sync_info\":{\"latest_block_hash\":\"HBCEesmK5W1K8FtnasswhZAKQ1qWxARjhPcPEbc4EtQq\", + + + + \"latest_block_height\":74946255,\"latest_state_root\":\"21RTTc1u7Uvdw7KPWuSvKkEPxgBCBtj9ddhaW16HTHpD\",\"latest_block_time\":\"2021-12-14T08:26:00.797528669Z\",\"syncing\":false,\"earliest_block_hash\":\"Gtfa893yb1vmWJSPHYMmJ4JsGRjPr4JBEMLZ8CMyLMtK\",\"earliest_block_height\":74691571,\"earliest_block_time\":\"2021-12-12T07:04:26.150892426Z\"},\"validator_account_id\":null},\"id\":\"dontcare\"}" + } +] +``` + +Give the already implemented `gas_price` and `tx_status` functions a try or add more methods from the RPC API. We are looking forward to your pull requests. + +### Summary + +We created a framework to enable highly portable Wasm modules as an adapter to NEAR's JSON-RPC framework, which may be distributed as hosted services to Rust peer nodes. These services may be used on their own or in conjunction with a specialized peer node, see above, taking care of signing tasks while shielding the wallet keys from preying eyes. Regardless the implementation route taken, Aqua allows us to seamlessly compose and reuse our our services. + + diff --git a/aqua-examples/near-integration/near-signing-node/.gitignore b/aqua-examples/near-integration/near-signing-node/.gitignore new file mode 100644 index 0000000..0a9f66f --- /dev/null +++ b/aqua-examples/near-integration/near-signing-node/.gitignore @@ -0,0 +1,46 @@ +lib-cov +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.gz +*.swp + +pids +logs +results +tmp + +# Build +public/css/main.css + +# Coverage reports +coverage + +# API keys and secrets +.env + +# Dependency directory +node_modules +bower_components + +# Editors +.idea +*.iml + +# OS metadata +.DS_Store +Thumbs.db + +# Ignore built ts files +dist/**/* + +# ignore yarn.lock +yarn.lock + +package-lock.json + +.bak +.bk \ No newline at end of file diff --git a/aqua-examples/near-integration/near-signing-node/aqua/near_signing_node.aqua b/aqua-examples/near-integration/near-signing-node/aqua/near_signing_node.aqua new file mode 100644 index 0000000..398d382 --- /dev/null +++ b/aqua-examples/near-integration/near-signing-node/aqua/near_signing_node.aqua @@ -0,0 +1,28 @@ +data AccountState: + amount: string + block_hash: string + block_height: u64 + code_hash: string + locked: string + storage_paid_at: u64 + storage_usage: u64 + +service NearSignerApi("near"): + account_state(network_id: string, account_id: string, password: string) -> string + send_money(network_id: string, account_id: string, receiver_id:string, amount:string, password: string) -> string + get_balance(network_id: string, account_id: string, password: string) -> string + +func account_state(network_id:string, account_id:string, password: string, node:string, relay:string) -> string: + on node via relay: + res <- NearSignerApi.account_state(network_id, account_id, password) + <- res + +func send_money(network_id:string, account_id:string, receiver_id:string, amount:string, password: string, node:string, relay:string) -> string: + on node via relay: + res <- NearSignerApi.send_money(network_id, account_id, receiver_id, amount, password) + <- res + +func get_balance(network_id: string, account_id: string, password: string, node:string, relay:string) -> string: + on node via relay: + res <- NearSignerApi.get_balance(network_id, account_id, password) + <- res \ No newline at end of file diff --git a/aqua-examples/near-integration/near-signing-node/package.json b/aqua-examples/near-integration/near-signing-node/package.json new file mode 100644 index 0000000..6c098fe --- /dev/null +++ b/aqua-examples/near-integration/near-signing-node/package.json @@ -0,0 +1,25 @@ +{ + "name": "near-signing-node", + "version": "0.1.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node -r ts-node/register src/index.ts", + "compile-aqua": "aqua -i aqua/ -o src/_aqua", + "watch-aqua": "chokidar \"**/*.aqua\" -c \"npm run compile-aqua\"" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@fluencelabs/aqua": "^0.5.0-249", + "chokidar-cli": "^1.2.0", + "ts-node": "^10.4.0", + "typescript": "^4.5.2" + }, + "dependencies": { + "@fluencelabs/fluence": "^0.15.3", + "@fluencelabs/fluence-network-environment": "1.0.10", + "@types/sqlite3": "^3.1.7", + "near-api-js": "^0.43.1" + } +} diff --git a/aqua-examples/near-integration/near-signing-node/src/_aqua/near_signing_node.ts b/aqua-examples/near-integration/near-signing-node/src/_aqua/near_signing_node.ts new file mode 100644 index 0000000..2163cb4 --- /dev/null +++ b/aqua-examples/near-integration/near-signing-node/src/_aqua/near_signing_node.ts @@ -0,0 +1,542 @@ +/** + * + * This file is auto-generated. Do not edit manually: changes may be erased. + * Generated by Aqua compiler: https://github.com/fluencelabs/aqua/. + * If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues + * Aqua version: 0.5.0-SNAPSHOT + * + */ +import { Fluence, FluencePeer } from '@fluencelabs/fluence'; +import { + CallParams, + callFunction, + registerService, +} from '@fluencelabs/fluence/dist/internal/compilerSupport/v2'; + + +// Services + +export interface NearSignerApiDef { + account_state: (network_id: string, account_id: string, password: string, callParams: CallParams<'network_id' | 'account_id' | 'password'>) => string | Promise; + get_balance: (network_id: string, account_id: string, password: string, callParams: CallParams<'network_id' | 'account_id' | 'password'>) => string | Promise; + send_money: (network_id: string, account_id: string, receiver_id: string, amount: string, password: string, callParams: CallParams<'network_id' | 'account_id' | 'receiver_id' | 'amount' | 'password'>) => string | Promise; +} +export function registerNearSignerApi(service: NearSignerApiDef): void; +export function registerNearSignerApi(serviceId: string, service: NearSignerApiDef): void; +export function registerNearSignerApi(peer: FluencePeer, service: NearSignerApiDef): void; +export function registerNearSignerApi(peer: FluencePeer, serviceId: string, service: NearSignerApiDef): void; + + +export function registerNearSignerApi(...args: any) { + registerService( + args, + { + "defaultServiceId" : "near", + "functions" : [ + { + "functionName" : "account_state", + "argDefs" : [ + { + "name" : "network_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "account_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "password", + "argType" : { + "tag" : "primitive" + } + } + ], + "returnType" : { + "tag" : "primitive" + } + }, + { + "functionName" : "get_balance", + "argDefs" : [ + { + "name" : "network_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "account_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "password", + "argType" : { + "tag" : "primitive" + } + } + ], + "returnType" : { + "tag" : "primitive" + } + }, + { + "functionName" : "send_money", + "argDefs" : [ + { + "name" : "network_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "account_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "receiver_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "amount", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "password", + "argType" : { + "tag" : "primitive" + } + } + ], + "returnType" : { + "tag" : "primitive" + } + } + ] +} + ); +} + +// Functions + + +export function account_state( + network_id: string, + account_id: string, + password: string, + node: string, + relay: string, + config?: {ttl?: number} +): Promise; + +export function account_state( + peer: FluencePeer, + network_id: string, + account_id: string, + password: string, + node: string, + relay: string, + config?: {ttl?: number} +): Promise; + +export function account_state(...args: any) { + + let script = ` + (xor + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "network_id") [] network_id) + ) + (call %init_peer_id% ("getDataSrv" "account_id") [] account_id) + ) + (call %init_peer_id% ("getDataSrv" "password") [] password) + ) + (call %init_peer_id% ("getDataSrv" "node") [] node) + ) + (call %init_peer_id% ("getDataSrv" "relay") [] relay) + ) + (call -relay- ("op" "noop") []) + ) + (call relay ("op" "noop") []) + ) + (xor + (call node ("near" "account_state") [network_id account_id password] res) + (seq + (seq + (seq + (call relay ("op" "noop") []) + (call -relay- ("op" "noop") []) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1]) + ) + (call -relay- ("op" "noop") []) + ) + ) + ) + (call relay ("op" "noop") []) + ) + (call -relay- ("op" "noop") []) + ) + (xor + (call %init_peer_id% ("callbackSrv" "response") [res]) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2]) + ) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3]) + ) + ` + return callFunction( + args, + { + "functionName" : "account_state", + "returnType" : { + "tag" : "primitive" + }, + "argDefs" : [ + { + "name" : "network_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "account_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "password", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "node", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "relay", + "argType" : { + "tag" : "primitive" + } + } + ], + "names" : { + "relay" : "-relay-", + "getDataSrv" : "getDataSrv", + "callbackSrv" : "callbackSrv", + "responseSrv" : "callbackSrv", + "responseFnName" : "response", + "errorHandlingSrv" : "errorHandlingSrv", + "errorFnName" : "error" + } +}, + script + ) +} + + + +export function send_money( + network_id: string, + account_id: string, + receiver_id: string, + amount: string, + password: string, + node: string, + relay: string, + config?: {ttl?: number} +): Promise; + +export function send_money( + peer: FluencePeer, + network_id: string, + account_id: string, + receiver_id: string, + amount: string, + password: string, + node: string, + relay: string, + config?: {ttl?: number} +): Promise; + +export function send_money(...args: any) { + + let script = ` + (xor + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "network_id") [] network_id) + ) + (call %init_peer_id% ("getDataSrv" "account_id") [] account_id) + ) + (call %init_peer_id% ("getDataSrv" "receiver_id") [] receiver_id) + ) + (call %init_peer_id% ("getDataSrv" "amount") [] amount) + ) + (call %init_peer_id% ("getDataSrv" "password") [] password) + ) + (call %init_peer_id% ("getDataSrv" "node") [] node) + ) + (call %init_peer_id% ("getDataSrv" "relay") [] relay) + ) + (call -relay- ("op" "noop") []) + ) + (call relay ("op" "noop") []) + ) + (xor + (call node ("near" "send_money") [network_id account_id receiver_id amount password] res) + (seq + (seq + (seq + (call relay ("op" "noop") []) + (call -relay- ("op" "noop") []) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1]) + ) + (call -relay- ("op" "noop") []) + ) + ) + ) + (call relay ("op" "noop") []) + ) + (call -relay- ("op" "noop") []) + ) + (xor + (call %init_peer_id% ("callbackSrv" "response") [res]) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2]) + ) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3]) + ) + ` + return callFunction( + args, + { + "functionName" : "send_money", + "returnType" : { + "tag" : "primitive" + }, + "argDefs" : [ + { + "name" : "network_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "account_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "receiver_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "amount", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "password", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "node", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "relay", + "argType" : { + "tag" : "primitive" + } + } + ], + "names" : { + "relay" : "-relay-", + "getDataSrv" : "getDataSrv", + "callbackSrv" : "callbackSrv", + "responseSrv" : "callbackSrv", + "responseFnName" : "response", + "errorHandlingSrv" : "errorHandlingSrv", + "errorFnName" : "error" + } +}, + script + ) +} + + + +export function get_balance( + network_id: string, + account_id: string, + password: string, + node: string, + relay: string, + config?: {ttl?: number} +): Promise; + +export function get_balance( + peer: FluencePeer, + network_id: string, + account_id: string, + password: string, + node: string, + relay: string, + config?: {ttl?: number} +): Promise; + +export function get_balance(...args: any) { + + let script = ` + (xor + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (seq + (call %init_peer_id% ("getDataSrv" "-relay-") [] -relay-) + (call %init_peer_id% ("getDataSrv" "network_id") [] network_id) + ) + (call %init_peer_id% ("getDataSrv" "account_id") [] account_id) + ) + (call %init_peer_id% ("getDataSrv" "password") [] password) + ) + (call %init_peer_id% ("getDataSrv" "node") [] node) + ) + (call %init_peer_id% ("getDataSrv" "relay") [] relay) + ) + (call -relay- ("op" "noop") []) + ) + (call relay ("op" "noop") []) + ) + (xor + (call node ("near" "get_balance") [network_id account_id password] res) + (seq + (seq + (seq + (call relay ("op" "noop") []) + (call -relay- ("op" "noop") []) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 1]) + ) + (call -relay- ("op" "noop") []) + ) + ) + ) + (call relay ("op" "noop") []) + ) + (call -relay- ("op" "noop") []) + ) + (xor + (call %init_peer_id% ("callbackSrv" "response") [res]) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 2]) + ) + ) + (call %init_peer_id% ("errorHandlingSrv" "error") [%last_error% 3]) + ) + ` + return callFunction( + args, + { + "functionName" : "get_balance", + "returnType" : { + "tag" : "primitive" + }, + "argDefs" : [ + { + "name" : "network_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "account_id", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "password", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "node", + "argType" : { + "tag" : "primitive" + } + }, + { + "name" : "relay", + "argType" : { + "tag" : "primitive" + } + } + ], + "names" : { + "relay" : "-relay-", + "getDataSrv" : "getDataSrv", + "callbackSrv" : "callbackSrv", + "responseSrv" : "callbackSrv", + "responseFnName" : "response", + "errorHandlingSrv" : "errorHandlingSrv", + "errorFnName" : "error" + } +}, + script + ) +} diff --git a/aqua-examples/near-integration/near-signing-node/src/index.ts b/aqua-examples/near-integration/near-signing-node/src/index.ts new file mode 100644 index 0000000..f5e29fb --- /dev/null +++ b/aqua-examples/near-integration/near-signing-node/src/index.ts @@ -0,0 +1,141 @@ +import { Fluence, KeyPair as FluenceKeyPair } from "@fluencelabs/fluence"; +import { krasnodar } from "@fluencelabs/fluence-network-environment"; +import { NearSignerApiDef, registerNearSignerApi } from "./_aqua/near_signing_node"; +import * as fs from 'fs'; +import * as path from 'path'; +import { Buffer } from 'buffer'; +import * as nearAPI from "near-api-js"; +import { AccountState } from './interfaces'; + + +const { connect, keyStores, KeyPair, WalletConnection, Account } = nearAPI; + + +const MY_LAME_PASSWORD = "lame_password"; + +// temp fix replace with your key, e.g., account pk +const SeedArray = new Uint8Array([10, 10, 20, 20, 100, 100]); + + +class NearSigner implements NearSignerApiDef { + + _homedir = require("os").homedir(); + _CREDENTIALS_DIR = ".near-credentials"; + _credentialsPath = path.join(this._homedir, this._CREDENTIALS_DIR); + _keyStore = new keyStores.UnencryptedFileSystemKeyStore(this._credentialsPath); + + password_checker(password: string): boolean { + return (password === MY_LAME_PASSWORD) + } + + async send_money(network_id: string, account_id: string, receiver_id: string, amount: string, password: string): Promise { + if (!this.password_checker(password)) { + return Promise.resolve("Not Authorized") + } + const config = get_config(network_id, this._keyStore); + const near = await network_connect(config); + let account = await near.account(account_id); + let tx_receipt = await account.sendMoney(receiver_id, amount) + // return Promise.resolve(tx_receipt); + let result = Promise.resolve(tx_receipt); + return result; + } + + async account_state(network_id: string, account_id: string, password: string): Promise { + if (!this.password_checker(password)) { + return Promise.resolve("Not Authorized") + } + + const config = get_config(network_id, this._keyStore); + const near = await network_connect(config); + const account = await near.account(account_id); + const response = await account.state(); + + return Promise.resolve(response); + } + + async get_balance(network_id: string, account_id: string, password: string): Promise { + if (!this.password_checker(password)) { + return Promise.resolve("Not Authorized") + } + + const config = get_config(network_id, this._keyStore); + const near = await network_connect(config); + const account = await near.account(account_id); + const balance = await account.getAccountBalance(); + return Promise.resolve(balance); + } + + /* + async sign_tx(network_id: string, from: string, to: string): Promise { + // const config = get_config("testnet", this._keyStore); + const near = await network_connect(config); + + let status = await near.connection.provider.status(); + const blockHash = status.sync_info.latest_block_hash; + + const raw_tx = nearAPI.transactions.createTransaction( + from, + pk, + to, + nonce_pk, + actions, + blockHash + ); + + const bytes = raw_tx.encode(); + + } + + async verify_signature(network_id: string, keyStore: nearAPI.keyStores.UnencryptedFileSystemKeyStore, account_id: string, payload: string, signature: Uint8Array) { + const keyPair = await keyStore.getKey(network_id, account_id); + const msg = Buffer.from(payload); + + } + */ +} + + +function get_config(networkId: string, keyStore: nearAPI.keyStores.UnencryptedFileSystemKeyStore): any { + const config = { + networkId, + keyStore, + nodeUrl: `https://rpc.${networkId}.near.org`, + walletUrl: `https://wallet.${networkId}.near.org`, + helperUrl: `https://helper.${networkId}.near.org`, + explorerUrl: `https://explorer.${networkId}.near.org`, + }; + + return config; +} + +async function network_connect(config: any): Promise { + const near = await connect(config); + return Promise.resolve(near); +} + + +async function main() { + + await Fluence.start({ + connectTo: krasnodar[5], + /* + connectTo: { + multiaddr: "/ip4/127.0.0.1/tcp/9990/ws/p2p/12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK", + peerId: "12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK" + }, + */ + KeyPair: await FluenceKeyPair.fromEd25519SK(SeedArray) + }); + + console.log("PeerId: ", Fluence.getStatus().peerId); + console.log("Relay id: ", Fluence.getStatus().relayPeerId); + + registerNearSignerApi("near", new NearSigner()); + + console.log("ctrl-c to exit"); + + // await Fluence.stop(); +} + +main(); diff --git a/aqua-examples/near-integration/near-signing-node/src/index.ts.bak2 b/aqua-examples/near-integration/near-signing-node/src/index.ts.bak2 new file mode 100644 index 0000000..f80bf09 --- /dev/null +++ b/aqua-examples/near-integration/near-signing-node/src/index.ts.bak2 @@ -0,0 +1,227 @@ +import { Fluence, KeyPair as FluenceKeyPair } from "@fluencelabs/fluence"; +import { krasnodar } from "@fluencelabs/fluence-network-environment"; +import { sign_transaction, NearSignerApiDef, registerNearSignerApi } from "./_aqua/near_signer"; +import * as nearAPI from "near-api-js"; +import { KeyStore } from "near-api-js/lib/key_stores"; +import * as fs from 'fs'; +import * as path from 'path'; +import { Buffer } from 'buffer'; +import { Near } from "near-api-js"; + + +const { connect, keyStores, KeyPair, WalletConnection, Account } = nearAPI; + +// const homedir = require("os").homedir(); +// const CREDENTIALS_DIR = ".near-credentials"; +// const credentialsPath = path.join(homedir, CREDENTIALS_DIR); +// const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); + +// temp fix replace with your key, e.g., account pk +const SeedArray = new Uint8Array([10, 10, 20, 20, 100, 100]); +class NearSigner implements NearSignerApiDef { + + _homedir = require("os").homedir(); + _CREDENTIALS_DIR = ".near-credentials"; + _credentialsPath = path.join(this._homedir, this._CREDENTIALS_DIR); + _keyStore = new keyStores.UnencryptedFileSystemKeyStore(this._credentialsPath); + + + async sign_transaction(network_id: string, tx_string: string, password: string): Promise { + const config = get_config(network_id, this._keyStore); + const near = await network_connect(config); + const wallet = await wallet_connect(near, "signer-node"); + await wallet_signout(wallet); + + return Promise.resolve("boo yah"); + } + + async account_state(network_id: string, account_id: string): Promise { + const config = get_config(network_id, this._keyStore); + const near = await network_connect(config); + const state = await account_state(near, account_id); + console.log("account state: ", state); + + return Promise.resolve(state); + } + + async send_tokens(network_id: string, account_id: string, receiver_id: string, amount: string): Promise { + console.log("keyStore: ", keyStore); + console.log("keyStore: ", this._keyStore); + + const config = get_config(network_id, keyStore); + // const config = get_config("testnet", this._keyStore); + console.log("config: ", config); + const near = await network_connect(config); + // let account = await near.account(account_id); + let account = await near.account("boneyard93501.testnet"); + console.log("account: ", account); + // let tx_receipt = await account.sendMoney(receiver_id, amount); + let tx_receipt = await account.sendMoney("boneyard93502.testnet", "100000"); + console.log("receipt: ", tx_receipt); + + return Promise.resolve(tx_receipt); + } +} + +function get_config(networkId: string, keyStore: any): any { + const config = { + // networkId, + networkId: "testnet", + keyStore, + // nodeUrl: `https://rpc.${networkId}.near.org`, + nodeUrl: `https://rpc.testnet.near.org`, + // walletUrl: `https://wallet.${networkId}.near.org`, + walletUrl: `https://wallet.testnet.near.org`, + // helperUrl: `https://helper.${networkId}.near.org`, + helperUrl: `https://helper.testnet.near.org`, + // explorerUrl: `https://explorer.${networkId}.near.org`, + explorerUrl: `https://explorer.testnet.near.org`, + }; + + return config; +} + +async function network_connect(network_id: string): Promise { + const config = get_config(network_id, keyStore); + const near = await connect(config); + // console.log("near: ", near); + return Promise.resolve(near); +} + +async function wallet_signout(wallet: nearAPI.WalletConnection): Promise { + if (wallet.isSignedIn()) { + wallet.signOut(); + } + return Promise.resolve(wallet.isSignedIn()); +} + +async function wallet_connect(near: nearAPI.Near, app_name: string): Promise { + // create wallet connection + const wallet = new WalletConnection(near, app_name); + return Promise.resolve(wallet); +} + +async function wallet_load(network_id: string, account_id: string) { + const config = get_config(network_id, keyStore); + const near = await connect(config); + const account = await near.account(account_id); +} + + +async function sign(network_id: string, payload: string, account_id: string): Promise { + const keyPair = await keyStore.getKey(network_id, account_id); + const msg = Buffer.from(payload); + const { signature } = keyPair.sign(msg); + return Promise.resolve(signature); +} + +async function verify_signature(network_id: string, account_id: string, payload: string, signature: Uint8Array) { + const keyPair = await keyStore.getKey(network_id, account_id); + const msg = Buffer.from(payload); + +} + + +// account +async function get_balance(near: nearAPI.Near, account_id: string): Promise { + const account = await near.account(account_id); + const balance = await account.getAccountBalance(); + return Promise.resolve(balance); +} + + +// deploy +async function deploy_contract_local(near: nearAPI.Near, account_id: string, wasm_path: string): Promise { + + const account = await near.account(account_id); + const deployment = account.deployContract(fs.readFileSync(wasm_path)); + return Promise.resolve(deployment); +} + +async function deploy_contract(near: nearAPI.Near, account_id: string, wasm_raw: Uint8Array): Promise { + + const account = await near.account(account_id); + const deployment = account.deployContract(wasm_raw); + return Promise.resolve(deployment); +} + + +async function deploy_contract_from_string(near: nearAPI.Near, account_id: string, wasm_str: string): Promise { + + const account = await near.account(account_id); + const buffer = Buffer.from(wasm_str, 'utf8'); + const deployment = account.deployContract(buffer); + return Promise.resolve(deployment); +} + + +async function send_tokens(near: nearAPI.Near, account_id: string, receiver_id: string, amount: string): Promise { + const account = await near.account(account_id); + const result = await account.sendMoney(receiver_id, amount); + return Promise.resolve(result); +} + + +// state +async function account_state(near: nearAPI.Near, account_id: string): Promise { + const account = await near.account(account_id); + const response = await account.state(); + return Promise.resolve(response); +} + +interface AccountState { + amount: string, + block_hash: string, + block_height: number, + code_hash: string, + locked: string, + storage_paid_at: number, + storage_usage: number +} + +async function main() { + /* + const config = get_config("testnet"); + const near = await connect(config); + console.log("near: ", near, "\n"); + + let account = await near.account("boneyard93501.testnet"); + let res = await account.sendMoney("boneyard93502.testnet", "100000"); + console.log("tx: ", res); + */ + + console.log("keyStore: ", keyStore); + + await Fluence.start({ + connectTo: krasnodar[5], + /* + connectTo: { + multiaddr: "/ip4/127.0.0.1/tcp/9990/ws/p2p/12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK", + peerId: "12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK" + }, + */ + KeyPair: await FluenceKeyPair.fromEd25519SK(SeedArray) + }); + + console.log("PeerId: ", Fluence.getStatus().peerId); + console.log("Relay id: ", Fluence.getStatus().relayPeerId); + + registerNearSignerApi("near", new NearSigner()); + + + /* + const config = get_config("testnet"); + const near = await connect(config); + console.log("near: ", near, "\n"); + + let accounts = new Array(); + + accounts.push(await near.account("boneyard93501.testnet")); + accounts.push(await near.account("boneyard93502.testnet")); + console.log("Accounts: ", accounts); + */ + + console.log("ctrl-c to exit"); +} + +main(); diff --git a/aqua-examples/near-integration/near-signing-node/src/interfaces.ts b/aqua-examples/near-integration/near-signing-node/src/interfaces.ts new file mode 100644 index 0000000..cfc2629 --- /dev/null +++ b/aqua-examples/near-integration/near-signing-node/src/interfaces.ts @@ -0,0 +1,102 @@ +export interface AccountState { + amount: string, + block_hash: string, + block_height: number, + code_hash: string, + locked: string, + storage_paid_at: number, + storage_usage: number +} + +export interface GetBalance { + available: string, + staked: string, + stateStaked: string, + total: string +} + + + +interface ReceiptsOutcome { + "block_hash": string, + "id": string, + "outcome": { + "executor_id": string, + "gas_burnt": number, + "logs": Array, + "metadata": { + "gas_profile": Array, + "version": number + }, + "receipt_ids": Array, + "status": { + "SuccessValue": string + }, + "tokens_burnt": string + }, + "proof": Array +} + +interface TransactionOutcome { + +} + +export interface SendMoneyReceipt { + "receipts_outcome": Array, + "status": { + "SuccessValue": "" + }, + "transaction": { + "actions": [ + { + "Transfer": { + "deposit": "10000" + } + } + ], + "hash": "CmK9rhgBSmamMHhYMzqkeNcH4CtaSvtCHHqkrL4UwM4n", + "nonce": 74253069000001, + "public_key": "ed25519:632DzcF3w7SLXJzDRcFdSHUBY2De8LECfJ9kCC4yERuj", + "receiver_id": "boneyard93501.testnet", + "signature": "ed25519:XJieAFpDwJHpvJ3QavjevfQkNAP5rFkXzBXYgUSrB2aUU88n8JDLJgxdkaT6yaj8DstjGbnfXug4AVKyzGmJhnm", + "signer_id": "boneyard93502.testnet" + }, + "transaction_outcome": { + "block_hash": "2Tuq4SCMJBxwhnnVyoHvAZbvydouDTk2DT7Z9F2kZnsy", + "id": "CmK9rhgBSmamMHhYMzqkeNcH4CtaSvtCHHqkrL4UwM4n", + "outcome": { + "executor_id": "boneyard93502.testnet", + "gas_burnt": 223182562500, + "logs": [], + "metadata": { + "gas_profile": null, + "version": 1 + }, + "receipt_ids": [ + "3c8CSgyEuisq8CcMgPGt7TVkiYBwei4vkR6CpJdcogbB" + ], + "status": { + "SuccessReceiptId": "3c8CSgyEuisq8CcMgPGt7TVkiYBwei4vkR6CpJdcogbB" + }, + "tokens_burnt": "22318256250000000000" + }, + "proof": [] + } +} + + + + + + + + + + + + + +interface Result { + stderr: string, + stdout: string +}; diff --git a/aqua-examples/near-integration/near-signing-node/tsconfig.json b/aqua-examples/near-integration/near-signing-node/tsconfig.json new file mode 100644 index 0000000..e1a4198 --- /dev/null +++ b/aqua-examples/near-integration/near-signing-node/tsconfig.json @@ -0,0 +1,69 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } + } \ No newline at end of file diff --git a/aqua-examples/near-integration/services/configs/Config.toml b/aqua-examples/near-integration/services/configs/Config.toml new file mode 100644 index 0000000..127e580 --- /dev/null +++ b/aqua-examples/near-integration/services/configs/Config.toml @@ -0,0 +1,13 @@ +modules_dir = "artifacts" + +[[module]] +name = "curl_adapter" +mem_pages_count = 10 +logger_enabled = true +[module.mounted_binaries] +curl = "/usr/bin/curl" + +[[module]] +name = "near_rpc_services" +mem_pages_count = 10 +logger_enabled = true diff --git a/aqua-examples/near-integration/services/configs/curl_adapter_cfg.json b/aqua-examples/near-integration/services/configs/curl_adapter_cfg.json new file mode 100644 index 0000000..f3899ef --- /dev/null +++ b/aqua-examples/near-integration/services/configs/curl_adapter_cfg.json @@ -0,0 +1,8 @@ +{ + "name": "curl_adapter", + "mountedBinaries": { + "curl": "/usr/bin/curl" + }, + "mem_page_count": 1, + "logger_enabled": false +} diff --git a/aqua-examples/near-integration/services/configs/near_rpc_services_cfg.json b/aqua-examples/near-integration/services/configs/near_rpc_services_cfg.json new file mode 100644 index 0000000..53ae24a --- /dev/null +++ b/aqua-examples/near-integration/services/configs/near_rpc_services_cfg.json @@ -0,0 +1,5 @@ +{ + "name": "near_rpc_services", + "mem_page_count": 10, + "logger_enabled": true +} diff --git a/aqua-examples/near-integration/services/curl-adapter/Cargo.toml b/aqua-examples/near-integration/services/curl-adapter/Cargo.toml new file mode 100644 index 0000000..c7cd104 --- /dev/null +++ b/aqua-examples/near-integration/services/curl-adapter/Cargo.toml @@ -0,0 +1,21 @@ +[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 = { version = "0.6.11", features = ["logger"] } +log = "0.4.14" + +[dev-dependencies] +marine-rs-sdk-test = "0.4.0" + +[dev] +[profile.release] +opt-level = "s" diff --git a/aqua-examples/near-integration/services/curl-adapter/src/main.rs b/aqua-examples/near-integration/services/curl-adapter/src/main.rs new file mode 100644 index 0000000..e60fc7f --- /dev/null +++ b/aqua-examples/near-integration/services/curl-adapter/src/main.rs @@ -0,0 +1,39 @@ +/* + * 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; +use marine_rs_sdk::WasmLoggerBuilder; + +module_manifest!(); + +pub fn main() { + WasmLoggerBuilder::new().build().ok(); +} + +#[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/near-integration/services/near-rpc-services/.gitignore b/aqua-examples/near-integration/services/near-rpc-services/.gitignore new file mode 100644 index 0000000..bbccc88 --- /dev/null +++ b/aqua-examples/near-integration/services/near-rpc-services/.gitignore @@ -0,0 +1,5 @@ +debug/ +target/ +Cargo.lock +**/*.bk +**/*.bak diff --git a/aqua-examples/near-integration/services/near-rpc-services/Cargo.toml b/aqua-examples/near-integration/services/near-rpc-services/Cargo.toml new file mode 100644 index 0000000..984842c --- /dev/null +++ b/aqua-examples/near-integration/services/near-rpc-services/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "near-rpc-services" +version = "0.1.0" +authors = ["boneyard93501 <4523011+boneyard93501@users.noreply.github.com>"] +edition = "2018" +description = "near-services, a Marine wasi module" +license = "Apache-2.0" + +[[bin]] +name = "near_rpc_services" +path = "src/main.rs" + +[dependencies] +marine-rs-sdk = { version = "0.6.15", features = ["logger"] } +log = "0.4.14" + +[dev-dependencies] +marine-rs-sdk-test = "0.4.0" + +[dev] +[profile.release] +opt-level = "s" diff --git a/aqua-examples/near-integration/services/near-rpc-services/src/main.rs b/aqua-examples/near-integration/services/near-rpc-services/src/main.rs new file mode 100644 index 0000000..6333d1b --- /dev/null +++ b/aqua-examples/near-integration/services/near-rpc-services/src/main.rs @@ -0,0 +1,99 @@ +/* + * 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, MountedBinaryResult, WasmLoggerBuilder}; +use serde_json; + +mod utils; +use utils::{rpc_maker, url_maker}; + +module_manifest!(); + +pub fn main() { + WasmLoggerBuilder::new().build().unwrap(); +} + +#[marine] +pub struct Result { + pub stderr: String, + pub stdout: String, +} + +#[marine] +pub fn tx_status(network_id: String, tx_id: String, account_id: String, receipt: bool) -> Result { + let mut method = "tx".to_string(); + if receipt { + method = "EXPERIMENTAL_tx_status".to_string(); + } + let url = url_maker(network_id); + let params = format!("[\"{}\", \"{}\"]", tx_id, account_id); + let curl_params: Vec = rpc_maker(url, method, params); + let response = curl_request(curl_params); + Result { + stderr: response.error, + stdout: String::from_utf8(response.stdout).unwrap(), + } +} + +#[marine] +pub fn gas_price(network_id: String, block_ref: String) -> Result { + // block-ref can be block height or block hash + let method = "gas_price".to_string(); + let url = url_maker(network_id); + let params = format!("[\"{}\"]", block_ref); + let curl_params: Vec = rpc_maker(url, method, params); + let response = curl_request(curl_params); + Result { + stderr: response.error, + stdout: String::from_utf8(response.stdout).unwrap(), + } +} + +#[marine] +pub fn node_status(network_id: String) -> Result { + // block-ref can be block height or block hash + let method = "status".to_string(); + let url = url_maker(network_id); + let params = "[]".to_string(); + let curl_params: Vec = rpc_maker(url, method, params); + let response = curl_request(curl_params); + Result { + stderr: response.error, + stdout: String::from_utf8(response.stdout).unwrap(), + } +} + +#[marine] +pub fn view_account(network_id: String, account_id: String) -> Result { + let method = "queryX".to_string(); + let url = url_maker(network_id); + let params = format!( + "{{\"request_type\": \"view_account\", \"finality\": \"final\",\"account_id\": \"{}\"}}", + account_id + ); + let curl_params: Vec = rpc_maker(url, method, params); + let response = curl_request(curl_params); + Result { + stderr: response.error, + stdout: String::from_utf8(response.stdout).unwrap(), + } +} + +#[marine] +#[link(wasm_import_module = "curl_adapter")] +extern "C" { + pub fn curl_request(cmd: Vec) -> MountedBinaryResult; +} diff --git a/aqua-examples/near-integration/services/near-rpc-services/src/utils.rs b/aqua-examples/near-integration/services/near-rpc-services/src/utils.rs new file mode 100644 index 0000000..c466171 --- /dev/null +++ b/aqua-examples/near-integration/services/near-rpc-services/src/utils.rs @@ -0,0 +1,22 @@ +pub const JSON_RPC: &'static str = "2.0"; +pub const NONCE: &'static str = "dontcare"; + +pub fn rpc_maker(url: String, method: String, params: String) -> Vec { + let data: String = format!( + "-d {{\"jsonrpc\":\"{}\", \"id\":\"{}\", \"method\":\"{}\", \"params\":{} }}", + JSON_RPC, NONCE, method, params + ); + let curl_params = vec![ + "-X".to_string(), + "POST".to_string(), + url, + data, + "-H".to_string(), + "Content-Type: application/json".to_string(), + ]; + curl_params +} + +pub fn url_maker(network_id: String) -> String { + format!("https://rpc.{}.near.org", network_id) +} diff --git a/aqua-examples/near-integration/services/scripts/build.sh b/aqua-examples/near-integration/services/scripts/build.sh new file mode 100755 index 0000000..5db03e4 --- /dev/null +++ b/aqua-examples/near-integration/services/scripts/build.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash -o errexit -o nounset -o pipefail + +mkdir -p artifacts +rm -f artifacts/*.wasm + +cd curl-adapter +cargo update --aggressive +marine build --release + +cd ../near-rpc-services +cargo update --aggressive +marine build --release +cd .. + +cp near-rpc-services/target/wasm32-wasi/release/near_rpc_services.wasm artifacts/ +cp curl-adapter/target/wasm32-wasi/release/curl_adapter.wasm artifacts/