Each operation in PascalCoin (transaction, buy account, etc.) holds a payload. This tech-tutorial will help you understand how to handle payloads with JSON RPC.
Each operation in PascalCoin (transaction, buy account, etc.) holds a payload that contains arbitrary data and can be defined when executing an operation.
It can hold values up to 2^8 - 1 bytes or can simply be left empty.
This is a powerful feature often misunderstood or simply not used at all. Exchanges for example use the payload to identify incoming transactions and can associate them with their users. Or an online shop can use the payload to identify a transaction and associate it with an order.
The possibilities are endless, but for now we will stick with explaining how the payload is constructed, what methods to decrypt/encrypt a payload are available and how one can use the JSON RPC API to create transactions with (un)encrypted payloads.
The format of the payload is a hex string (PascalCoin jargon: HexaString) without the commonly used prefix 0x
. The wallet does the job of transforming a string to a HexaString, but if you talk to the JSON RPC API directly, you'll need to encode the payload prior embedding it within an operation.
When transforming a string to a HexaString make sure you handle multi-byte characters. E.g. the character €
is transformed to E282AC
.
You also need to make sure that the length of the resulting HexaString is even (mod 2). If a byte results in a single hex value (e.g. TAB
=> 9
), please prepend an additional 0
(zero) to it => 09
.
There should be a method available in each popular programming language, but if not here is some pseudo code to the rescue.
Pseudo Code string to HexaString:
originalString
hexaString
foreach 8BitChar in originalString
byte = to-decimal(8BitChar)
hex = to-hex(8BitChar)
hex = pad-left(hex, length=2, '0')
append hex to hexaString
end foreach
Pseudo Code HexaString to string:
hexaString
originalString
foreach 2chars in hexaString
dec = to-decimal(2chars)
8BitChar = to-ascii(dec)
append 8BitChar to originalString
end foreach
We assume that you know about JSON RPC and the implementation in PascalCoin. If not, please visit http://www.jsonrpc.org and https://github.com/PascalCoin/PascalCoin/wiki/JSON-RPC-API to make yourself comfortable with it.
Each call to a API endpoint that creates an operation expects a payload value as well as the encryption method. The payload value for each request is always defined in the payload
field and is a HexaString. The encryption method is always defined in the payload_method
field and can contain 4 possible values:
none
- No encryption
No encryption will take place and the payload is added to the operation "as is". Everyone can read the payload.
dest
- Destination public key
The encryption will be executed through the destination public key. PascalCoin will take the public key that owns the receiving account (destination) and encrypts the payload.
The payload can only be decrypted by the owner of the public key (the one that also knows the private key). No-one else will be able to decrypt the value.
sender
- Sender public key
The encryption is done through the senders public key. PascalCoin will take the public key of the sender account and encrypts the payload.
The payload can only be decrypted by the owner of the public key (the one that also knows the private key). No-one else will be able to decrypt the value.
aes
- AES256 with password
The third and last encryption method is to use a password. PascalCoin will take the defined password to create an initialization vector (IV) and Key to encrypts the payload using AES256-CBC. The payload can be decrypted by everyone who knows the password.
The password used for the encryption is defined in the pwd
field of the JSON RPC request.
As said in the beginning of this article, the payload can contain up to 2^8 - 1 bytes. That's not entirely correct. When transforming a string to a HexaString, the length will be doubled. So the payload can hold a string of 2^9-2 (510) bytes.
The limit of 510 bytes must be taken into consideration when using one of the encryption methods above. An encrypted payload is always longer than the original payload and it must never exceed the allowed length. So a raw string with the length of 255 bytes can never be encrypted, as the resulting encrypted value will be larger than the allowed size.
The JSON RPC API contains a method that tries to decrypt a payload using either one or more passwords (each will be tested) or the private keys available in the wallet (make sure the wallet is unlocked). We say try because the operation object gives no clue if, and if, how a payload is encrypted.
Please have a look at the following examples on how to use this method. (https://github.com/PascalCoin/PascalCoin/wiki/JSON-RPC-API#payloaddecrypt)
In the following examples we will examine how to create an operation (in these examples always transactions) using the 4 available payload_method
methods and how we can possibly decrypt the payload of the operation. All operations will be executed from account 66033
to 55099
with an amount of 0.0001 PASC
and a fee of 0.0001 PASC
.
none
- No encryptionFor an operation with an unencrypted payload, we will set the payload_method
field to none
and the payload
field to a HexaString from the ASCII string test_no_encryption
(example). The HexaString for this payload is 746573745F6E6F5F656E6372797074696F6E
.
Our JSON RPC request object now looks like this.
{
"jsonrpc":"2.0",
"method":"sendto",
"params":{
"sender":66033,
"target":55099,
"amount":0.0001,
"fee":0.0001,
"payload":"746573745F6E6F5F656E6372797074696F6E",
"payload_method":"none"
},
"id":1
}
When executed against the API the resulting operation object looks like this:
{
"result":{
"block":0,
"time":0,
"opblock":-1,
"maturation":null,
"optype":1,
"subtype":11,
"account":66033,
"signer_account":66033,
"n_operation":39,
"senders":[
{
"account":66033,
"n_operation":39,
"amount":-0.0002,
"payload":"6E6F5F656E6372797074696F6E"
}
],
"receivers":[
{
"account":55099,
"amount":0.0001,
"payload":"6E6F5F656E6372797074696F6E"
}
],
"changers":[
],
"optxt":"Tx-Out 0.0001 PASC from 66033-39 to 55099-17",
"fee":-0.0001,
"amount":-0.0001,
"payload":"6E6F5F656E6372797074696F6E",
"balance":0,
"sender_account":66033,
"dest_account":55099,
"ophash":"00000000F101010027000000955F236B446F2A8C05ED17AF20B89C2DDE2EF235",
"old_ophash":""
},
"id":123,
"jsonrpc":"2.0"
}
As you can see in the result->senders->payload
and result->receivers->payload
you can already view the unencrypted HexaString we sent and we can simply transform the HexaString back to a string to see the payload.
dest
- Destination public keyTo send an operation where the payload can only be decrypted by the receiving party (in our case the owner of the account 55099
), we will set the payload_method
field to dest
and the payload
field to a HexaString from the ASCII string test_dest
(example). The HexaString for this payload is 746573745F64657374
.
Our JSON RPC request object now looks like this:
{
"jsonrpc":"2.0",
"method":"sendto",
"params":{
"sender":66033,
"target":55099,
"amount":0.0001,
"fee":0.0001,
"payload":"746573745F64657374",
"payload_method":"dest"
},
"id":1
}
When executed against the API the resulting operation object looks like this:
{
"result":{
"block":0,
"time":0,
"opblock":-1,
"maturation":null,
"optype":1,
"subtype":11,
"account":66033,
"signer_account":66033,
"n_operation":40,
"senders":[
{
"account":66033,
"n_operation":40,
"amount":-0.0002,
"payload": "2110090010000330C9D8CD35863BA22C3A53EEDFEEF48D1E63475A26BD5834FE24DE875F5961B7458A23597ABADBBD6908FCB3628F00FE88CFE74A3CE322720F648A5816E21B7A"
}
],
"receivers":[
{
"account":55099,
"amount":0.0001,
"payload": "2110090010000330C9D8CD35863BA22C3A53EEDFEEF48D1E63475A26BD5834FE24DE875F5961B7458A23597ABADBBD6908FCB3628F00FE88CFE74A3CE322720F648A5816E21B7A"
}
],
"changers":[
],
"optxt":"Tx-Out 0.0001 PASC from 66033-39 to 55099-17",
"fee":-0.0001,
"amount":-0.0001,
"payload": "2110090010000330C9D8CD35863BA22C3A53EEDFEEF48D1E63475A26BD5834FE24DE875F5961B7458A23597ABADBBD6908FCB3628F00FE88CFE74A3CE322720F648A5816E21B7A",
"balance":0,
"sender_account":66033,
"dest_account":55099,
"ophash":"00000000F1010100280000009502ABE63171DEFB2A0B8851318276F17682BE09",
"old_ophash":""
},
"id":1,
"jsonrpc":"2.0"
}
You will notice this: The payload in result->senders->payload
and result->receivers->payload
is much longer than the payload we used when creating the operation. This is because it was encrypted. This value can only be decrypted with the private key connected to the public key of the account 55099
- and no-one else.
The receiver can now use the payloaddecrypt
method to decrypted the value.
{
"jsonrpc":"2.0",
"method":"payloaddecrypt",
"params":{
"payload": "2110090010000330C9D8CD35863BA22C3A53EEDFEEF48D1E63475A26BD5834FE24DE875F5961B7458A23597ABADBBD6908FCB3628F00FE88CFE74A3CE322720F648A5816E21B7A"
},
"id":1
}
The response looks like this:
{
"result":{
"result":true,
"enc_payload": "2110090010000330C9D8CD35863BA22C3A53EEDFEEF48D1E63475A26BD5834FE24DE875F5961B7458A23597ABADBBD6908FCB3628F00FE88CFE74A3CE322720F648A5816E21B7A",
"unenc_payload":"test_dest",
"unenc_hexpayload":"746573745F64657374",
"payload_method":"key",
"enc_pubkey": "CA022000B24DE3565D6717799D78635DDB7D6847E219D2B4745CE6DA4A0681DF2D6F1ABE2000353C4C2AEBB552C11CDA8A4EA5F1082FC90FE0243DCE33F16CCDFB4F84F5A78F"
},
"id":1,
"jsonrpc":"2.0"
}
In the response you can 1. see the public key used to encrypt the payload (env_pubkey
) and 2. see the decrypted payload (unenc_payload
), which is test_dest
.
sender
- Sender public keyTo send an operation where the payload can only be decrypted by the sending party (in our case the owner of the account 66033
), we will set the payload_method
field to sender
and the payload
field to a HexaString from the ASCII string test_sender
. The HexaString for this payload is 746573745F73656E646572
.
Our JSON RPC request object now looks like this:
{
"jsonrpc":"2.0",
"method":"sendto",
"params":{
"sender":66033,
"target":55099,
"amount":0.0001,
"fee":0.0001,
"payload":"746573745F73656E646572",
"payload_method":"sender"
},
"id":1
}
When executed against the API the resulting operation object looks like this:
{
"result":{
"block":0,
"time":0,
"opblock":-1,
"maturation":null,
"optype":1,
"subtype":11,
"account":66033,
"signer_account":66033,
"n_operation":41,
"senders":[
{
"account":66033,
"n_operation":41,
"amount":-0.0002,
"payload": "21100B00100002784ABDC55F0C958DCEFF6FDD4BF70B55C809F0058E1CE381C5098B3613DFE7C65F20F57A2AE2ED05DE318107E17ED94BF9016A263086E360F891ADC85B20991C"
}
],
"receivers":[
{
"account":55099,
"amount":0.0001,
"payload": "21100B00100002784ABDC55F0C958DCEFF6FDD4BF70B55C809F0058E1CE381C5098B3613DFE7C65F20F57A2AE2ED05DE318107E17ED94BF9016A263086E360F891ADC85B20991C"
}
],
"changers":[
],
"optxt":"Tx-Out 0.0001 PASC from 66033-39 to 55099-17",
"fee":-0.0001,
"amount":-0.0001,
"payload": "21100B00100002784ABDC55F0C958DCEFF6FDD4BF70B55C809F0058E1CE381C5098B3613DFE7C65F20F57A2AE2ED05DE318107E17ED94BF9016A263086E360F891ADC85B20991C",
"balance":0,
"sender_account":66033,
"dest_account":55099,
"ophash":"00000000F101010029000000DB216409EF93B660F700FD4903172807766B8466",
"old_ophash":""
},
"id":1,
"jsonrpc":"2.0"
}
Again, you will notice that the payload in result->senders->payload
and result->receivers->payload
is much longer than the payload we used when creating the operation. This is because it was encrypted using the senders public key. This value can only be decrypted by the owning private key of the account 66033
- no-one else.
The sender can use the payloaddecrypt
method to decrypted the value.
{
"jsonrpc":"2.0",
"method":"payloaddecrypt",
"params":{
"payload": "21100B00100002784ABDC55F0C958DCEFF6FDD4BF70B55C809F0058E1CE381C5098B3613DFE7C65F20F57A2AE2ED05DE318107E17ED94BF9016A263086E360F891ADC85B20991C"
},
"id":1
}
The response looks like this:
{
"result":{
"result":true,
"enc_payload": "21100B00100002784ABDC55F0C958DCEFF6FDD4BF70B55C809F0058E1CE381C5098B3613DFE7C65F20F57A2AE2ED05DE318107E17ED94BF9016A263086E360F891ADC85B20991C",
"unenc_payload":"test_sender",
"unenc_hexpayload":"746573745F73656E646572",
"payload_method":"key",
"enc_pubkey": "CA022000B24DE3565D6717799D78635DDB7D6847E219D2B4745CE6DA4A0681DF2D6F1ABE2000353C4C2AEBB552C11CDA8A4EA5F1082FC90FE0243DCE33F16CCDFB4F84F5A78F"
},
"id":1,
"jsonrpc":"2.0"
}
In the response you can 1. see the public key used to encrypt the payload (enc_pubkey
) and 2. see the decrypted payload (unenc_payload
), which is test_sender
.
aes
- PasswordNow the last possibility. We will send an operation where the payload can only be decrypted with a password. We will set the payload_method
field to aes
and the payload
field to a HexaString from the ASCII string test_password
and the password to secret
. The HexaString for this payload is 746573745F70617373776F7264
.
Our JSON RPC request object now looks like this:
{
"jsonrpc":"2.0",
"method":"sendto",
"params":{
"sender":66033,
"target":55099,
"amount":0.0001,
"fee":0.0001,
"payload":"746573745F70617373776F7264",
"payload_method":"aes",
"pwd": "secret"
},
"id":1
}
When executed against the API the resulting operation object looks like this:
{
"result":{
"block":0,
"time":0,
"opblock":-1,
"maturation":null,
"optype":1,
"subtype":11,
"account":66033,
"signer_account":66033,
"n_operation":42,
"senders":[
{
"account":66033,
"n_operation":42,
"amount":-0.0002,
"payload":"53616C7465645F5FE3A381879577006B76C9B9B73ABFF4E79036006E45405398"
}
],
"receivers":[
{
"account":55099,
"amount":0.0001,
"payload":"53616C7465645F5FE3A381879577006B76C9B9B73ABFF4E79036006E45405398"
}
],
"changers":[
],
"optxt":"Tx-Out 0.0001 PASC from 66033-39 to 55099-17",
"fee":-0.0001,
"amount":-0.0001,
"payload":"53616C7465645F5FE3A381879577006B76C9B9B73ABFF4E79036006E45405398",
"balance":0,
"sender_account":66033,
"dest_account":55099,
"ophash":"00000000F10101002A000000596490B2C5D1019A992A3093BE25BA3103FA227E",
"old_ophash":""
},
"id":1,
"jsonrpc":"2.0"
}
The payload in the operation object is now encrypted using your password and AES256-CBC and it can only be decrypted if you know the password.
Anyone can use the payloaddecrypt
method to decrypt the value with a correct password.
{
"jsonrpc":"2.0",
"method":"payloaddecrypt",
"params":{
"payload": "21100B00100002784ABDC55F0C958DCEFF6FDD4BF70B55C809F0058E1CE381C5098B3613DFE7C65F20F57A2AE2ED05DE318107E17ED94BF9016A263086E360F891ADC85B20991C"
},
"id":1
}
The response looks like this:
{
"result":{
"result":true,
"enc_payload":"53616C7465645F5FE3A381879577006B76C9B9B73ABFF4E79036006E45405398",
"unenc_payload":"test_password",
"unenc_hexpayload":"746573745F70617373776F7264",
"payload_method":"pwd",
"pwd":"secret"
},
"id":123,
"jsonrpc":"2.0"
}
In the response you can see that the decryption was successful and the payload of the recently generated operation was test_password
.
If you are using payloads to identify your users in deposit transactions, you should support both public and encrypted payloads. This is because some users may encrypt their payload and others may not. For example, Poloniex withdrawals to your deposit address will always have encrypted payloads. However, deposits from wallet users may be public. In principle, it is up to the recipient to examine the payloads both in their raw public form and then in their decrypted form.
As a result, when processing user deposit transactions to your exchange, it is highly recommended that you:
First check if the Operation Payload matches your users deposit code. If it matches, then the deposit can be accepted otherwise proceed to (2).
Decrypt the payload using decryptpayload
method as shown in the above examples.
Repeat (1) using the decrypted payload code from (2). If it matches then deposit is accepted otherwise the deposit is unrecognised.
With this approach you can ensure deposits are never missed and that support requests are minimised
You know now that PascalCoin offers a variety of possibilities to encrypt the payload of operations and depending on the use case, one or the other method makes sense.
sender
if you want to make sure only the sender can decrypt the payload.dest
if you want to make sure only the receiver can decrypt the payload.aes
if you want to make sure only parties that know the password can decrypt the payload.none
if you don't care about encryption at all and everyone should be able to read the payload.To decrypt a payload the following way is advised:
Take the payload as is, transform the HexaString to a string and see if the value makes sense. "Makes sense" means: If you need the payload to react based on it, you should know the format of the resulting string, so you should be able to say if it makes sense.
If the resulting string does not make sense, try to decrypt the value using the payloaddecrypt
RPC API and a possible set of passwords. PascalCoin will try to decrypt the payload with the available private keys and the possible passwords.
If everything fails, the payload got encrypted
sender
and you are not the senderdest
and you are not the owner of the receiver.aes
and you used the wrong passwordsOr.. there is a misunderstanding and the sender used the wrong encryption method.
Happy Coding!