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
  • function SaveOpToStream(Stream: TStream; SaveExtendedData : Boolean): Boolean;
    • The SaveOpToStream fills the stream with the specific OpType data

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

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 bytes
  • 02000000 <- Sender next n_operation value (previous was 1, next will be 2) stored as a little endian in 4 bytes
  • D21E0000 <- Target account 7890 to hexadecimal stored as a little endian in 4 bytes
  • B888000000000000 <- Amount 3.5 PASC in native value (*10000) = 35000 to hexadecimal stored as a little endian in 8 bytes
  • 0100000000000000 <- Fee 0.0001 PASC in native value (*10000) = 1 to hexadecimal stored as a little endian in 8 bytes
  • 4558414D504C45 <- Payload "EXAMPLE" value
  • 0000 <- 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 bytes
  • 02000000 <- Sender next n_operation value (previous was 1, next will be 2) stored as a little endian in 4 bytes
  • D21E0000 <- Target account 7890 to hexadecimal stored as a little endian in 4 bytes
  • B888000000000000 <- Amount 3.5 PASC in native value (*10000) = 35000 to hexadecimal stored as a little endian in 8 bytes
  • 0100000000000000 <- Fee 0.0001 PASC in native value (*10000) = 1 to hexadecimal stored as a little endian in 8 bytes
  • 0700 <- Payload length (EXAMPLE = 7 bytes) storead in 2 bytes little endian
  • 4558414D504C45 <- 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 endian
  • EFD5CBC12F6CC347ED55F26471E046CF59C87E099513F56F4F1DD49BDFA84C0E <- SIGNATURE.R
  • 2000 <- Signature.S length (32 to hexadecimal = 0x20) stored as a 2 bytes little endian
  • 7BCB0D96A93202A9C6F11D90BFDCAB99F513C880C4888FECAC74D9B09618C06E <- 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 RAW
    • 740E000002000000D21E0000B888000000000000010000000000000007004558414D504C450000000000002000EFD5CBC12F6CC347ED55F26471E046CF59C87E099513F56F4F1DD49BDFA84C0E20007BCB0D96A93202A9C6F11D90BFDCAB99F513C880C4888FECAC74D9B09618C06E

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