GitBook: [docs] 54 pages and 17 assets modified

This commit is contained in:
boneyard93501
2021-03-29 02:17:52 +00:00
committed by gitbook-bot
parent c6a1b1204a
commit 818e9f6253
71 changed files with 3830 additions and 0 deletions

View File

@ -0,0 +1,10 @@
# Adding A Storage Service
So far, all of the modules we have used were stateless and we did not have to give security much thought. If application A and application B both use our curl service, we don't run into any kind of conflict other than maybe scheduling. Not so for stateful services like a database. If we take no precautions with respect to authorization, other users of the service may, for example, alter or delete our data. And that's definitely not what we want especially if we plan on monetizing that data.
Without diving too deep into the Fluence security framework, you should be aware that Fluence has an out-of-the box authentication primitive that allows a `user == owner` check not unlike what we've seen in various blockchain platforms or plain old _sudo_. Of course, the Fluence security framework goes much further and affords developers a great deal of flexibility to code a security solution to their needs. For the purpose of our tutorial, however, we'll stick with the built-in authentication and use it as a base for [ambient authority](https://en.wikipedia.org/wiki/Ambient_authority). That is, we use authentication as an authorization guard for select functions at development time and provide the necessary credentials at service call time.
For the purposes of this tutorial, there is a caveat you need to keep in mind: Every reader of this document inevitably ends up using the same sample service with the same ownership control. In the highly, highly unlikely event you're getting funky results, it's most likely due to someone else doing the very same tutorial at the very same time. \[Jinx\]\([https://en.wikipedia.org/wiki/Jinx\_\(game](https://en.wikipedia.org/wiki/Jinx_%28game)\)\) ! Buy me a Coke, drink the Coke, slowly, try again and you should be fine.
The next sections explore both the setup and the use of our database: Sqlite as a Service.

View File

@ -0,0 +1,299 @@
# CRUD All the Way
It's finally time to populate our shiny new storage as a service with some data. In order to suss out our system, let's run an AIR smoketest. Save the script below to ethqlite\_roundtrip.clj.
```text
(xor
(seq
(seq
(seq
(seq
(seq
(seq
(seq
(call relay ("op" "identity") [])
(call node_1 (service_1 "get_latest_block") [api_key] hex_block_result)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") [hex_block_result])
)
)
(seq
(seq
(call relay ("op" "identity") [])
(call node_2 (service_2 "hex_to_int") [hex_block_result] int_block_result)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") [int_block_result])
)
)
)
(seq
(seq
(call relay ("op" "identity") [])
(call node_1 (service_1 "get_block") [api_key int_block_result] block_result)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") [block_result])
)
)
)
(seq
(seq
(call relay ("op" "identity") [])
(call sqlite_node (sqlite_service "update_reward_blocks") [block_result] insert_result)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") [insert_result])
)
)
)
(seq
(seq
(call relay ("op" "identity") [])
(call sqlite_node (sqlite_service "get_latest_reward_block") [] select_result)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") [select_result])
)
)
)
(seq
(seq
(seq
(call relay ("op" "identity") [])
(call sqlite_node (sqlite_service "get_reward_block") [int_block_result] select_result_2)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") [select_result_2])
)
)
(seq
(seq
(call relay ("op" "identity") [])
(call sqlite_node (sqlite_service "get_miner_rewards") [select_result_2.$.["block_miner"]!] select_result_3)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") [select_result_3])
)
)
)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") ["XOR FAILED" %last_error%])
)
)
```
The top part of the script is identical what we used before:
* get the latest block number \(`get_latest_block`\),
* convert the hex string to an integer \(`hex_to_int`\) and
* retrieve the reward block info \(`get_block`\)
The new service components called are:
* update\_reward\_blocks, which takes the `get_block` output and writes it to the db. Please note that this service requires authentication,
* get\_latest\_reward\_block, which is a read operation querying the most recent row in the reward block table,
* get\_reward\_block, which takes a miner address and in this cae the one produced by `get_block`, and finally
* get\_miner\_rewards, which returns a list of miner rewards for a particular miner address; in this case, the one provided by the `get_reward_block` result. Note the `$` operator to access the `block_miner` field in the return struct and the `!` operator to flatten the response
From the previous section we know that
* service\_1: 74d5c5da-4c83-4af9-9371-2ab5d31f8019 , node\_1: 12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H
* service\_2: 285e2a5e-e505-475f-a99d-15c16c7253f9 , node\_2: 12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH
* service\_sqlite : 506528d3-3aaf-4ef5-a97d-18f1654fcf8d , node\_sqlite: 12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH
* api\_key: you Etherscan API key
* client seed: Dq3rsUZUs25FGrZM3qpiUzyKJ3NFgtqocgGRqWq9YGsx
which allows us to construct the payload for our `fldist` command-line app:
```bash
fldist --node-id 12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH run_air -p air-scripts/ethqlite_roundtrip.clj -d '{"service_1":"74d5c5da-4c83-4af9-9371-2ab5d31f8019", "node_1":"12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H","service_2":"285e2a5e-e505-475f-a99d-15c16c7253f9", "node_2": "12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH", "sqlite_service":"506528d3-3aaf-4ef5-a97d-18f1654fcf8d", "sqlite_node":"12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH", "api_key": "your api key"}' -s Dq3rsUZUs25FGrZM3qpiUzyKJ3NFgtqocgGRqWq9YGsx
```
and upon execution gives us the expected results flow:
```bash
===================
[
"0xb736bc"
]
[
[
{
peer_pk: '12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H',
service_id: '74d5c5da-4c83-4af9-9371-2ab5d31f8019',
function_name: 'get_latest_block',
json_path: ''
}
]
]
===================
===================
[
12007100
]
[
[
{
peer_pk: '12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH',
service_id: '285e2a5e-e505-475f-a99d-15c16c7253f9',
function_name: 'hex_to_int',
json_path: ''
}
]
]
===================
===================
[
"{\"status\":\"1\",\"message\":\"OK\",\"result\":{\"blockNumber\":\"12007100\",\"timeStamp\":\"1615330221\",\"blockMiner\":\"0x04668ec2f57cc15c381b461b9fedab5d451c8f7f\",\"blockReward\":\"3799386136990487274\",\"uncles\":[],\"uncleInclusionReward\":\"0\"}}"
]
[
[
{
peer_pk: '12D3KooWGzNvhSDsgFoHwpWHAyPf1kcTYCGeRBPfznL8J6qdyu2H',
service_id: '74d5c5da-4c83-4af9-9371-2ab5d31f8019',
function_name: 'get_block',
json_path: ''
}
]
]
===================
===================
[
{
"err_str": "",
"success": 1
}
]
[
[
{
peer_pk: '12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH',
service_id: '506528d3-3aaf-4ef5-a97d-18f1654fcf8d',
function_name: 'update_reward_blocks',
json_path: ''
}
]
]
===================
===================
[
{
"block_miner": "\"0x04668ec2f57cc15c381b461b9fedab5d451c8f7f\"",
"block_number": 12007100,
"block_reward": "3799386136990487274",
"timestamp": 1615330221
}
]
[
[
{
peer_pk: '12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH',
service_id: '506528d3-3aaf-4ef5-a97d-18f1654fcf8d',
function_name: 'get_latest_reward_block',
json_path: ''
}
]
]
===================
===================
[
{
"block_miner": "\"0x04668ec2f57cc15c381b461b9fedab5d451c8f7f\"",
"block_number": 12007100,
"block_reward": "3799386136990487274",
"timestamp": 1615330221
}
]
[
[
{
peer_pk: '12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH',
service_id: '506528d3-3aaf-4ef5-a97d-18f1654fcf8d',
function_name: 'get_reward_block',
json_path: ''
}
]
]
===================
===================
[
{
"miner_address": "\"0x04668ec2f57cc15c381b461b9fedab5d451c8f7f\"",
"rewards": [
"3799386136990487274"
]
}
]
[
[
{
peer_pk: '12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH',
service_id: '506528d3-3aaf-4ef5-a97d-18f1654fcf8d',
function_name: 'get_miner_rewards',
json_path: ''
}
]
]
===================
```
Feel free to re-run the workflow without the `-s` flag. Before we conclude, let's cleanup our digital footprint and reset the Sqlite service for other tutorial users.
```text
(xor
(seq
(seq
(call relay ("op" "identity") [])
(call sqlite_node (sqlite_service "owner_nuclear_reset") [] result)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") [result])
)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") ["XOR FAILED" %last_error%])
)
)
```
save the script to _ethqlite\_reset.clj_ and run with:
```bash
fldist --node-id 12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH run_air -p air-scripts/ethqlite_reset.clj -d '{"service": "506528d3-3aaf-4ef5-a97d-18f1654fcf8d", "node_1": "12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH"}' -s Dq3rsUZUs25FGrZM3qpiUzyKJ3NFgtqocgGRqWq9YGsx
```
to get
```bash
[
1
]
[
[
{
peer_pk: '12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH',
service_id: '506528d3-3aaf-4ef5-a97d-18f1654fcf8d',
function_name: 'owner_nuclear_reset',
json_path: ''
}
]
]
```
Of course, if you tried that without the `-s` flag, the return result would be 0. Try re-running the _ethqlite\_roundtrip.clj_ script after reset and without initialization.
In summary, you have extended the multi-service application with a Sqlite database service by simply adding the service methods to your existing workflow. We further extended the workflow by adding read queries which of course could be run separately or even as a separate application that may include a service-side paywall for data retrieval.

View File

@ -0,0 +1,161 @@
# Setting Up
For our database solution we have two goals in mind:
1. Persist reward block information and
2. Allow for the querying of the database
while maintaining not inly data integrity but also access control, which implies that we need some sort of authentication and authorization scheme that allows us to nominate and process
Again using the [Fluence Dashboard](https://dash.fluence.dev) we can find for a Sqlite database service solution, such as this [service](https://dash.fluence.dev/blueprint/b67afc58ed7d15757303b60e694ed5083faedb466b7cc36242fa0979d4f8b1b7), which gives us:
* service id: 506528d3-3aaf-4ef5-a97d-18f1654fcf8d and
* node id: 12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH
As with most stateful solutions, we need to take care of a little pre-work to set up the database, e.g., create tables, insert default values, etc., which requires us to fire a one time initialization call. But before we get to the service initialization, let's write a small script to check whether we have the necessary owner privileges and we do that with an AIR script:
```text
(xor
(seq
(seq
(call relay ("op" "identity") [])
(call node (service "am_i_owner") [] result)
; (call node_1 (service "get_tplet") [] result)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") [result])
)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") ["XOR FAILED" %last_error%])
)
)
```
This should look rather familiar as we're calling a am\_i\_owner method of the remote service to see if `owner == us` using the familiar `fldist` tool:
```bash
fldist --node-id 12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH run_air -p air-scripts/ethqlite_owner.clj -d '{"service": "506528d3-3aaf-4ef5-a97d-18f1654fcf8d", "node_1": "12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH"}'
```
which yields
```bash
[
0
]
[
[
{
peer_pk: '12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH',
service_id: '506528d3-3aaf-4ef5-a97d-18f1654fcf8d',
function_name: 'am_i_owner',
json_path: ''
}
]
]
```
Looking at that pesky 0, i.e. false, return value, it seems that we have no owner privileges. Let's see if the service really enforces ownership requirements and execute the init script discussed earlier. Our AIR script is pretty short and executes only the `init_service` method to handle table creation.
```text
(xor
(seq
(seq
(call relay ("op" "identity") [])
(call node_1 (service "init_service") [] result)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") [result])
)
)
(seq
(call relay ("op" "identity") [])
(call %init_peer_id% (returnService "run") ["XOR FAILED" %last_error%])
)
)
```
Run with:
```bash
fldist --node-id 12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH run_air -p air-scripts/ethqlite_init.clj -d '{"service": "506528d3-3aaf-4ef5-a97d-18f1654fcf8d", "node_1": "12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH"}'fldist --node-id 12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH run_air -p air-scripts/ethqlite_init.clj -d '{"service": "506528d3-3aaf-4ef5-a97d-18f1654fcf8d", "node_1": "12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH"}'
```
which yields
```bash
[
{
"err_msg": "Not authorized to use this service",
"success": 0
}
]
[
[
{
peer_pk: '12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH',
service_id: '506528d3-3aaf-4ef5-a97d-18f1654fcf8d',
function_name: 'init_service',
json_path: ''
}
]
]
```
Looks like the service wasn't joking and we need to have ownership privileges to even initiate the service. In order to prove ownership for the service, we need what's called the client seed, or just seed, in Fluence parlance, which is derived from a the private key of a user's keypair and used in the module upload and service deployment process. For our purposes, it suffices to say that the seed is `Dq3rsUZUs25FGrZM3qpiUzyKJ3NFgtqocgGRqWq9YGsx` and re-running the scripts with the seed information largely improves our lot.
```bash
fldist --node-id 12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH run_air -p air-scripts/ethqlite_owner.clj -d '{"service": "506528d3-3aaf-4ef5-a97d-18f1654fcf8d", "node_1": "12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH"}' -s Dq3rsUZUs25FGrZM3qpiUzyKJ3NFgtqocgGRqWq9YGsx
```
now gives us a juicy thumbs up regarding ownership:
```bash
[
1
]
[
[
{
peer_pk: '12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH',
service_id: '506528d3-3aaf-4ef5-a97d-18f1654fcf8d',
function_name: 'am_i_owner',
json_path: ''
}
]
]
```
and the db init
```bash
fldist --node-id 12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH run_air -p air-scripts/ethqlite_init.clj -d '{"service": "506528d3-3aaf-4ef5-a97d-18f1654fcf8d", "node_1": "12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH"}' -s Dq3rsUZUs25FGrZM3qpiUzyKJ3NFgtqocgGRqWq9YGsx
```
also executes:
```bash
[
{
"err_msg": "",
"success": 1
}
]
[
[
{
peer_pk: '12D3KooWKnRcsTpYx9axkJ6d69LPfpPXrkVLe96skuPTAo76LLVH',
service_id: '506528d3-3aaf-4ef5-a97d-18f1654fcf8d',
function_name: 'init_service',
json_path: ''
}
]
]
```
We now have the tools and confidence that we can authenticate as the service owner and that the service actually implements some authorization schema. Specifically, we expect all state changing operations to utilize the authorization guard and the read operations to be available without authorization.