Universal Blocks

TOP

All new transactions on the Nano Protocol are communicated in a data format known as Universal Blocks (State Blocks). The account's entire state - including the balance after each transaction - is recorded in every State Block. Transaction amounts are interpreted as the difference in balance between consecutive blocks.

If an account balance decreases, the transaction that caused the decrease is considered a Send. Similarly, if an account balance increases, the transaction that caused the increase is considered a Receive.


Because final balances are recorded rather than transaction amounts, API calls must be done carefully to avoid sending erroneous amounts.

Previously final balances were only recorded within "send" transactions while intermediate account balances had to be explicitly computed and cached.


The "state" block type combines all 4 of the legacy block types into a single block. Because of this, the "type" of the block is always "state".

Key Type Description
type constant "state"
previous 32-Byte HEX Previous head block on account; 0 if *open* block
link 32-Byte HEX Multipurpose Field - See Link Table below
representative String Representative xrb_ address
account String This account's xrb_ address
balance Decimal String in raw Resulting balance
work 8-Byte HEX Proof of Work Nonce
signature 64-Byte HEX ED25519+Blake2b 512-bit signature

Depending on transaction intent, the "link" field is multipurpose for block_create rpc calls:

Tx Type Type Description Example
Open HEX Pairing Send Block's Hash F076D8F6254F089A8E66D0C934FA63D927F4458FC1D96815066D83B3658ABA26
Change HEX Must be 0 0000000000000000000000000000000000000000000000000000000000000000
Send STR Destination "xrb_" address xrb_1utx843j4hgac1ixbtdygpayqcqho35oy3ufkfp19pj4rdb3sezqt975f8ae
Receive HEX Pairing Send Block's Hash F076D8F6254F089A8E66D0C934FA63D927F4458FC1D96815066D83B3658ABA26

Any transaction may also simultaneously change the representative; the above description is for an explicit representative change block where no funds are transferred.

In the completed, signed transaction json, the "link" field is always hexadecimal.

State Blocks began deployment in v11 of the rai_node software. The update procedure was conducted via two canary blocks:

Function Hash
State-Block Parsing 89F1C0AC4C5AD23964AB880571E3EA67FDC41BD11AB20E67F0A29CF94CD4E24A
State-Block Generation B6DC4D64801BEC7D81DAA086A5733D251E8CBA0E9226FD6173D97C0569EC2998

Below are a few key points to remember:

  • State Blocks and Universal Blocks are synonymous.
  • Once State Blocks are used on an account-chain, legacy blocks can no longer be added.
  • State Blocks can still interact (e.g receive funds) with legacy blocks.

Self-Signed Blocks

If you choose to implement your own signing, the order of data (in bytes) to hash prior to signing is as follows.

  • The State Block Preamble is 32 bytes and has value 0x6.

  • All values are binary representations

  • No ASCII/UTF-8 strings.

  • State Block Preamble (32-Bytes)

  • account (32-Bytes)

  • previous (32-Bytes)

  • representative (32-Bytes)

  • balance (16-Bytes)

  • link (32-Bytes)

The digital signing algorithm (which internally applies another Blake2b hashing) is applied on the resulting digest. Make sure that your private key uses the correct partnering public key while signing as using an incorrect public key may leak information about your private key.

Examples

Lets take a look through a few examples of using Universal Blocks in practice.

Example 1: Open

Scenario

  • Address sends 1 rawraw to account xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php.
  • Receiving address does not have an "open" block yet.
  • Send block hash is 1EF0AD02257987B48030CC8D38511D3B2511672F33AF115AD09E18A86A8355A8.

Response

  • Creates an "open" block for xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php.
  • Set xrb_3p1asma84n8k84joneka776q4egm5wwru3suho9wjsfyuem8j95b3c78nw8j as the representative.
  • Receives the mentioned block.
curl -d '{
  "action":"block_create",
  "type":"state",
  "previous":"0",
  "account":"xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php",
  "representative":"xrb_3p1asma84n8k84joneka776q4egm5wwru3suho9wjsfyuem8j95b3c78nw8j",
  "balance":"1",
  "link":"1EF0AD02257987B48030CC8D38511D3B2511672F33AF115AD09E18A86A8355A8",
  "wallet":"557832FF41BAF4860ED4D7023E9ACE74F1427C3F8232B6AFFB491D98DD0EA1A2"
}' http://127.0.0.1:7076
{
  "hash": "FC5A7FB777110A858052468D448B2DF22B648943C097C0608D1E2341007438B0",
  "block": "{\n    \"type\": \"state\",\n    \"account\": \"xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php\",\n    \"previous\": \"0000000000000000000000000000000000000000000000000000000000000000\",\n    \"representative\": \"xrb_3p1asma84n8k84joneka776q4egm5wwru3suho9wjsfyuem8j95b3c78nw8j\",\n    \"balance\": \"1\",\n    \"link\": \"1EF0AD02257987B48030CC8D38511D3B2511672F33AF115AD09E18A86A8355A8\",\n    \"link_as_account\": \"xrb_19qion34cye9pk153m6f93ajtgs747mkyexh47ff39iro3oa8ofa43o4zwx4\",\n    \"signature\": \"593D865DDCC6018F197C0EACD15E5CED3DAF134EDFAF6553DB9C1D0E11DBDCBBE1B01E1A4C6D4378289567E59BA122DA5BFD49729AA6C2B0FC9E592A546B4F09\",\n    \"work\": \"0000000000000000\"\n}\n"
}

The "balance" field is in rawraw format. For more information, see units.

Note the field "link_as_account". This is if the "link" field were to be interpreted as a 256-bit public key and translated into an "xrb_..." address. This field is only provided for convenience and is stripped away before it is broadcast to the network.

If you are creating and signing your own blocks external to rai_node, you do not need to include a "link_as_account" field.


Example 2: Receive

Scenario

  • 5 nanonano is sent to xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php
  • Send block hash is B2EC73C1F503F47E051AD72ECB512C63BA8E1A0ACC2CEE4EA9A22FE1CBDB693F

Response

Our receive block on the account-chain would be handled like so.

curl -d '{
  "action":"block_create",
  "type":"state",
  "previous":"FC5A7FB777110A858052468D448B2DF22B648943C097C0608D1E2341007438B0",
  "account":"xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php",
  "representative":"xrb_3p1asma84n8k84joneka776q4egm5wwru3suho9wjsfyuem8j95b3c78nw8j",
  "balance":"5000000000000000000000000000001",
  "link":"B2EC73C1F503F47E051AD72ECB512C63BA8E1A0ACC2CEE4EA9A22FE1CBDB693F",
  "wallet":"557832FF41BAF4860ED4D7023E9ACE74F1427C3F8232B6AFFB491D98DD0EA1A2"
}' http://127.0.0.1:7076
{
  "hash": "597395E83BD04DF8EF30AF04234EAAFE0606A883CF4AEAD2DB8196AAF5C4444F",
  "block": "{\n    \"type\": \"state\",\n    \"account\": \"xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php\",\n    \"previous\": \"FC5A7FB777110A858052468D448B2DF22B648943C097C0608D1E2341007438B0\",\n    \"representative\": \"xrb_3p1asma84n8k84joneka776q4egm5wwru3suho9wjsfyuem8j95b3c78nw8j\",\n    \"balance\": \"5000000000000000000000000000001\",\n    \"link\": \"B2EC73C1F503F47E051AD72ECB512C63BA8E1A0ACC2CEE4EA9A22FE1CBDB693F\",\n    \"link_as_account\": \"xrb_3eqegh1zc1znhr4joosgsfakrrxtjrf1om3exs9cmajhw97xptbzi3kfba1j\",\n    \"signature\": \"90CBD62F5466E35DB3BFE5EFDBC6283BD30C0591A3787C9458D11F2AF6188E45E6E71B5F4A8E3598B1C80080D6024867878E355161AD1935CD757477991D3B0B\",\n    \"work\": \"0000000000000000\"\n}\n"
}

Here the balance is 5000000000000000000000000000001 rawraw because we originally had 1 rawraw in our account and now we are adding 5000000000000000000000000000000 (5 nanonano) to it.


Example 3: Send

Scenario

  • We will be sending from our account ­xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php.
  • We want to send 2 nanonano to another account.
  • We want to send to account xrb_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p.

Response

curl -d '{
  "action":"block_create",
  "type":"state",
  "previous":"597395E83BD04DF8EF30AF04234EAAFE0606A883CF4AEAD2DB8196AAF5C4444F",
  "account":"xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php",
  "representative":"xrb_3p1asma84n8k84joneka776q4egm5wwru3suho9wjsfyuem8j95b3c78nw8j",
  "balance":"3000000000000000000000000000001",
  "link":"xrb_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p",
  "wallet":"557832FF41BAF4860ED4D7023E9ACE74F1427C3F8232B6AFFB491D98DD0EA1A2"
}' http://127.0.0.1:7076
{
  "hash": "128106287002E595F479ACD615C818117FCB3860EC112670557A2467386249D4",
  "block": "{\n    \"type\": \"state\",\n    \"account\": \"xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php\",\n    \"previous\": \"597395E83BD04DF8EF30AF04234EAAFE0606A883CF4AEAD2DB8196AAF5C4444F\",\n    \"representative\": \"xrb_3p1asma84n8k84joneka776q4egm5wwru3suho9wjsfyuem8j95b3c78nw8j\",\n    \"balance\": \"3000000000000000000000000000001\",\n    \"link\": \"5C2FBB148E006A8E8BA7A75DD86C9FE00C83F5FFDBFD76EAA09531071436B6AF\",\n    \"link_as_account\": \"xrb_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p\",\n    \"signature\": \"D7975EE2F6FAE1FC7DA336FB9DD5F7E30FC1A6825021194E614F0588073D1A4901E34E3CAE8739F1DE2FD85A73D2A0B26F8BE6539E0548C9A45E1C1887BFFC05\",\n    \"work\": \"0000000000000000\"\n}\n"
}

Because the account balance changed from 5000000000000000000000000000001 rawraw to 3000000000000000000000000000001 rawraw, the block is interpreted as a send. The "link" field is populated with the public key of the account we are sending to.


Example 4: Change

Scenario

  • We want to change the representative of our account to be xrb_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs

Response

curl -d '{
  "action":"block_create",
  "type":"state",
  "previous":"128106287002E595F479ACD615C818117FCB3860EC112670557A2467386249D4",
  "account":"xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php",
  "representative":"xrb_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs",
  "balance":"3000000000000000000000000000001",
  "link":"0000000000000000000000000000000000000000000000000000000000000000",
  "wallet":"557832FF41BAF4860ED4D7023E9ACE74F1427C3F8232B6AFFB491D98DD0EA1A2"
}' http://127.0.0.1:7076
{
  "hash": "2A322FD5ACAF50C057A8CF5200A000CF1193494C79C786B579E0B4A7D10E5A1E",
  "block": "{\n    \"type\": \"state\",\n    \"account\": \"xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php\",\n    \"previous\": \"128106287002E595F479ACD615C818117FCB3860EC112670557A2467386249D4\",\n    \"representative\": \"xrb_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs\",\n    \"balance\": \"3000000000000000000000000000001\",\n    \"link\": \"0000000000000000000000000000000000000000000000000000000000000000\",\n    \"link_as_account\": \"xrb_1111111111111111111111111111111111111111111111111111hifc8npp\",\n    \"signature\": \"7E9A7B368DBEB280B01C22633DC82F6CEF00F529E07B76A0232614D2BCDAF85BF52AC9DA4DBE4468B6F144CE82F2FDE44080C8363F903A6EC3D999252CB1E801\",\n    \"work\": \"0000000000000000\"\n}\n"
}

Note that the ""link" field is all 0's. As another sanity check, we notice the all 0 public key gets translated into the burn address xrb_1111111111111111111111111111111111111111111111111111hifc8npp


Example 5: Change & Send

Scenario

  • We want to change our representative at the same time we perform a send or receive.
  • We want to send 3 more nanonano to account xrb_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p
  • We want to revert our representative back to xrb_3p1asma84n8k84joneka776q4egm5wwru3suho9wjsfyuem8j95b3c78nw8j

Response

curl -d '{
  "action":"block_create",
  "type":"state",
  "previous":"2A322FD5ACAF50C057A8CF5200A000CF1193494C79C786B579E0B4A7D10E5A1E",
  "account":"xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php",
  "representative":"xrb_3p1asma84n8k84joneka776q4egm5wwru3suho9wjsfyuem8j95b3c78nw8j",
  "balance":"1",
  "link":"xrb_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p",
  "wallet":"557832FF41BAF4860ED4D7023E9ACE74F1427C3F8232B6AFFB491D98DD0EA1A2"
}' http://127.0.0.1:7076
{
  "hash": "9664412A834F0C27056C7BC4A363FBAE86DF8EF51341A5A5EA14061727AE519F",
  "block": "{\n    \"type\": \"state\",\n    \"account\": \"xrb_3igf8hd4sjshoibbbkeitmgkp1o6ug4xads43j6e4gqkj5xk5o83j8ja9php\",\n    \"previous\": \"2A322FD5ACAF50C057A8CF5200A000CF1193494C79C786B579E0B4A7D10E5A1E\",\n    \"representative\": \"xrb_3p1asma84n8k84joneka776q4egm5wwru3suho9wjsfyuem8j95b3c78nw8j\",\n    \"balance\": \"1\",\n    \"link\": \"5C2FBB148E006A8E8BA7A75DD86C9FE00C83F5FFDBFD76EAA09531071436B6AF\",\n    \"link_as_account\": \"xrb_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p\",\n    \"signature\": \"4D388F982188E337D22E0E66CD24BCABD09BED1E920940C453039B55B6A4724D7BD106019AACC1840480938FF4FA024F041E6E6A32B3641C28E0262025020B03\",\n    \"work\": \"0000000000000000\"\n}\n"
}