SophiaTX Developers logo

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

Suse guide

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:

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:

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.

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.

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.

current_witness

Account name of the current witness.

current_supply

SPHTX currently in existence.

total_vesting_shares

VESTS that are invested in SPHTX.

witness_required_vesting

VESTS that are required to be invested in SPHTX to in order to become a witness.

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.

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.

recent_slots_filled

Used to compute witness participation.

last_irreversible_block_num

The latest block number that has been confirmed by two thirds of all block producers and is thus irreversible.

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.

Not Covered

Fields not covered in this recipe are:

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

Standard plugins vs External plugins

add_library( track_and_trace_plugin SHARED
             source files...
           )
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.