Create operations - PascalCoin/PascalCoin GitHub Wiki
How to create operations by external apps (third party)
Description
This will show the byte-to-byte creation of a valid transaction to be transferred to the PascalCoin network using "executeoperations" JSON-RPC call. The "executeoperations" needs a "rawoperations" param, that will show how to create it.
This example allows cold storage, because you will not need PascalCoin node to sign oprations.
We will need an available Node with JSON-RPC enabled in order to send/receive commands. The "Node" does not need to store private keys, but must be our connection to the PascalCoin network.
We will be able to create and sign an operation in any other language and sign out-off Pascal language
Data specifications
- All information, except strings or RAW data, are saved LITTLE ENDIAN, so for example an int32 = $01020304 is saved as 04030201 RAW data
- All money information is saved in a NON FRACTIONAL number, so allways in a Int64 number. 1 PASC = 10000 Int64 value. If money is 1.23 PASC, the Int64 value is 12300 (multiply by 10000) that is stored as 8 bytes little endian 0C30000000000000
RAWOPERATIONS
A Raw operations is the data storing operations. We will use RAWOPERATIONS for "executeoperations" calls to JSON-RPC The format is as follows:
- Count: 4 bytes
- For each "count":
- OpType : 2 bytes
- Protocol Used : 2 bytes (if
0
then assumes is a protocol 4 compatible, otherwise use 5 or current protocol version) - Specific OpType data: See below examples:
Operations
Each operation is defined by it's OpType
:
- Transaction:
OpType = 1
- Change key:
OpType = 2
- Recover funds:
OpType = 3
(Introduced on protocol v2) - List account for sale:
OpType = 4
- Delist account:
OpType = 5
- Buy account:
OpType = 6
- Change key signed:
OpType = 7
<-- this is allows that the signer of the operation is not the same than the target account to change key but must have same public key, this allow pay for a fee when account has no balance - Change account info:
OpType = 8
(Introduced on protocol v3) - MultiOperation:
OpType = 9
(Introduced on protocol v4) - Op Data:
OpType = 10
Operations specific data
Each operation type (opType) uses it's own method for creating the HASH that must be signed, and also its RAW data storage format
You will find Pascal definition at the source code
Function GetDigestToSign(current_protocol : Word) : TRawBytes;
- The
GetDigestToSign
returns the HASH to be signed
- The
function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
- The
SaveOpToStream
fills thestream
with the specific OpType data
- The
Examples
Create a Transaction and Sign
In order to create a valid transaction for the network, first step is create the HASH (RAW data) that must be signed. Once signed, this transaction will be ready for be transferred to the network.
The HASH RAW info is created like this: (You will find Pascal implementation at function TOpTransaction.GetDigestToSign(current_protocol : Word): TRawBytes;
)
- Sender account: 4 bytes (little endian)
- Sender n_operation: 4 bytes (little endian) <- Note that n_operation must be last account n_operation+1 (is a incremental value to avoid double spend)
- Target account: 4 bytes (little endian)
- Amount: 8 bytes (little endiant) <- Note that PascalCoin core works with non fractional values, and limit fractional part of PASC is 10000 (10k). A current 1 PASC = 10000
- Fee: 8 bytes (little endian) <- Same coin rule as amount
- Payload data: length(Payload) bytes stored as a RAW (left-to-right)
- 0000: Fixed 2 bytes to null value
- add a final 1 byte with OpType value. For Transaction, the value is $01
- RAW = SHA256( RAW )
Example: Create the HASH RAW value ready to sign
NOTE: The private keys, and values shown at this example are tested and works. Account 3700-88 wants to send 3.5 PASC to account 7890-83 with a payload data "EXAMPLE" non encrypted (public payload) Account 3700-88 private key type SECP_256k1 is:
- Private key:
37B799726961D231492823513F5686B3F7C7909DEFF20907D91BF4D24A356624
- Public key:
- x:
1F9462CA6FA8FB39DC309F2EF3A6EB8AE9B2538D4EB62055A316692CCEA1557F
- y:
42FFDCCF7ACEA4685EEC3C0A9F9B223636CF257902693933FB0D1EC14A6C519B
- x:
Using JSON-RPC getaccount we can see information about account 3700-88
{
"account":3700,
"enc_pubkey":"CA0220001F9462CA6FA8FB39DC309F2EF3A6EB8AE9B2538D4EB62055A316692CCEA1557F200042FFDCCF7ACEA4685EEC3C0A9F9B223636CF257902693933FB0D1EC14A6C519B",
"balance":80,
"n_operation":1,
"updated_b":33062,
"state":"normal",
"name":"",
"type":0}
Note: We ned to know the n_operation
field value
Digest RAW creation: (shown as an hexadecimal RAW)
740E0000
<- Sender account 3700 to hexadecimal stored as a little endian in 4 bytes02000000
<- Sender next n_operation value (previous was 1, next will be 2) stored as a little endian in 4 bytesD21E0000
<- Target account 7890 to hexadecimal stored as a little endian in 4 bytesB888000000000000
<- Amount 3.5 PASC in native value (*10000) = 35000 to hexadecimal stored as a little endian in 8 bytes0100000000000000
<- Fee 0.0001 PASC in native value (*10000) = 1 to hexadecimal stored as a little endian in 8 bytes4558414D504C45
<- Payload "EXAMPLE" value0000
<- CONSTANT VALUE 2 bytes to "null"01
<- OpType 1=Transaction 1 byte
Final HASH value to be signed in Protocol V4 is:
- HASH =
SHA256( 740E000002000000D21E0000B88800000000000001000000000000004558414D504C45000001 )
- HASH =
B8C2057F4BA187B7A29CC810DB56B66C7B9361FA64FD77BADC759DD21FF4ABE7
<- Digest to be signed!
SIGNATURE of HASH using private key 37B799726961D231492823513F5686B3F7C7909DEFF20907D91BF4D24A356624
<- Note: This is TESTNET Account 3700 private key
K = A235553C44D970D0FC4D0C6C1AF80330BF06E3B4A6C039A7B9E8A2B5D3722D1F
<- Not needed, only for testing purposes if training with this code
Result of signature
SIGNATURE.R = EFD5CBC12F6CC347ED55F26471E046CF59C87E099513F56F4F1DD49BDFA84C0E
SIGNATURE.S = 7BCB0D96A93202A9C6F11D90BFDCAB99F513C880C4888FECAC74D9B09618C06E
Save a signed transaction as RAW
(You will find Pascal implementation at function TOpTransaction.SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
)
01000000
<- Optype (1=Transaction) stored as a 4 bytes in little endian If this is a "Transaction" then...740E0000
<- Sender account 3700 to hexadecimal stored as a little endian in 4 bytes02000000
<- Sender next n_operation value (previous was 1, next will be 2) stored as a little endian in 4 bytesD21E0000
<- Target account 7890 to hexadecimal stored as a little endian in 4 bytesB888000000000000
<- Amount 3.5 PASC in native value (*10000) = 35000 to hexadecimal stored as a little endian in 8 bytes0100000000000000
<- Fee 0.0001 PASC in native value (*10000) = 1 to hexadecimal stored as a little endian in 8 bytes0700
<- Payload length (EXAMPLE = 7 bytes) storead in 2 bytes little endian4558414D504C45
<- Payload "EXAMPLE" value (7 bytes)000000000000
<- CONSTANT VALUE 6 bytes to "null"2000
<- Signature.R length (32 to hexadecimal = 0x20) stored as a 2 bytes little endianEFD5CBC12F6CC347ED55F26471E046CF59C87E099513F56F4F1DD49BDFA84C0E
<- SIGNATURE.R2000
<- Signature.S length (32 to hexadecimal = 0x20) stored as a 2 bytes little endian7BCB0D96A93202A9C6F11D90BFDCAB99F513C880C4888FECAC74D9B09618C06E
<- SIGNATURE.S
Create the RAWOPERATIONS
This is the final step. In this example we have a single RAW data containing a signed transaction from account 3700 to 7890
01000000
<- Operations count (1 for this example) stored as a little endian in 4 bytes- For each operation... (in this example only 1)
01000000
<- Optype (1=Transaction) stored as a 4 bytes in little endian ... and then fill with Signed transaction as RAW740E000002000000D21E0000B888000000000000010000000000000007004558414D504C450000000000002000EFD5CBC12F6CC347ED55F26471E046CF59C87E099513F56F4F1DD49BDFA84C0E20007BCB0D96A93202A9C6F11D90BFDCAB99F513C880C4888FECAC74D9B09618C06E
RAWOPERATIONS =
0100000001000000740E000002000000D21E0000B888000000000000010000000000000007004558414D504C450000000000002000EFD5CBC12F6CC347ED55F26471E046CF59C87E099513F56F4F1DD49BDFA84C0E20007BCB0D96A93202A9C6F11D90BFDCAB99F513C880C4888FECAC74D9B09618C06E
Send RAWOPERATIONS usign JSON-RPC node
Execute JSON-RPC call to "executeoperations" and param "rawoperations" the above value we get:
{"result":
[{"block":0,
"time":0,
"opblock":0,
"maturation":null,
"optype":1,
"subtype":11,
"account":3700,
"signer_account":3700,
"n_operation":2,
"senders":
[{"account":3700,"n_operation":2,"amount":-3.5001,"payload":"4558414D504C45"}],
"receivers":[{"account":7890,"amount":3.5,"payload":"4558414D504C45"}],
"changers":[],
"optxt":"Tx-Out 3,5000 PASC from 3700-88 to 7890-83",
"fee":-0.0001,
"amount":-3.5,
"payload":"4558414D504C45",
"balance":76.4999,
"sender_account":3700,
"dest_account":7890,
"ophash":"00000000740E00000200000054BB7CC4FC784B6877DA7E3D3181B9510CAB2340",
"old_ophash":""
}],
"id":100,
"jsonrpc":"2.0"
}
Note: Used this website for testing: https://javacardos.com/tools/ecdsa-sign-verify