The Accounts Model
In this section, learn how the Accounts model affects how transactions are formatted, how the current state is structured and how smart contracts are written.
Overview​
- 2.8 Accounts Transaction Structure
- 2.9 Read your First Accounts Transaction
- 2.10 Accounts State
- 2.11 Read your First Accounts State
- 2.12 User Defined Code in Accounts Transactions
[2.8] Accounts Transaction Structure​
To modify a distributed ledger that is tracking asset ownership via an accounts data structure, you will need an account formatted transaction. This type of transaction contains at least the following sections.
Transaction Origin
The transaction origin is the main account linked to this transaction.
Due to the flexibility of the accounts-based DLT model, we cannot say much more on the transaction origin without warning the reader that the following may not always be the case.
That said, the transaction origin is usually the account which will be debited any assets sent via this transaction. The transaction origin is also usually the same account as the signer of the transaction.
Transaction Destination
This parameter indicates the account that will receive assets from this transaction and/or the account whose smart contract code is being triggered.
Usually in the accounts model there is only one transaction destination, but again this is not a definite rule. For instance, transactions using the Stellar DLT can declare multiple destinations.
Transaction Sequence
A transaction sequence number (also referred to as the transaction nonce) is a unique value that has not been used before in a transaction from this origin account. It must be changed with every new transaction sent from this account. Each transaction must change the sequence so that the same transaction cannot be rebroadcast multiple times (known as a replay attack).
Usually the sequence value is set equal to the number of transactions sent from the origin account. But again this is not a definite rule. For instance, on the XRP ledger, an account starts with a sequence number equal to the block number of when they received their first transaction. Regardless of what value the sequence number starts at, the standard is for the sequence number to be monotonically increasing.
Transaction Type
In accounts based DLTs, each transaction has an explicit and/or implicit type. For instance, the XRP Ledger has a set of explicit transaction types, such as ‘payment’ (to send assets), ‘escrow create’ (to lock assets until a certain condition is met) and ‘escrow finish’ (to unlock escrowed assets).
Ethereum on the other hand has a smaller number of explicit transaction types, such as ‘legacy’ and ‘eip-1559’. The main difference between these two transaction types is that the transaction creator has more flexibility with transaction fees when using the eip-1559 transaction type (see the Transaction Fee section below for more details).
Ethereum additionally has 3 implicit transaction types: ‘payment’ (to send ETH from one account to another; ‘smart contract creation’ (to add smart contract code onto the ledger); and ‘smart contract invocation’ (to call a function of a smart contract already on the ledger).
Account Transaction Structure Flexibility
The above sections are only the core ones needed in an accounts formatted transaction. Due to the flexibility of the accounts DLT design, you can expect to see a wide variety of additional parameters. For example, in permissionless DLT networks, you would expect transactions to have an attached cryptocurrency amount and transaction fee information. Whereas in both permissioned and permissionless DLT networks you could expect smart contract related information attached (such as a smart contract function name and associated input parameters).
Accounts Transaction Example
Ethereum Transaction Diagram (See here)
This diagram shows a transaction which has been accepted by the Ethereum mainnet. This transaction has Alice as the origin. Alice has created this transaction to send a 1 ETH payment to Bob. This is Alice’s fifth transaction that she has created. Lastly, all transactions on the Ethereum mainnet have to pay a transaction fee, which we have presented to you here as the final amount.
Transaction Fee
In permissionless DLT networks a transaction fee must be paid for the transaction to be accepted. In account formatted transactions, this transaction fee can be explicitly or implicitly declared.
The exact transaction fee for a non smart contract transaction can be known before the transaction is accepted by the DLT network, because the exact computation costs of processing these transactions can be accurately predicted before it is processed.
On the other hand, the exact transaction fee for a smart contract transaction is unknown before the transaction is accepted by the DLT network. This is because the computation cost of processing a smart contract function can depend on the current ledger state when the transaction is finally accepted. To manage this, users denote a specific price they will pay per computational unit (in Ethereum, this computational unit is known as gas) and they also denote a maximum number of computational units they can be charged for.
Note that recently Ethereum eip-1559 transactions allow users to declare a maximum price per computational unit, instead of a specific price as they did when using legacy transactions. This means that in theory users are less likely to overpay to have their transaction accepted by the Ethereum mainnet.
[2.9] Read your First Accounts Transaction​
Note that the external exercise linked to in this page is a non-essential element of the course. It is provided for your benefit to build your blockchain developer skills. FutureLearn advises you that interacting with the content may require or result in personal data transfer to this external website; FutureLearn recommend that you check external websites privacy policies before use.
Overledger Accounts Transaction Search Example
In our next example, we will use Overledger to read accounts transactions from the Ethereum Ropsten testnet and the XRP Ledger testnet.
For this task, go to the fifth exercise link on our Github repository in a new tab and follow the step by step instructions.
[2.10] Accounts State​
To track assets correctly, the current state and transitions to possible future states must be defined.
Account Current State
The Account current state is fundamentally a list of accounts and data related to each account. Each account is referenced by a unique account identifier (e.g. an Ethereum address).
Example parameters relating to each account are:
- Balance: The amount of the native asset of the ledger (e.g. ETH) that this account holds.
- Sequence (also referred to as nonce): A unique number currently assigned to this account.
- Contract code: Any associated user defined code relating to this account.
- Contract storage: The data store relating to the contract code. This data store can only be changed by the contract code.
These are just a few possible examples but they do change from DLT to DLT due to the flexibility of the accounts model. For instance, in Hyperledger Fabric, there is only one parameter assigned to a smart contract account, which is its contract storage (known in Fabric as the world state).
Account State Transition
The Accounts state transition model is more complex than the UTXO variant, due to the many possible parameters associated with each account. This is why we present two examples of an account-based state transition function below, one without triggering smart contract code, and one that triggers a single simple smart contract function. In a future course, we will explain smart contracts in more detail and present state transition functions for transactions that trigger multiple smart contracts.
Note that the state transitions presented below are assuming the common validate-order-execute transaction flow of permissionless DLTs. This flow states that transactions are validated, then ordered and only after ordering is the transaction associated computation processed. We briefly describe an alternative execute-order-validate transaction flow model in section 2.12.
State Transitions for Non-Smart Contract Assets
Assume that each accepted transaction does not interact with an account that has smart contract code associated with it.
Then for each newly accepted transaction by the DLT node:
- Add the asset(s) declared in the transaction to the destination account
- Remove the asset(s) declared in the transaction from the origin account
- Remove the transaction fee from the transaction origin account (if this DLT network requires a fee)
- Change the transaction origin’s account sequence
In Ethereum there is only one asset that exists outside smart contracts (ETH). But this is not the case for all accounts-based DLTs. For instance, the XRP Ledger allows users to add their own assets (e.g. TOKEN123) without using smart contract code. These assets, such as TOKEN123, are then sent between accounts in the manner described in the 4 steps above.
State Transactions for Single Smart Contracts
In the second example, we assume that each accepted transaction interacts with a single smart contract and this smart contract does not interact with any other accounts. For simplicity, we also assume that each accepted transaction is not sending an asset at the same time as invoking a smart contract function.
For each newly accepted transaction by the DLT node:
- Run the smart contract function declared in the transaction if it exists in the transaction destination account’s contract code.
- If the code did not error then commit any changes that the destination account’s contract code has made to its contract storage.
- Remove the transaction fee from the transaction origin account (if this DLT network requires a fee)
- Change the transaction origin account’s sequence
Recall that regardless of if the smart contract code errored, steps 3 and 4 still need to occur, to firstly pay the DLT network processing fee and secondly to stop replay attacks.
Double Spend Prevention
Double spends are prevented as transactions from a particular account are processed in sequence order. If a transaction sent by Alice, with sequence value 1, transfers an asset to Bob, Alice cannot resend the same asset to Caroline with a transaction with sequence value 2. That is, unless, between Alice’s transactions 1 and 2, Bob has sent the asset back to Alice.
Note that temporary double spends can occur if there is a dispute on transaction ordering between DLT nodes. These disputes are resolved via the consensus protocol.
[2.11] Read your First Accounts State​
Note that the external exercise linked to in this page is a non-essential element of the course. It is provided for your benefit to build your blockchain developer skills. FutureLearn advises you that interacting with the content may require or result in personal data transfer to this external website; FutureLearn recommend that you check external websites privacy policies before use.
Overledger Accounts State Search Example
In our next example, we will use Overledger to read accounts state from the Ethereum Ropsten testnet and the XRP Ledger testnet.
Recall that in the accounts model, state can be made up of many DLT specific variables. Therefore in this example we will focus on querying for the balance and sequence number of different accounts, as these two parameters are required in all accounts based permissionless DLT networks.
For this task, go to the sixth exercise link on our Github repository in a new tab and follow the step by step instructions.
[2.12] User Defined Code in Accounts Transactions​
Accounts-based DLTs may or may not allow user-defined (smart contract) code to be added to the ledger. For instance, the XRP ledger does not allow smart contracts, whereas Ethereum does.
Developers typically code smart contracts in a high-level language which complies down to byte code to be added to the ledger via transactions (e.g. like Ethereum) or via installation on the DLT nodes themselves (e.g. like Hyperledger Fabric).
Ethereum Smart Contracts
Ethereum has the honour of being the first DLT created that can use, in a permissionless DLT network, smart contracts programmed using a Turing complete language. It uses a unique virtual machine known as the Ethereum Virtual Machine (EVM) to process accepted transactions. The EVM, therefore, allows any type of user-defined code to be uploaded to the ledger.
This computational flexibility allows for many types of application to be reprogrammed as a decentralised application (dApp). But the computational flexibility of the EVM leads to unpredictable executions, in terms of time and even success (some accepted transactions can error). Ethereum handles the unpredictable execution of smart contract code in the following ways:
Handling Unpredictable Execution Times
The EVM measures processing in computational units known as gas. Each transaction must state either its price per gas unit (if using the legacy transaction type) or its maximum price per gas unit (if using the eip-1559 transaction type). Additionally, each transaction must state the maximum number of gas units the transaction origin account is willing to pay for.
Now when the EVM is processing an accepted transaction, it keeps track of the amount of gas used. If the maximum number declared in the transaction is reached and a smart contract function has not completed, then all state changes made through the processing of the transaction (apart from the transaction fee) are reverted. A transaction fee is still paid for because firstly the processing work was still completed by the DLT network, and secondly, if these transactions were free, they could be used to perform free denial of service attacks on the DLT network.
Finally, each Ethereum block also has a restriction on the maximum amount of gas that it can contain. This gives fairly consistent computational requirements to process each block (even though the processing time of each transaction may vary significantly).
This block computation limit restriction does in fact mean that code deployed onto a blockchain can never truly be Turing complete, even if the code was written in a Turing complete language. This is a desirable restriction as otherwise, the DLT nodes will easily be open to denial of services attacks.
Handling Unpredictable Execution Success
Smart contracts are uploaded from ledger users themselves. Therefore there is no guarantee of the quality of this code. Bugs can be present. But also the smart contract developers may legitimately want their code to error under certain situations (e.g. when Alice tries to send Caroline an asset that is assigned to Bob). To handle this, Ethereum allows transactions to complete successfully or revert.
As previously mentioned, reverted transactions do not change the ledger state (apart from moving the transaction fee from the transaction origin to the block creator). Successful transactions do change the ledger state. Exactly how they change the state is dependant on what type of transaction it was. Payment transactions will update accounts’ ETH balances. Smart contract creation transactions will add smart contract code to the ledger and possibly set initial smart contract storage parameters. Whereas smart contract invocation transactions will change smart contract storage parameters.
Deploying User Defined Code
Some accounts-based DLTs (e.g. Ethereum) allow smart contracts to be deployed via transactions by embedding the smart contract code into transactions. This tightly couples the smart contract code with the distributed ledger.
But there exists other smart contract deployment models. Fabric v2.0 does not tightly couple smart contract (chaincode) deployment to the implementation of the distributed ledger. Smart contracts can instead be deployed locally to a DLT node or deployed as a service. A service deployment can then be accessible by one or more DLT nodes of the Fabric DLT network. Now to connect the smart contracts to the Fabric DLT network, there will be a transaction added to the Fabric DLT network that defines and references the smart contract code (but the code is not included in this transaction).
Other Smart Contract Models
The Ethereum Virtual Machine, due to its inclusion on multiple DLT networks, is the defacto standard for accounts-based DLTs that require smart contracts written in a Turing complete language. See here for a list of DLT networks that implement the EVM.
The accounts based smart contract DLT with arguably the largest architecture difference from Ethereum is Hyperledger Fabric. We have already discussed from the previous section how smart contract deployment differs between these models. Additionally the processing of transactions also differ. Fabric follows the execute-order-validate model, meaning that the smart contract code is first run locally on a node before it is endorsed as a transaction. When a sufficient number of endorsements are gained, a user can submit the transaction for ordering. Only after ordering can the outputs of the execution be applied to change the contract storage (world state), if these changes still apply that is. The complete transaction flow can be consulted here.
Due to this different transaction flow model, when a transaction is executed before endorsement it generates a read and write set. The read set states what contract storage parameters are read during the processing of this transaction and the write set states what the contract storage parameters are changed to after the processing of a transaction. In Fabric the contract storage is effectively made up of (key, value, version) tuples, where the version is changed every time the value is changed.
The presence of the version parameter provides Fabric with an additional method to prevent double spends (as well as the transaction sequence as described in section 2.7). This is because, inside a transaction, the parameters read are tagged by their version number. Therefore, after a Fabric transaction is ordered, as part of its validation, each node will check if the read/write set still applies. If the read/write set is out of date because of an old key value version, then the ledger state changes desired by this transaction will not be applied.
Changes to Contract Storage
Regardless of the DLT, a contract storage parameter associated with a smart contract can only be changed if:
- A transaction has been created that invokes a function from that smart contract.
- This function changes the storage parameter in question.
- This function call completes successfully.
- The transaction is accepted by the DLT network and is processed successfully.
Note that smart contract functions can have various (or no) access controls. This will be discussed in a future course.