07_transactions

Andrew Beyond edited this page Nov 13, 2018 · 5 revisions

7. Transactions (deprecated, please, see the next chapter where transaction v2 described)

The change in the state of the blockchain occurs through transactions. Consider the following steps to create, sign, and send a financial transaction (sending money from one wallet to another).

  1. Calculate seq of transactions. This should be any non-repeating number (often such sequences are called nonce). Each subsequent seq should be greater than the previous one. Also, seq must be greater than the current seq for this wallet. When implementing library to access the API, it`s recommended to store seq on the client and independently increase it with each new transaction. The wallet keeps the seq of the last successful transaction. If the transaction fails, then the seq of wallet won`t be updated. However, you should remember that if you perform multiple transactions with the same seq, only one of the transactions will pass, and the rest will fail. It is good practice to use the current time in milliseconds (as an example of a constantly increasing counter) as seq.

  2. Form the transaction body. To do this, create an array: [ From, To, Amount, Currency, Timestamp, Seq, Json ]

Field title Description
From The sender address in the binary representation (8 bytes of binary data, in message pack data type bin 8, first byte 0xC4)
To The destination address in the binary representation (8 bytes of binary data, msgpack bin 8, 0xC4)
Amount Transfer amount (unsigned integer, data type msgpack: 0x00..0x7f or 0xcc .. 0xcf, ie from 8 to 64 bits, data types positive fixint, uint 8, uint 16, uint 32, uint 64)
Currency Currency for translation (line, data type msgpack fixstr, 0xa0 .. 0xbf)
Timestamp Time of transaction generation in milliseconds (unsigned 64-bit integer, data type msgpack uint 64, 0xcf)
Seq Seq of transactions, (unsigned integer, data type msgpack: 0x00..0x7f or 0xcc .. 0xcf, ie from 8 to 64 bits, data types positive fixint, uint 8, uint 16, uint 32, uint 64)positive fixint, uint 8, uint 16, uint 32, uint 64)
Json Additional transaction parameters. A row of arbitrary length must be either empty or filled with valid json. Data type msgpack: 0xa0..0xbf or 0xd9..0xdb, fixstr data types, str 8, str 16, str 32)

In the table, data types in terms of msgpack are given are not accidentally. Converting to the msgpack format, it's noticed that different implementations of this format fairly freely interpret the built-in types of a language. Since the transaction signature is calculated based on the transaction body converted to the message pack format, use of the proper data types is very important in the conversion. Otherwise, there won't be any signature when user sends the transaction sata to the server.

1. To generate the transaction signature at the beginning of the array in the previous step, add the transaction label to the row 'tx':

['Tx', From, To, Amount, Currency, Timestamp, Seq, Json]

Next, serialize the resulting array into the messagepack format. Attention! All numbers in the transaction body are serialized as integers. When an incorrect serialization in JavaScript was noticed, integers were serialized as a float type. To verify the correctness of the serialization, the correct data types are shown in the table above.

2. The sha256 hash is derived from the transaction body serialized in the message pack and is signed using a private wallet key. The signature is stored in an array of signatures. Each element of the signature array is an array of 2 elements: the public purse key in the format der, the computed signature:

[ [pub_key, sign] ]

In our example, the signature array consists of a single element.

3. We form the signed and packed transaction. To do this, we form an object (in some programming languages this data structure is called a hashmap or an associative array, in terms of msgpack this is a fixmap) of the following content:

"type" => row "tx",
"tx" => transaction body,
"sig" => signature array

This object is serialized to the message pack format, and then encoded in base64.

Example of generating a transaction

image8

The program in php language with the help of which this data was computed:

<?php
function der2pem($der_data, $type='PUBLIC KEY') {
    if ($type == 'EC PRIVATE KEY') {
        $der_data = hex2bin('302e0201010420') . $der_data . hex2bin('a00706052b8104000a');
    } else {
        $der_data = hex2bin('3036301006072a8648ce3d020106052b8104000a032200') . $der_data;
    }

    $pem = chunk_split(base64_encode($der_data), 64, "\n");
    $pem = "-----BEGIN ".$type."-----\n".
        trim($pem) .
        "\n-----END ".$type."-----\n";
    return $pem;
}

function encode_uint($uint) {
    if($uint<0) return false;
    if($uint<=127) return pack("C",$uint);
    if($uint<=255) return pack("CC",0xcc,$uint);
    if($uint<=65535) return pack("Cn",0xcd,$uint);
    if($uint<=4294967295) return pack("CN",0xce,$uint);
    return pack("CJ",0xcf,$uint);
}

function encode_str($str) {
    if(strlen($str)<=31) return pack("C",0xa0+strlen($str)).$str;
    if(strlen($str)<=255) return pack("CC",0xd9,strlen($str)).$str;
    if(strlen($str)<=65535) return pack("Cn",0xda,strlen($str)).$str;
    return pack("CN",0xdb,strlen($str)).$str;
}

function pack_txbody($arr) {
    if($arr[0]!="tx") return false;
    return "\x98\xa2tx\xc4\x08".$arr[1]."\xc4\x08".$arr[2].
        encode_uint($arr[3]).encode_str($arr[4]).encode_uint($arr[5]).
        encode_uint($arr[6]).encode_str($arr[7]);
}

$pub_key = hex2bin('027040667fc592b006c8f8b51275020bf0d09b9edfc2a0ac333330e8a4f2bc4d1a');
$priv_key = hex2bin('700CCF05349E75A46D17676AF81AA26B65C34119C00CE65BF304A6E2EED5ED03');

$priv_pem = der2pem($priv_key, 'EC PRIVATE KEY');
$pub_pem = der2pem($pub_key);

$priv_key_handle = openssl_pkey_get_private($priv_pem);
$pub_key_handle = openssl_pkey_get_public($pub_pem);

$from = hex2bin('800140000100000B'); // AA100000001677722742
$to = hex2bin('8001400000000011');   // AA100000000000001785
$amount = 20;
$currency = 'FTT';
$timestamp = time()*1000;
$seq = 5;
$extra_data = json_encode(['message' => 'test transaction']);

$transaction_body = [ $from, $to, $amount, $currency, $timestamp, $seq, $extra_data ];
$transaction_body4sign = [ 'tx', $from, $to, $amount, $currency, $timestamp, $seq, $extra_data ];
$packed_body4sign = pack_txbody($transaction_body4sign);

printf("packed body for sign: %s\n", bin2hex($packed_body4sign));

$hash4sign = hash('sha256', $packed_body4sign);
printf("hash for sign: %s\n", $hash4sign);

$sign_result = openssl_sign($packed_body4sign, $sign, $priv_key_handle, OPENSSL_ALGO_SHA256);
printf("sign: %s\n", bin2hex($sign));

$check_sign = openssl_verify($packed_body4sign, $sign, $pub_key_handle, OPENSSL_ALGO_SHA256);
printf("sign verify: %s\n", $check_sign);

$transaction = [
    'type' => 'tx',
    'tx' => $transaction_body,
    'sig' => [
        [ $pub_key, $sign ]
    ]
];

$transaction_packed = msgpack_pack($transaction);
printf("packed transaction: %s\n", bin2hex($transaction_packed));

$transaction_base64 = base64_encode($transaction_packed);
printf("transaction in base64: %s\n", $transaction_base64);

The signed and packed transaction (ie transaction in base64 in the previous example) should be sent to url /api/tx/new as an HTTP POST request. As the request body, you need to pass JSON of the following type:

{
    "tx": "..... packed and signed transaction in base64 ......"
}

Example of sending a transaction using curl:

curl http://127.0.0.1/api/tx/new -d '{"tx":"g6R0eXBlonR4onR4l6iAAUAAAQAAC6iAAUAAAAAAERSjRlRUzwAAAWUzb+HYBb57Im1lc3NhZ2UiOiJ0ZXN0IHRyYW5zYWN0aW9uIn2jc2lnkZLZIQJwQGZ/xZKwBsj4tRJ1Agvw0Jue38KgrDMzMOik8rxNGtlIMEYCIQCBJpdRQupaplq97SHHKXEkY3P8ixTlRYrWgrkIUwl5jgIhANvm+qXWu7tf8bJb/j6/DWD9SvJuX562MjI4X8TfA5gc"}'

From the answer to this query we are interested in the txid field, which contains the temporary transaction identifier. This temporary identifier remains in the node memory for 2 minutes, then it expires and ceases to be valid. By this temporary identifier, you can find out the status of the transaction.

To do this, you need to make a request to url /api/tx/status/{txid}. ({txid} needs to be replaced with a temporary transaction identifier. In the Response, we are interested in the res field. The possible values for this field are listed in the table:

The value of res Description
null The transaction has not shown in the block yet
{"res": "ok"} The transaction was included in the next block
Url Request Type Description of parameters
/api/tx/new POST Input parameters: {"tx": "packed and signed transaction"}
Response: {"txid": "153B7614F8051F79-3RGPJnQuajxy1r9zj5Jb9JUr4skE-6BC2"} - transaction identifier
/api/tx/status/{txid} GET input parameters: {txid} - transaction identifier received at call /api/tx/new
Response:
{"Res": null} - the transaction has not yet reached the block
{"Res": {"ok": true}} - the transaction hit the block
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.