_images/transaction.png

Transactions

A transaction denotes a ledger state affecting actions issued by an account (holder). Transactions can be issued from any client program using an uplink-sdk to an uplink node’s RPC interface, or from an uplink node via the uplink CLI. Transactions are sent from the node to all validating nodes in the network. Transactions received by validating nodes are inserted into the node’s mempool in the order in which the node received the transactions (not by the transaction’s timestamp). When it is time for a validating node to construct a new block (determined by the previous block’s PoA Consensus parameters), the validating node attempts to apply transactions in the mempool to the current world state. Transactions that result in an error during world state application are discarded, whereas transactions that are valid are included in the newly generated block. Finally, transactions are applied to the world state on every node in the uplink network once the validating nodes achieve consensus and issue a Block message to the entire network. If the block is well formed and all transactions included are valid, the transactions are applied to the node’s world state and the new world state is saved to disk.

_images/Transactions.png

Transaction Contents

A transaction consists of several fields of data and can be divided into two distinct parts, the transaction body and the transaction header. Technically, the transaction header is part of the transaction body, but it stores most of the data determining the transaction type and how it affects the ledger. The Transaction body contains metadata relevant to the transaction header that would be redundant to include in the header.

Uniqueness

Transactions issued to Uplink must be verifiably unique; i.e. the SHA3_256 hash of the transaction should not be equal to the hash of any other transaction that exists in a block in the Uplink network. Since the uniqueness of a transaction is determined by the uniqueness of the SHA3_256 hash of all the transaction fields and not by the hash of the transaction header alone, transactions that specify identical updates to the ledger can still be verified as unique. Each transaction issued to the network must be _signed_ by an ECC private key; The invariant that each signature must be produced using a truly random number enforces that signatures of identical data using the same private key will yield unique signatures. As the data signature is included as a field of the transaction, transaction hashes will assuredly be unique even if the transaction’s header and issuer are identical to another transaction’s header and issuer.

Forgery

The necessity for enforcing transaction uniqueness in Uplink is to prevent transaction forgery. Without checking for transaction uniqueness when validating a transaction for inclusion within a block, transactions could be forged by a malicious Uplink node as simple as copying an entire transaction, byte by byte, and reissuing the transaction to the network allowing uplink nodes to issue transaction under the authorization of the owner of the private key that was used to sign the copied transaction. As all fields of the transaction would remain the same (including the signature field), the transaction would be validated and accepted as if the account specified in the issuer field of the transaction body authorized the transaction.

Ledger Value Addressing

Following from the fact that transaction uniqueness is enforced through transaction hash, a simple yet definitive way for identifying ledger values uniquely emerges; Assets and Contracts created on the ledger are assigned an address based on the hash of the transaction that issued their creation (Accounts use their own addressing scheme):

\[\text{address}_v \ = \ \texttt{base58}(\texttt{sha3}(\texttt{base16}(\texttt{sha3}(\text{transaction}))))\]

These addresses of these ledger values are used to uniquely identify values on the ledger. Since querying the ledger or issuing transactions operating over these ledger values is the main functionality Uplink provides to users, the raw hash of the transaction is encoded in base 16 format to distinguish the unique transaction identifier from a ledger value address; Ledger value addresses will be base 58 encoded SHA3_256 hashes, whereas transaction hashes (identifiers) will be base 16 encoded.

Transaction Body

header:The transaction header which includes the type of transaction and all relevant parameters
signature:An ECDSA signature of the TransactionHeader signed with the private key related to the public key referenced by the origin address.
origin:The origin address corresponding to the issuer of the transaction
timestamp:The time the transaction was created by the issuer
{
  "header": <TransactionHeader>,
  "signature": "AQEAAAAAAAAAIK_hbMij047BWAVD7lzmtZxtIvL1P4O1HStDs7yxHo3-AQEAAAAAAAAAILniBXBnaoPxnE_9yZ7WldG9vHtHckm34YUWHNj_n9FQ",
  "origin": "H1tbrEKWGpbPjSeG856kz2DjViCwMU3qTw3i1PqCLz65",
  "timestamp": 1231006505
}

Transaction Header

The transaction header encodes the type of the transaction (determining what stateful action should be applied to the world state) as well as the parameters necessary to perform this state change.

There are three main classes of transactions:

  • Account transactions
  • Asset transactions
  • Contract transactions

Accounts

  • CreateAccount
  • RevokeAccount

CreateAccount:

Creates a new account on the ledger.
pubKey:The public key derived from a user generated ECDSA private key, used to validate signatures signed by the new account being created.’
timezone:The timezone in which the user account was created.
metadata:The meta-data, a key/value store representing arbitrary information about the user account being created.
{
  "pubKey": <PublicKey>,
  "timezone": "EST/Eastern Standard Time",
  "metadata": {
                "company": "Adjoint Inc.",
                "employee: "Thomas Dietert"
              }
}

Failure:

AccountExists

The public key supplied references an account that already exists.

RevokeAccount:

Removes an account from the ledger state.
address:The address of the account to remove from the ledger.
{
    "address": <Address>
}

Failure:

AccountDoesNotExist

The address supplied does not reference an existing account.

Assets

Assets support three different transactions:

  • CreateAsset
  • RevokeAsset
  • TransferAsset
  • BindAsset

CreateAsset:

Issues a new asset on the ledger.
assetAddr:The address of the asset to create
assetName:The name of the asset to create
supply:The total amount of holdings that can ever be in circulation
ref:The real world currency reference of the asset
assetType:The way holdings of this asset should be interpreted.
{
    "assetAddr": <Address>
    "assetName": "MyAsset",
    "supply": 1234,
    "reference": USD,
    "assetType": Discrete
}

Failure:

AssetExists

The address derived from the CreateAsset parameters results in an address referencing an already existing asset.

RevokeAsset

Revokes an asset from the ledger such that it’s state can no longer be modified; I.e. circulate and transfer transactions cannot be issued on the asset, and all holdings are permanently revoked.
address:The address of the asset to revoke
{
    "address": <Address>
}

TransferAsset:

Transfers holdings of assets from one holder to another. The issuer of an asset must first issue a CirculateAsset transaction before transferring an amount of an asset to any other account.
assetAddr:The address of the asset of which holdings are being transferred
toAddr:The recipient (beneficiary) of the holdings transferred
balance:The amount to transfer (must be a positive value).
{
    "assetAddr": <Address>,
    "toAddr": <Address>,
    "balance": 42
}

Note: The issuer of a transaction is denoted in the transaction body; Thus for transfer transactions, the transferer is derived from the origin account which issued the transaction.

Failure

AssetDoesNotExist

The asset referenced by assetAddr does not exist.

SenderDoesNotExist

The sender account address does not reference an existing account.

ReceiverDoesNotExist

The reciever account address does not reference an existing account.

HolderDoesNotExist

The account issuing the transfer does not have any holdings of the asset.

InsufficientSupply

The issuer of the assset is attempting to allocate an amount greater than the supply of the asset.

InsufficientHoldings

The issuer of the transfer is attempting to transfer an amount greater than their current holdings.

CirculateAsset:

Transfers a specified amount of supply of an asset to the issuer of the asset’s holdings. This transaction must be issued before an issuer can transfer holdings to another account.
assetAddr:The address of the asset that’s supply is being circulated
amount:The amount of the asset to circulate (add to the issuer’s holdings)

Note: The issuer of a transacftionis denoted in the transaction body; Thus for circulate transactions, the circulator is derived from the origin account which issued the transaction. The account issuing the circulate transaction must match the issuer of the asset denoted in the asset body.

Failure:

AssetDoesNotExist

The asset referenced by assetAddr does not exist.

CirculatorIsNotIssuer

The account attempting to issue the transaction is not the issuer of the asset, and therefore is not permitted to circulate any of the asset’s supply.

InsufficientSupply

The issuer of the asset is attempting to circulate an amount more than the current remaining supply of the asset.

Contracts

Contract transactions are subdivided into three:

  • CreateContract
  • SyncLocal
  • CallContract

CreateContract

Deploys a contract onto the ledger.
address:The address of the contract to create (currently contract address derivation is not the same for both uplink and it’s sdks).
contract:A bytestring representing the text representation of a func smart contract; To be parsed and typechecked by nodes on receipt.
{
    "address": <Address>,
    "contract": "global int x = 0;\n\nsetX (int n) { x = n; }",
}

Failure

InvalidContract

If the contract field fails to be parse or typecheck as a valid FCL program.

ContractExists

If a contract with an address the same as the one supplied for the new contract being created already exists.

CallContract

Calls a method in a smart contract
address:The address of the contract
method:The name of the method to call
args:A list of arguments to provide to the method being called.
{
    "address": <Address>,
    "method": "setX",
    "args": [VInt 42]
}

Failure

ContractDoesNotExist

If the address of the contract supplied does not refer to an existing contract

MethodDoesNotExist

If the method name supplied does not reference a method in the contract to which the address supplied refers to.

InvalidCallArgType

If the type of an argument supplied does not match the type of an argument expected by the method.

EvalFail

If the method evaluation fails for any reason.