A blockchain is pretty much a distributed state machine with a database that is in sync with all other nodes on the network. Thus, we break the lifecycle of a transaction into several steps, or states, in which it is being processed by the network.
Transactions are created on the client side and sent to one of any Masternodes in the quorum. All masternodes must accept transactions from a standarized web API.
After getting a transaction from a user, the masternode performs a series of steps to determine whether or not the transaction is valid or spam.
- Is the signature valid?
- Does the user have the balance to cover the stamps on this transaction?
- Is the nonce accurate?
- Is the transaction formatted with all the required fields?
If so, the transaction is moved into a queue of other pending transactions. Otherwise, an error is sent to the user and the transaction is thrown out.
The masternode waits to send the queue until the current block is done being created. After that, the queue is wrapped up into a transaction batch, signed by the masternode, timestamped, and sent to the delegates.
Delegates are then notified by at least one masternode that work is available. Masternodes also tell other masternodes that there is work to be made so they can tell delegates whether or not they have work waiting in their queue. Masternodes may not have anything to work on, in which case an empty transaction batch is sent to the delegates.
Delegates wait for some sort of transaction batch to be recieved by all masternodes in the system. If one is not recieved in enough time, a 'shim' or placeholder transaction batch is added and it is assumed the masternode has failed.
Each transaction batch is also verified, with transactions being re-verified to prevent rouge masternodes from slipping in bad transactions.
- Is this transaction batch signed by a real masternode?
- Is the timestamp on this batch too old?
- Do any of the transactions in this batch fail verification?
If so, delegates will either scrap the entire transaction batch, or just the bad transactions and move forward. When the entire batch has to be scrapped, a shim batch is added in its place.
Delegates then begin execution of all transactions. Transactions are run in the same order of operation on each machine. While there are optimizations to speed up the processing of transactions, the technical order of execution of each transaction is preserved.
Execution occurs by simply informing the Contracting layer of the function the user has requested to run. The Contracting layer responds with:
- Whatever the function has returned. Default is
- Any error, if the transaction fails.
- Status code of the transaction (Pass or fail)
- Number of stamps used in the transaction
- State changed made by the transaction
All of these outputs are gathered and paired with their transaction inputs to make 'subblocks'. A subblock is a block of completed transactions seperated by transaction batch. Transaction batches are turned into subblocks. Subblocks are turned into blocks.
Subblocks are given a merkle proof, calculated by creating a merkle tree of all transactions and signing them. This cryptographically verifies the work and links the results to a particular delegate in case of fraud or error. Each subblock is sent to the masternodes to verify.
As soon as the work is sent to delegates, the subblock gathering step begins. As each subblock is recieved, it is verified.
- Is this from a delegate?
- Is the merkle proof signature valid?
- Can you rebuild the merkle tree from the transactions and come to the same root hash?
- Is the merkle tree missing any leaves?
If any subblock fails verification, it is tossed out. One subblock from a delegate can fail while others succeed.
Some subblock results may differ from other delegates. This is where consensus comes into play. Each subblock has a result hash that identifies its results. A subblock is completed when it gets 66% of delegates to agree on the result hash.
There can be failures in consensus on some subblocks, but agreements on others and a block is still made. A block 'fails' if all subblocks from all delegates fail to make consensus. In either case, each subblock that fails is never included in the block.
After there is enough consensus, the subblocks are wrapped up into their final block format, given a block hash, which is a cryptographic and determinsitic function, and broadcast to everyone.
All nodes listen for block updates. Whenever a block is received, it is validated.
- Is this block older than the current block height?
- Can I recreate the block hash from the inputs?
If so, the state changes in each transaction are applied to state, and the blockchain has successfully moved forward one block.