-
Notifications
You must be signed in to change notification settings - Fork 0
07_transactions
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).
-
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.
-
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
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 |