TUTORIALS
How to become witness
Setup witness node
Download wallet and node daemon
Go to https://github.com/SophiaTX/SophiaTX/releases and download latest version.
Prepare node
Start node for the first time with following command:
./sophiatxd
Now go to kill the daemon and go to witness_node_data_dir/ and edit config.ini
cd witness_node_data_dir/
And modify this properties:
plugin = alexandria_api
plugin = network_broadcast_api
webserver-ws-endpoint = 0.0.0.0:9191
Now start cli_wallet
, create new wallet file and key for witness
./cli_wallet
new >>> set_password YOUR_PASSWORD
locked >>> unlock YOUR_PASSWORD
unlocked >>> import_key YOUR_PRIVATE_KEY
unlocked >>> suggest_brain_key
Output should look like something like this
{
"brain_priv_key": "BLENT EPOCHA GREED OCQUE BHALU TAPE SMUT AVERAH BALAO SHARPEN BUSTARD ATHIRST PACTION WIDBIN NYMPHA BANK",
"wif_priv_key": "5J7ejuMu2ZkNWQ367X38wVvBWFzgbsjPvw6PLNtwsYEQrbhigT6",
"pub_key": "SPH6m3vR9ZfRiGCSmpdY8G6kaAjXxGYBxu6pRE99cFL95SEcxEWeh"
}
Now kill the daemon and modify config.ini Now you can reedit config.ini with following properties (you can remove rest of the plugins if you want to be just the witness)
plugin = witness
witness = "YOUR_ACCOUNT_NAME"
private-key = 5J7ejuMu2ZkNWQ367X38wVvBWFzgbsjPvw6PLNtwsYEQrbhigT6 (just example)
enable-stale-production = false
required-participation = 35
And rerun the daemon with command
./sophiatxd
Now you need to vest tokens to you account and update yourself as witness
unlocked >>> transfer_to_vesting YOUR_ACCOUNT_NAME YOUR_ACCOUNT_NAME "250000 SPHTX" true
unlocked >>> update_witness YOUR_ACCOUNT_NAME "YOUR_URL" SPH6m3vR9ZfRiGCSmpdY8G6kaAjXxGYBxu6pRE99cFL95SEcxEWeh {"account_creation_fee":"0.05 SPHTX","maximum_block_size":50331648,"price_feeds":[]} true
Check if your setup is right
Go to cli_wallet and execute command: get_witness YOUR_WITNESS_NAME
and if you check the output, you should be able to you last confirmed block
Setup price feeding service
Install the price feeder script: npm install -g sophiatx-price-feeder
.
Once done, test it with:
feeder <YOUR_ACCOUNT_NAME> <YOUR_MINING_KEY>
, e.g. feeder <YOUR_ACCOUNT_NAME> 5J7ejuMu2ZkNWQ367X38wVvBWFzgbsjPvw6PLNtwsYEQrbhigT6
. Use your actual mining key, as entered into config.ini.
Once the script functionality is checked, run it regularly, at lease once per day. You can use e.g. crond service on Linux:
crontab
0 * * * * /usr/local/bin/feeder <YOUR_ACCOUNT_NAME> <YOUR_MINING_KEY>
<ctrl-D>
Setup NTP Time Synchronization
Suse
Ubuntu
Before installing ntpd, you should turn off timesyncd:
sudo timedatectl set-ntp no
Please install ntpd by executing following command:
sudo apt-get install ntp
ntpd will be started automatically after install. You can verify that everything is working by following command:
sudo ntpq -p
Example:
remote refid st t when poll reach delay offset jitter
==============================================================================
0.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
1.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
2.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
3.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
ntp.ubuntu.com .POOL. 16 p - 64 0 0.000 0.000 0.000
ec2-52-17-231-7 193.120.142.71 2 u - 64 1 0.483 -1.022 0.025
ec2-52-209-118- 89.101.218.6 2 u 1 64 1 0.679 -1.663 0.267
tshirt.heanet.i .PPS. 1 u - 64 1 3.800 -0.999 0.017
meg.magnet.ie 195.66.241.2 2 u - 64 1 1.549 -0.667 0.276
ec2-34-242-217- 89.101.218.6 2 u - 64 1 0.817 1.451 0.092
t1.time.ir2.yah 212.82.106.33 2 u 1 64 1 1.666 -0.939 0.005
mirror.datacent 192.36.144.22 2 u 1 64 1 48.287 -0.197 0.036
pugot.canonical 17.253.34.125 2 u - 64 1 11.398 -1.159 0.000
chilipepper.can 145.238.203.14 2 u 1 64 1 10.779 -1.549 0.000
How to start sync node
Download latest binaries
Go here and download latest binaries.
Extract the binaries
Please run this command to extract the binaries tar -xzf BINARY_NAME
Start a node
Please execute following command to run the node ./sophiatxd
and kill it (Ctrl + C)
Modify configuration
Now you need to modify config.ini file, to have proper full node. Execute following commands:
cd witness_node_data_dir/
nano config.ini
And add following configuration to your config.ini
plugin = condenser_api
plugin = chain_api
plugin = database_api
plugin = block_api
webserver-ws-endpoint = 0.0.0.0:8090
When you finished you can save document with Ctrl+x and then press Y to approve changes.
Rerun the node
Now go back and rerun the node
cd ..
./sophiatxd
Check if node is syncing
Open new terminal window and run wallet ./cli_wallet
when wallet is running set up a password for wallet and execute info command to check if node is syncing.
set_password YOUR_PASSWORD
unlock YOUR_PASSWORD
info
Output should look like something like this
{
"head_block_number": 22646,
"head_block_id": "00005876350c332f4009a37c55fc9147902b8eb1",
"time": "2018-07-26T07:15:36",
"current_witness": "initminer",
"current_supply": "349999999.780000 SPHTX",
"total_vesting_shares": "2250001.000000 VESTS",
"witness_required_vesting": "250000.000000 VESTS",
"maximum_block_size": 262144,
"current_aslot": 24312,
"recent_slots_filled": "340282366920938463463374607431768211455",
"participation_count": 128,
"last_irreversible_block_num": 22595,
"average_block_size": 0,
"witness_majority_version": "0.0.0",
"hardfork_version": "1.0.0",
"head_block_age": "2 seconds old",
"participation": "100.00000000000000000",
"median_sbd1_price": {
"base": "0.000000 SPHTX",
"quote": "0.000000 SPHTX"
},
"median_sbd2_price": {
"base": "0.000000 SPHTX",
"quote": "0.000000 SPHTX"
},
"median_sbd3_price": {
"base": "0.000000 SPHTX",
"quote": "0.000000 SPHTX"
},
"median_sbd4_price": {
"base": "0.000000 SPHTX",
"quote": "0.000000 SPHTX"
},
"median_sbd5_price": {
"base": "0.000000 SPHTX",
"quote": "0.000000 SPHTX"
},
"account_creation_fee": "0.050000 SPHTX"
}
You can rerun this command a few times and see the head_block_age is up to date
Setting up seednode
If you have followed all steps, now you can create a seednode and be official part of our network. To do that you just need to edit following line in config.ini .
p2p-endpoint = 0.0.0.0:60000
Please be sure, that your node is open for p2p traffic.
Multiparty Encryption
Multiparty encryption operates in a closed group of participants, managed by one admin. Multiparty messaging is implemented as an application on top of SophiaTX’s custom_json_operation
.
Use cases
Group creation
Group is created by assigning it a new random name (“group address”), a group key, and individually inviting initial participants to it. The invite is encrypted and readable only to the sender (admin) and recipient (new group member).
Group update
Group can be updated any time with new name and/or new group key. Whenever new participants are being added or deleted, the group key must be updated. Group name might be optionally updated. Regular updates of the group will prevent external observers to map group activity, i.e. the potential attacker might not even learn of the existence of the group, its lifetime, and/or members. The group updates are updated to the group address and encrypted using the group key, and thus readable only to group members.
Group messaging
Any member of the group can send message to the group address. This message is encrypted using the group key.
Participant list change
Whenever admin adds new group member(s), he updates the group key and sends invite to these new members. Updating the group key prevents new members to be able to read old messages. Whenever admin removes participants from the group, new group address and group key has to be generated. The new group key is encrypted in the way that only remaining participants can read it.
Group disband
Admin can discontinue the group by disbanding it. No new messages, updates or messages, can be sent to the group anymore.
Receiving group messages
Group participants can read the new group messages only if they know the actual group name (group address) and group key. To keep track, the group updates needs to be monitored and group parameters updated accordingly.
Message format
There are three basic types of messages:
- Group invites
- Group updates
- Group message
First two are so called system message. They don’t carry the actual communication between participants, just the various group parameters. The last one, group message, is carrying actual information between users.
Group invite
Group invite is sent from admin to individual user. The format of the JSON payload is as follows:
{
"type" : 0, //<aka "system message"
"operation_data" : {
"version" : 1, //<always 1
"type" : "add",
"new_group_name" : "<NEW GROUP NAME>",
"description" : "<A SHORT DESCRIPTION OF THE PURPOSE OF THE GROUP>",
"user_list" : ["<OPTIONAL LIST OF OTHER PARTICIPANTS>"],
"senders_pubkey" : "<ADMIN PUBLIC MEMO KEY>",
"new_key" : [["<RECIPIENT PUBLIC MEMO KEY>", "<GROUP KEY ENCRYPTED WITH SENDER-RECIPIENT SHARED SECRET>"]]
}
}
NOTE: Public memo keys are stored in the structure for references. In case one of the participants changes her memo key, one can easily find which key was used in construction of the shared secret.
This payload is then encrypted using sender-recipient shared secret, and packed into a JSON wrapper:
{
"sender": "<ADMIN PUBLIC MEMO KEY>",
"recipient": "<RECIPIENT PUBLIC MEMO KEY>",
"data" : "<HEX ENCODED BYTE ARRAY OF THE ENCRYPTED PAYLOAD>"
}
Group update
Group update is sent from admin to the actual group address. The format of the JSON payload is as follows:
{
"type" : 0, //<aka "system message"
"operation_data" : {
"version" : 1, //<always 1
"type" : "update",
"new_group_name" : "<NEW GROUP NAME>", //optional parameter, in case group name changes. Can be omitted.
"description" : "<A SHORT DESCRIPTION OF THE PURPOSE OF THE GROUP>", //optional parameter, in case group description changes. Can be omitted.
"user_list" : ["<OPTIONAL LIST OF OTHER PARTICIPANTS>"],
"senders_pubkey" : "<ADMIN PUBLIC MEMO KEY>",
"new_key" : [["SEE BELOW"]]
}
}
Would the group key change, the new key is encoded either with group key or with shared secrets for each individual group participant. The later must be used in case of deleting participants. The first case has then the following format:
"new_key" : [["SPH1111111111111111111111111111111114T1Anm", "<ENCRYPTED_GROUP_KEY>"]]
The second case will end in a list of encrypted keys:
"new_key" : [
["<RECIPIENT1 PUBLIC MEMO KEY>", "<GROUP KEY ENCRYPTED WITH SENDER-RECIPIENT1 SHARED SECRET>"],
["<RECIPIENT2 PUBLIC MEMO KEY>", "<GROUP KEY ENCRYPTED WITH SENDER-RECIPIENT2 SHARED SECRET>"],
(...)
["<RECIPIENT_N PUBLIC MEMO KEY>", "<GROUP KEY ENCRYPTED WITH SENDER-RECIPIENT_N SHARED SECRET>"]
]
This payload is then encrypted using group key, and packed into a JSON wrapper:
{
"iv": "<INITIAL VECTOR FOR AES>",
"data" : "<HEX ENCODED BYTE ARRAY OF THE ENCRYPTED PAYLOAD>"
}
Group message
Group update is sent from a group member to the actual group address.
{
"type" : 1,
"message_data" : "<HEX ENCODED COMMUNICATION>"
}
This payload is then encrypted using group key, and packed into a JSON wrapper:
{
"iv": "<INITIAL VECTOR FOR AES>",
"data" : "<HEX ENCODED BYTE ARRAY OF THE ENCRYPTED PAYLOAD>"
}
Encryption
As a base, AES256 is used.
Group key is of length 256 bits. Initialisation vector of length 256 bits is randomly generated and added as a parameter to the messages where group key encryption is used.
Shared secret between sender and recipient is derived from sender’s private and recipient’s public keys or recipient’s private and sender’s public keys using Elliptic curve integrated encryption scheme (ECIES) over SECP256K1 curve. The resulting 512 bits are split to halfs, one half used as IV and the other as key in AES256 schema.
API
The multiparty messaging plugin was developed as a reference implementation. Its usability is limited to be run on end-user’s node, and the node must not expose its API to general public. The plugin is started when the following parameters are added to the daemon when starting:
--plugin multiparty_messaging --mpm-app-id 2 --mpm-account <MYACCOUNT> --mpm-private-key <PRIVATE_MEMO_KEY>
NOTE: The mpm-account
and mpm-private-key
can be repeated more than once.
Once the plugin is started, it starts listening for custom_json_operations
with the given application ID and executes the logic as described above. It stores in database information about all past and active groups the user has been involved in, as well as all decoded messages.
The API calls can be split into two logical part:
- Retrieving the group and messages info from the database, namely
get_group
,get_group_name
,list_my_groups
andlist_messages
. - Preparing encrypted
custom_json_operations
payloads. The returned payloads has to be sent to the provided addresses either with Alexandria SDKs or cli_wallet.
Example
First create the group invitation by user initminer
using wscat:
> {"jsonrpc":"2.0", "id":1, "method":"multiparty_messaging.create_group", "params":{"admin":"initminer", "description":"blah", "members":["kpX6yHkAb_RIEuQ7g7UVoGwpUKo"]}}
< {"jsonrpc":"2.0","result":{"group_name":"wY8SqXZDuH3SL0Im0G5vuZOch6g","operation_payloads":[["kpX6yHkAb_RIEuQ7g7UVoGwpUKo",{"sender":"SPH8HWPUynRQnH8QCHdCvebNKbKGuVpZ9FZpg3EWv9UN5qayA8v7k","recipient":"SPH7t5298LP6CDuC8SHT7PGaH7rvqCYgV6sCsrT55VLJXEuQXWnPw","data":"396a3370a56a188403c1185b6ac98d69d01a2c42699a986da80f1d762e54671d82a9097983a228da8635d157d24c01bd9fe20ea0c1a390a6d6c7a247eea80ab3b6f4649c1dc1d3539307a36e9f525f8bab3416ccaf1bac26ee539ea0e289489a1f0261ac9a9f5a95317c3fe62bd49b3b0a5f85db3708eab406e6f181deb6a6fce851154f7ded1495befd308a33abe7002798f4a3d45adbb0c52a97175dbe14e76850d40b05d1147e4d070320051c6c46de600307e5ad59aa0290deb6368e92974a98047f96c5b70a7be005dd455c1557"}]]},"id":1}
Group name "wY8SqXZDuH3SL0Im0G5vuZOch6g"
was generated, as well as one invitation.
The returned invitation payload is then sent using cli_wallet:
unlocked >>> send_custom_json_document 2 initminer ["kpX6yHkAb_RIEuQ7g7UVoGwpUKo"] "{\"sender\":\"SPH8HWPUynRQnH8QCHdCvebNKbKGuVpZ9FZpg3EWv9UN5qayA8v7k\",\"recipient\":\"SPH7t5298LP6CDuC8SHT7PGaH7rvqCYgV6sCsrT55VLJXEuQXWnPw\",\"data\":\"396a3370a56a188403c1185b6ac98d69d01a2c42699a986da80f1d762e54671d82a9097983a228da8635d157d24c01bd9fe20ea0c1a390a6d6c7a247eea80ab3b6f4649c1dc1d3539307a36e9f525f8bab3416ccaf1bac26ee539ea0e289489a1f0261ac9a9f5a95317c3fe62bd49b3b0a5f85db3708eab406e6f181deb6a6fce851154f7ded1495befd308a33abe7002798f4a3d45adbb0c52a97175dbe14e76850d40b05d1147e4d070320051c6c46de600307e5ad59aa0290deb6368e92974a98047f96c5b70a7be005dd455c1557\"}" true
{
"ref_block_num": 2417,
"ref_block_prefix": 3747953573,
"expiration": "2018-12-13T13:34:00",
"operations": [[
"custom_json",{
"fee": "0.000000 SPHTX",
"sender": "initminer",
"recipients": [
"kpX6yHkAb_RIEuQ7g7UVoGwpUKo"
],
"app_id": 2,
"json": "{\"sender\":\"SPH8HWPUynRQnH8QCHdCvebNKbKGuVpZ9FZpg3EWv9UN5qayA8v7k\",\"recipient\":\"SPH7t5298LP6CDuC8SHT7PGaH7rvqCYgV6sCsrT55VLJXEuQXWnPw\",\"data\":\"396a3370a56a188403c1185b6ac98d69d01a2c42699a986da80f1d762e54671d82a9097983a228da8635d157d24c01bd9fe20ea0c1a390a6d6c7a247eea80ab3b6f4649c1dc1d3539307a36e9f525f8bab3416ccaf1bac26ee539ea0e289489a1f0261ac9a9f5a95317c3fe62bd49b3b0a5f85db3708eab406e6f181deb6a6fce851154f7ded1495befd308a33abe7002798f4a3d45adbb0c52a97175dbe14e76850d40b05d1147e4d070320051c6c46de600307e5ad59aa0290deb6368e92974a98047f96c5b70a7be005dd455c1557\"}"
}
]
],
"extensions": [],
"signatures": [
"1f6b1c7281e2d875aed831d9f117a1c65f84c5b806dcac88f395a9635247418a104a0c69b0935dcc848be8171c288ff56b8e3e944041c3e4000d93afb3d10bf8d1"
],
"transaction_id": "e76c37b71a3ca23ddc044edd2224880295a0bce0",
"block_num": 2418,
"transaction_num": 0
}
unlocked >>>
We can check success of the operation again with wscat:
> {"jsonrpc":"2.0", "id":2, "method":"multiparty_messaging.get_group", "params":{"group_name":"wY8SqXZDuH3SL0Im0G5vuZOch6g"}}
< {"jsonrpc":"2.0","result":{"group_name":"wY8SqXZDuH3SL0Im0G5vuZOch6g","current_group_name":"wY8SqXZDuH3SL0Im0G5vuZOch6g","description":"blah","members":["kpX6yHkAb_RIEuQ7g7UVoGwpUKo","initminer"],"admin":"initminer","group_key":"b019a8e962d111b020f0fd0181f3e59e6f7727076fce2486bc28cf8c0270a589","messages":1},"id":2}
Subscription API guide
Subscription API quick guide
Subscription API is accessible as one of the daemon APIs. In order to work, it has to be enabled, as well as dependant APIs with params
--plugin custom_api --plugin subscribe_api
Once it is enabled, WebSocket subscriptions can be used. The only call supported now is custom_object_subscription
. Example of usage is:
wscat --connect 127.0.0.1:8095
connected (press CTRL+C to quit)
> {"jsonrpc": "2.0", "method": "subscribe_api.custom_object_subscription", "params":{"return_id":22, "app_id":1, "account_name":"initminer", "search_type":"by_sender", "start":1}, "id": 1}
< {"jsonrpc":"2.0","result":1,"id":1}
(now the messages comes)
< {"method":"notice","params":[22,[2,{"id":3,"sender":"initminer","recipients":["initminer"],"app_id":1,"data":"{}","received":"2018-08-23T11:55:12","binary":false}]]}
Hello World via API calls
Hello World example
This is just simple example of sending Hello World message proper way in our blockchain via public API.
Creating custom json operation
To send Hello World via our API function to our blockchain, you need to fill this JSON structure. JSON struct:
[
"custom_json",{
"sender": "USERNAME", //actual user account on blockchain, that will sign this transaction
"recipients": [ //array of recipients, does not to be actuall account on blockchain, but you are free to used for your app logic
"MSG_BOX"
],
"app_id": 777, //App id of your app
"json": "{\"Hello World\"}" //escaped msg
}
]
Then you can use this curl commnad to send it to blockchain. private_key is private key of USERNAME
curl -v -s --data '{"id":0,"jsonrpc":"2.0","method":"alexandria_api.send_and_sign_operation", "params":{"op": ["custom_json",{"sender": "USERNAME", "recipients": [ "MSG_BOX" ],"app_id": 777, "json": "{ \"msg\" : \"Hello World\"}"}], "pk" : private_key}}' https://API_URL
resposne
{"jsonrpc":"2.0","result":{"signed_tx":{"ref_block_num":15641,"ref_block_prefix":973882209,"expiration":"2019-04-04T12:39:12","operations":[["custom_json",{"fee":"0.010000 SPHTX","sender":"user1","recipients":["MSG_BOX"],"app_id":777,"json":"{ \"msg\" :\"Hello World\"}"}]],"extensions":[],"signatures":["20044265aecd561bd3cf05bae0eab195698a16f5cba8eb8f69e930b4992be0144558437db2503fbbec748c29c8a667d44399e38d75e215138d1eb2ccd10c8f73f7"],"transaction_id":"418cf08ac9192aa309c31357d3caf0d3bc214362","block_num":81178,"transaction_num":0}},"id":0}
Checking message
curl -v -s --data '{"id":0,"jsonrpc":"2.0","method":"alexandria_api.get_received_documents", "params":{ "app_id": 777, "account_name": "USERNAME", "search_type": "by_sender_reverse", "start": "1", "count": 10 }}' https://API_URL
response:
{"jsonrpc":"2.0","result":{"received_documents":[[1,{"id":0,"sender":"user1","recipients":["MSG_BOX"],"app_id":777,"data":"{ \"msg\" :\"Hello World\"}","received":"2019-04-04T12:38:42","binary":false}]]},"id":0}
Hello World on SophiaTX
Hello World example
This is just simple example of sending Hello World message proper way in our blockchain with help of our alexandria_deamon
Creating account
First step is to create second account (recipient of message). For this step you need to have one account already created, if not please use our wallet.
Generating keys
New account requires new pair of keys, we use 3 different key_pairs in our blockchain, but for this simple example I will same key for all 3 different keys. So to generate key pair you need to execute this command
>>>generate_key_pair
response:
{
"pub_key": "SPH667jA4gwFxRkSpKFAyWTDbzydRF1JPQrDc4ReqtpeujwvVUDfQ",
"wif_priv_key": "5KdvUxpR2NTdQHV91qT9K7g8oHvNz6F6vyhnGDUNUC1KYpaYH9r"
}
Creating account
So now, when the key pair is ready I can create create_account operation
create_account registered_account test_account "{}" SPH667jA4gwFxRkSpKFAyWTDbzydRF1JPQrDc4ReqtpeujwvVUDfQ SPH667jA4gwFxRkSpKFAyWTDbzydRF1JPQrDc4ReqtpeujwvVUDfQ SPH667jA4gwFxRkSpKFAyWTDbzydRF1JPQrDc4ReqtpeujwvVUDfQ
response:
[
"account_create",{
"fee": "0.100000 SPHTX",
"creator": "4PMRaUBMJmL89eWcb1XrpL7DFGhJ",
"name_seed": "test_account",
"owner": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [[
"SPH667jA4gwFxRkSpKFAyWTDbzydRF1JPQrDc4ReqtpeujwvVUDfQ",
1
]
]
},
"active": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [[
"SPH667jA4gwFxRkSpKFAyWTDbzydRF1JPQrDc4ReqtpeujwvVUDfQ",
1
]
]
},
"memo_key": "SPH667jA4gwFxRkSpKFAyWTDbzydRF1JPQrDc4ReqtpeujwvVUDfQ",
"json_metadata": "{}"
}
]
Signing and broadcasting transaction
Now when operation is ready, we need to create transaction from it, sign it and broadcast to blockchain node. It is possible to use specific function for each of these steps, but there is one function that will do everything for us, but do not use this function on network, because it will expose your private key!
>>>send_and_sign_operation operation_from_last_step registered_account_private_key
Checking if account was created
Now we should check if account was successfully created and for that we can use function:
>>>get_account test_account
response:
{
"id": 3,
"name": "4PMRaUBMJmL89eWcb1XrpL7DFGhJ",
"owner": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [[
"SPH667jA4gwFxRkSpKFAyWTDbzydRF1JPQrDc4ReqtpeujwvVUDfQ",
1
]
]
},
"active": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [[
"SPH667jA4gwFxRkSpKFAyWTDbzydRF1JPQrDc4ReqtpeujwvVUDfQ",
1
]
]
},
"memo_key": "SPH667jA4gwFxRkSpKFAyWTDbzydRF1JPQrDc4ReqtpeujwvVUDfQ",
"json_metadata": "",
"voting_proxy": "11111111111111111111",
"balance": "0.000000 SPHTX",
"vesting_shares": "0.000000 VESTS",
"vesting_withdraw_rate": "0.000000 VESTS",
"to_withdraw": 0,
"witness_votes": [],
"sponsored_accounts": [],
"sponsoring_account": "11111111111111111111"
}
Creating application
Next step is to create application, that we will use for sending custom document
>>>create_application 4PMRaUBMJmL89eWcb1XrpL7DFGhJ TESTAPP "www.sophiatx.com" "" 0
result:
[
"application_create",{
"fee": "0.000000 SPHTX",
"author": "4PMRaUBMJmL89eWcb1XrpL7DFGhJ",
"name": "TESTAPP",
"url": "www.sophiatx.com",
"metadata": "",
"price_param": 0
}
]
Now you need to sign and broadcast it as in create account.
Checking if account was created
>>>get_applications ["TESTAPP"]
result:
[{
"id": 1,
"name": "TESTAPP",
"author": "4PMRaUBMJmL89eWcb1XrpL7DFGhJ",
"url": "www.sophiatx.com",
"metadata": "",
"price_param": "permanent"
}
]
Creating custom json operation
Now the final step sending message Hello world. For that we will use make_custom_json_operation.
make_custom_json_operation 1 GN3Ug8ou6tiE4kWvyopJBJmZgcw ["4PMRaUBMJmL89eWcb1XrpL7DFGhJ"] "{\"Hello World\"}"
result:
[
"custom_json",{
"fee": "0.000000 SPHTX",
"sender": "GN3Ug8ou6tiE4kWvyopJBJmZgcw",
"recipients": [
"4PMRaUBMJmL89eWcb1XrpL7DFGhJ"
],
"app_id": 1,
"json": "{\"Hello World\"}"
}
]
Now we need to sign and send it
Checking message
get_received_documents 1 GN3Ug8ou6tiE4kWvyopJBJmZgcw "by_sender" 10 1
result:
[[
1,{
"sender": "GN3Ug8ou6tiE4kWvyopJBJmZgcw",
"recipients": [
"4PMRaUBMJmL89eWcb1XrpL7DFGhJ"
],
"app_id": 1,
"data": "{\"Hello World\"}",
"received": "2018-07-10T11:37:42",
"binary": false
}
]
]
Understanding Dynamic Global Properties
Maintains global state information
Intro
Dynamic Global Properties represents a set of values that are calculated during normal chain operations and reflect the current values of global blockchain properties.
The API call returns an object containing information that changes every block interval such as the head block number, the total vesting fund, etc.
Sections
head_block_number
Block height at the head of the blockchain. This represents the latest block produced by witnesses.
- example:
24155032
head_block_id
Used to implement TaPoS (Transaction as Proof of Stake). The first 4 bytes (8 hex digits) of the block ID represents the block number. E.g., 01709398
in hex is 24155032
in decimal.
- example:
0170939865fa4e3aa7fca8f8df35d23333fe0bee
- see: RIPEMD-160 hashes
time
Point in time (UTC) that the block was included in the chain.
Used to synchronize events like Hard Fork activation.
When attempting to calculate the validity of a transaction we need to lookup a past block and check its block hash and the time it occurred so we can calculate whether the current transaction is valid and at what time it should expire.
For new transactions, expirations originate from this time.
- example:
2018-07-14T01:19:51
current_witness
Account name of the current witness.
- example:
blocktrades
current_supply
SPHTX currently in existence.
- example:
271546371.129 SPHTX
total_vesting_shares
VESTS that are invested in SPHTX.
- example:
390950506702.452773 SPHTX
witness_required_vesting
VESTS that are required to be invested in SPHTX to in order to become a witness.
- example:
300000.000000 SPHTX
maximum_block_size
Maximum block size is decided by the set of active witnesses which change every round. Each witness posts what they think the maximum size should be as part of their witness properties, the median size is chosen to be the maximum block size for the round.
Note: the minimum value for maximum_block_size
is defined by the protocol to prevent the network from getting stuck by witnesses attempting to set this too low.
- example:
65536
current_aslot
The current absolute slot number. Equal to the total number of slots since genesis. Also equal to the total number of missed slots plus head_block_number
.
- example:
24231997
recent_slots_filled
Used to compute witness participation.
- example:
340282366920938463463374607431768211455
last_irreversible_block_num
The latest block number that has been confirmed by two thirds of all block producers and is thus irreversible.
- example:
24155017
average_block_size
Removed
Average block size is updated every block to be: average_block_size = (99 * average_block_size + new_block_size) / 100
. This property is used to update the current_reserve_ratio
to maintain approximately * 50% or less utilization of network capacity.
- example:
9309
Not Covered
Fields not covered in this recipe are:
participation_count
Example Method Call
To retrieve the current results for alexandria_api.get_dynamic_global_properties
, we can retrieve the current state information using curl
:
curl -s --data '{"jsonrpc":"2.0", "method":"alexandria_api.get_dynamic_global_properties", "params":{}, "id":1}' https://API_URL
Example Output
{
"jsonrpc": "2.0",
"result": {
"properties": {
"head_block_number": 58019,
"head_block_id": "0000e2a396fb66a55ae156bf48ce26105b984826",
"time": "1970-01-01T00:00:00",
"current_witness": "initminer",
"current_supply": "350015744.660303 SPHTX",
"total_vesting_shares": "4169.375986 VESTS",
"witness_required_vesting": "250000.000000 VESTS",
"maximum_block_size": 262144,
"current_aslot": 7835941,
"recent_slots_filled": "340282366920938463463374607431768211455",
"participation_count": 128,
"last_irreversible_block_num": 57968,
"average_block_size": 49
}
},
"id": 1
}
Node Deploy script
Automatic node deploy
Deploy Script connects to the server and automatically deploys node binaries and stores previous version
Created file structure on server:
~ # home of specified user
├── ...
├── sophiatx_binaries # Binaries folder
│ ├── sophiatx_#<NUM>.tar.gz # SophiaTX archived binaries
│ ├── sophiatx_#<NUM>.tar.gz.old # Previous version of SophiaTX archived binaries
│ ├── sophiatxd # SophiaTX demon
│ └── sophia_app_data # Master data folder
│ ├── configs # Configs data folder
│ │ ├── testnet_config.ini # SophiaTX demon config file
│ │ ├── testnet_config.ini.old # Previous version of SophiaTX demon config file
│ │ └── ...
│ └── ...
└── ...
Usage
./deploy-node.sh -h host -u user [-s sourceUrl] [-r]
Arguments description:
-h host : host(server), where node binaries should be deployed into
-u user : user to be used to connect to the host. This user has to have id_rsa.pub imported on host
[-s sourceUrl] : sourceUrl from which are the the binaries downloaded. optional. In case it is not specified, latest binaries(devnet) are downloaded.
[-r] : replay-blockain flag. It is used when starting sophiatxd. optional
Requirements
For using automatic deploy script, you have to install Ansible on your machine:
$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:ansible/ansible
$ sudo apt-get update
$ sudo apt-get install ansible**
Custom plugins
External Plugins
External/runtime-loadable plugins provide a way for building custom applications with advanced functionality on top of blockchain core functionality.
Key Features
- Dynamically specify external plugins to load
- Plugins are developed as standalone shared libraries
- Automatically load dependent plugins in order
- Plugins can specify commandline arguments and configuration file options
- Separate configuration files for each plugin
Standard plugins vs External plugins
- Standard plugins are static libraries(.a), external plugins must be shared libraries(.so) + their names must contain suffix “_plugin”:
add_library( track_and_trace_plugin SHARED
source files...
)
- They both share the same interface(
plugin<T>, which is derived from abstract_plugin
), but external plugins must provide dll import interface on top of that. Format:
extern "C" BOOST_SYMBOL_EXPORT std::shared_ptr<abstract_plugin> get_plugin() {
return std::make_shared<track_and_trace_plugin>();
}
Configuration
In main configuration file are two parameters, which define list of external plugins:
# Directory containing external/runtime-loadable plugins binaries (absolute path or relative to the data-dir/)
external-plugins-dir = "external-plugins"
# External plugin(s) to enable, may be specified multiple times
external_plugin = track_and_trace
“external_plugin” parameter contains only plugin name. Binary file of such plugin must be stored in directory specified by “external-plugins-dir” parameter under following naming convetion:
lib<plugin name>_plugin.so -> example: "libtrack_and_trace_plugin.so"
Plugin-specific parameters are stored in separate configuration file in subdirectory “configs” of “data-dir” parameter under naming convention:
<plugin name>_plugin_config.ini -> example: "track_and_trace_plugin_config.ini"
Example of data-dir directory structure with external plugin:
Lets say program parameter “data-dir” is set to default value: “sophia_app_data”. This is how directory structure would look like:
sophiatx-binaries # directory with sophiaTX binaries
├── sophiatxd # SophiaTX demon
├── sophia_app_data # data-dir
│ ├── external-plugins # external plugins binaries directory
│ │ ├── libtrack_and_trace_plugin.so
│ │ └── ...
│ ├── configs # configuration files directory
│ │ ├── config.ini # main config
│ │ ├── track_and_trace_plugin_config.ini # track_and_trace plugin config
│ │ └── ...
│ ├── blockchain # blockchain directory
│ │ └── ...
│ ├── logs # logs directory
│ │ └── ...
│ └── p2p # p2p directory
│ └── ...
└── ...
Code samples:
External Plugin code template is the same as for standard plugins with two minor differencies mentioned in “Standard plugins vs External plugins” section. Standard plugin template can be found here.
Example of concrete implementation of external plugin (track_and_trace) can be found here.
Build instructions:
External plugin as well as all libraries that are linked must be build with -DBUILD_PIC flag to enable Position Independent Code.