Skip to content

1. How to Construct a Transaction

syuan100 edited this page Aug 27, 2021 · 7 revisions

The key to any Rosetta Implementation is how transactions are standardized through the notion of Operations. At a high level, Operations are Rosetta objects that have the option to contain a balance changing Amount, along with other metadata. A single transaction can have one or more Operations in a particular order to fully denote the intent of a particular transaction.

For example, a simple transfer transaction may contain two Operations:

  • One operation to deduct a specific amount from the sending account
  • Another operation to add a specific amount to the receiving account

While Operation objects have a standard schema, it is up to each implementation to define a set of Operation types that are able to denote various intents based on transaction types present in that blockchain. For example, in our Helium implementation, a simple payment_v2 transaction is denoted by a Debit operation of a specific amount and a corresponding Credit operation with the same amount.

"operations": [
    {
        "operation_identifier": {
            "index": 0
        },
        "type": "debit_op",
        "account": {
            "address": "..."
        },
        "amount": {
            "value": "-10000000",
            "currency": {
                "symbol": "HNT",
                "decimals": 8
            }
        },
        "metadata": {
            "debit_category": "payment"
        }
    },
    {
        "operation_identifier": {
            "index": 1
        },
        "type": "credit_op",
        "account": {
            "address": "..."
        },
        "amount": {
            "value": "10000000",
            "currency": {
                "symbol": "HNT",
                "decimals": 8
            }
        },
        "metadata": {
            "credit_category": "payment"
        }
    }
]

By having discrete low level operations, we can go further and construct a multi-payment version of payment_v2 by adding more Debit and Credit operations. In addition, this allows us to reuse Debit operations for other transactions such as staking, or reuse Credit operations for mining rewards. Lastly, we can build in guardrails while we parse to ensure that a set of Operations denote the proper intent (i.e. checking if a set of Operations for a payment_v2 transaction always have pairs of Debit and Credit transactions with the same amounts).

See how Operations can be combined to create specific Helium transactions here. (TBD)

Once we know what set of Operations we want to use to create a transaction, we are ready to begin our transaction creation journey.

Our first stop is preprocessing. When constructing a transaction, we often need information that can only be retrieved on-chain and online. In order to determine what information we need to retrieve, we first send our array of Operations to /construction/preprocess. This will return an options object that contains all the necessary information for us to gather the metadata we need.

Next, we submit the options object to /construction/metadata which will take our requests and fetch the metadata we need. For Helium, metadata will almost always mean the current chain_vars and the current nonce of the account sending the transaction.

Once we have our Operations and the necessary metadata, we have all the information needed to construct the unsigned transaction. By passing all the information to /construction/payloads, we will get the raw unsigned transaction and payloads in hex string format.

Step 4: Signing the payloads (Outside of Rosetta)

The generated payloads are then signed OUTSIDE of the Rosetta implementation. Rosetta never deals with private keys and the actual signing process for security purposes. Click here to see possible signing methods for Helium payloads. (WIP)

The signatures created from the signing process are then combined with the unsigned transaction using /construction/combine, which result in the raw signed transaction (represented in base64 for Helium).

Finally, the raw signed transaction can then be submitted to /construction/submit which will then broadcast the transaction to other peers and commit it to the blockchain. This endpoint will return a transaction hash that you can use to monitor the status of your pending transaction.