Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Latest commit

 

History

History
245 lines (186 loc) · 12.9 KB

lip-0068.md

File metadata and controls

245 lines (186 loc) · 12.9 KB
LIP: 0068
Title: Define new transaction schema
Author: Grigorios Koumoutsos <[email protected]>
        Maxime Gagnebin <[email protected]>
Discussions-To: https://research.lisk.com/t/define-new-transaction-schema/348/15
Status: Active
Type: Standards Track
Created: 2022-05-31
Updated: 2024-01-04

Abstract

This LIP defines a new schema to be used to serialize transactions. The main change is to replace module and command identifiers by the corresponding names, which are of type string. This LIP also updates the terminology used for transaction and transaction properties.

Copyright

This LIP is licensed under the Creative Commons Zero 1.0 Universal.

Motivation

The Lisk protocol handles identifiers for transactions, modules, commands, and many more. In many of those cases such as modules and commands there is also a name property, which is of type string and is set to some intuitive value (e.g., "token" module, "transfer" command). In the current protocol, such objects are referenced using their identifiers and their names have an auxiliary role. This is not so convenient for users and developers, since they have to memorize the (non-intuitive) identifier values. Merging those two properties and identifying modules and commands using their name provides a much better user/developer experience.

Rationale

Simplifying Identification

The type identifiers in the Lisk protocol are of type uint32 or of type bytes. In both cases, they are set to a non-intuitive value which has to be memorized by users and developers. As the ecosystem grows and more functionalities are added, especially with the intoduction of interoperability, the number of values that have to be memorized gets quite large, making the identification system quite impractical (and potentially error-prone). On the other hand, for each component (modules, commands, events, etc.) there is a much more intuitive parameter: its name. Switching identifiers to those names (and removing the old identifiers) makes the whole user and developer experience easier and less error-prone.

Therefore, for the cases where removing the old identifiers does not introduce any significant challenge in the ecosystem, it is plausible to proceed to this switch and use names as identifiers. This is the case for modules, commands and events.

Changes Compared to LIP 0028

The previous transaction schema was defined in LIP 0028. Here we define the following changes:

New property names: All properties in the proposed transaction schema are equivalent to the ones defined in LIP 0028. The only changes are the replacement of identifiers by the corresponding names and the update of terminology according to the LIP 0069 (renaming a module asset to a command and a transaction asset property to params). Overall, moduleID is replaced by module, assetID is replaced by command and asset is renamed to params.

Serialization/Deserialization: Serialization and deserialization follow the same specifications already defined in LIP 0028; the resulting serialization is however different when the proposed transaction schema is used, due to the change of types for identifiers for module and command. For completeness we include the pseudocode below. The transaction ID is calculated in the same way as described in LIP 0028 (the SHA-256 hash of the serialized transaction object).

Signature Calculation: The signature calculation function defined in LIP 0028 is updated to incorporate message tags introduced in LIP 0037.

Specification

The transaction schema defined in LIP 0028 is superseded by the one defined below.

The params property must follow the schema corresponding to the (module, command) pair defined in the corresponding module; we call this schema paramsSchema.

Constants

Name Type Value Description
Global Constants
ED25519_PUBLIC_KEY_LENGTH uint32 32 The length of public keys.
ED25519_PRIVATE_KEY_LENGTH uint32 32 The length of private keys.
ED25519_SIGNATURE_LENGTH uint32 64 The length of signatures.
MESSAGE_TAG_TRANSACTION bytes "LSK_TX_" as ASCII-encoded literal Message tag for transaction signatures (see LIP 0037).
MIN_MODULE_NAME_LENGTH uint32 1 The minimum length of a string specifying the name of a module.
MAX_MODULE_NAME_LENGTH uint32 32 The maximum length of a string specifying the name of a module
MIN_COMMAND_NAME_LENGTH uint32 1 The minimum length of a string specifying the name of a command.
MAX_COMMAND_NAME_LENGTH uint32 32 The maximum length of a string specifying the name of a command.
Configurable Constants Mainchain Value
MAX_PARAMS_SIZE uint32 14 KiB (14*1024 bytes) The maximum allowed length of the transaction parameters.

Type Definition

Name Type Validation Description
SignatureEd25519 bytes Must be of length ED25519_SIGNATURE_LENGTH. Used for Ed25519 signatures.
PrivateKeyEd25519 bytes Must be of length ED25519_PRIVATE_KEY_LENGTH. Used for Ed25519 private keys.
Transaction object Must follow the transactionSchema schema. An object representing a non-serialized transaction.

JSON Schema

Transactions are serialized using transactionSchema given below.

transactionSchema = {
    "type": "object",
    "required": [
        "module",
        "command",
        "nonce",
        "fee",
        "senderPublicKey",
        "params",
        "signatures"
    ],
    "properties": {
        "module": {
            "dataType": "string",
            "minLength": MIN_MODULE_NAME_LENGTH,
            "maxLength": MAX_MODULE_NAME_LENGTH,
            "fieldNumber": 1
        },
        "command": {
            "dataType": "string",
            "minLength": MIN_COMMAND_NAME_LENGTH,
            "maxLength": MAX_COMMAND_NAME_LENGTH,
            "fieldNumber": 2
        },
        "nonce": {
            "dataType": "uint64",
            "fieldNumber": 3
        },
        "fee": {
            "dataType": "uint64",
            "fieldNumber": 4
        },
        "senderPublicKey": {
            "dataType": "bytes",
            "length": ED25519_PUBLIC_KEY_LENGTH,
            "fieldNumber": 5
        },
        "params": {
            "dataType": "bytes",
            "fieldNumber": 6
        },
        "signatures": {
            "dataType": "array",
            "items": {
                "dataType": "bytes",
                "length": ED25519_SIGNATURE_LENGTH
            },
            "fieldNumber": 7
        }
    }
}

Validation

For a transaction trs to be valid, it must satisfy the following:

  • trs must follow the transactionSchema.
  • trs.params is of length less than or equal to MAX_PARAMS_SIZE.
  • trs.module is an alphanumeric string, i.e., matches the regular expression "^[a-zA-Z0-9]*$".
  • trs.command is an alphanumeric string, i.e., matches the regular expression "^[a-zA-Z0-9]*$".

The checks above are performed in the static validation stage of the block processing.

Further, it is the responsibility of every module to check in the "Command Verification" hook of a command that the bytes in trs.params satisfy the parameter schema of the respective command.

Serialization

The serialization of an object of type Transaction is described in the following pseudocode.

def encodeTransaction(trs: Transaction) -> bytes:
    return encode(transactionSchema, trs)

Deserialization

Consider a binary message trsMsg, corresponding to a serialized transaction. The deserialization procedure is as follows:

def decodeTransaction(trsMsg: bytes) -> Transaction:
    return decode(transactionSchema, trsMsg)

Transaction Signature Calculation

The following function calculates the signature with secret key sk of a transaction trs for a chain identified by the given chainID.

def computeTransactionSignature(sk: PrivateKeyEd25519, trs: Transaction, chainID: bytes) -> SignatureEd25519:
    trs.signatures = []
    serializedTrs = encodeTransaction(trs)
    return signEd25519(sk, MESSAGE_TAG_TRANSACTION, chainID, serializedTrs)

By convention, the signatures property of the transaction object that is signed is required to be the empty array and therefore this property is set accordingly. Further note that the function signEd25519 is defined in LIP 0062.

Transaction Signature Validation

Signature validation is done using the verifySignatures function defined in LIP 0041 with the input parameter tag equal to MESSAGE_TAG_TRANSACTION.

Backwards Compatibility

This LIP results in a hard fork as nodes following the proposed protocol will reject transactions following the previous schema, and nodes following the previous protocol will reject transactions following the proposed schema.

Reference Implementation

https://github.com/LiskHQ/lisk-sdk/blob/v6.0.1/elements/lisk-chain/src/transaction.ts

Appendix

In this section, we present a serialization example for a token transfer transaction (defined in LIP 0051). To calculate the signature, we use the chain identifier: chainID = 0x00000000 and the tag: tag = "LSK_TX_".encode().

Transaction object to serialize:

myTrs = {
    "module": "token",
    "command": "transfer",
    "nonce": "5",
    "fee": "1216299416",
    "senderPublicKey": "43e59548e356f581251041dc922b8e27b7bc5fd37b33e7939422db82e29c9d73",
    "params": {
        "tokenID": "0000000000000000",
        "amount": "123986407700",
        "recipientAddress": "2ca4b4e9924547c48c04300b320be84e8cd81e4a",
        "data": "Odi et amo. Quare id faciam, fortasse requiris."
    },
    "signatures": [
        '7164221c518617704a0d41d945d5ae87d1af471e911be35988704eee82c45aef37078489685808ed4369aa892b09a3845e81c821f783e6d4519439774ba65603',
        '54bf7d19959d3f7d39fd8aec6874063b23ce95cfa2cc1a5e5b0fc98e3b6e122153de1933cd7661ef094e23ad459ff46b42ed267a9d56045c0b59fa1a8d4c6b0e'
    ]
}

Binary message without signatures (149 bytes):

0a05746f6b656e12087472616e736665721805209883fdc3042a2043e59548e356f581251041dc922b8e27b7bc5fd37b33e7939422db82e29c9d7332580a0800000000000000001094e2a9f1cd031a142ca4b4e9924547c48c04300b320be84e8cd81e4a222f4f646920657420616d6f2e2051756172652069642066616369616d2c20666f7274617373652072657175697269732e

Transaction ID:

b3517c097df5b267ec9e12bf77a0d07faf12a262aa1dc454abfc9903461ac716

First key pair:

private key = 4cf6720801a87c4f9a4f8269671bff116d9af98734cae22315155d357f8b8510
public key = 43e59548e356f581251041dc922b8e27b7bc5fd37b33e7939422db82e29c9d73

Second key pair:

private key = c6bb32474a51daf65478204cb7cb554e7dbb7f7d44def985db56c925fd3f0859
public key = 5f40d1f7a4e57ff921f5b06788877e85070f1f7bc382d293a43b79935048aed3