Application
Application of Blocks is one of the two primary consensus functions (the other being validation). The two are closely intertwined: since Transactions can observe the effects of earlier transactions within the same block, validating a transaction requires first applying all prior transactions.
Blocks are applied to their parent State, producing a new state and a set of effects; the parent state is not altered. In order to track intra-block state changes, transactions are applied to a "midstate" rather than the parent state itself. For example, the Outputs created by each transaction are recorded so that, despite these outputs not being present in the Accumulator, later transactions may spend them.
A full accounting of the application process is as follows:
- First, transactions are applied to the midstate, in the order in which they appear in the block:
- Siacoin inputs are marked as spent.
- Siacoin outputs are created.
- Siafund inputs are marked as spent.
- Siafund claim outputs are created.
- Siafund outputs are created.
- File contracts are created.
- File contract revisions are applied, overwriting the prior contract.
- File contract resolutions are applied, marking the contract as complete and creating outputs (whether valid or missed).
- Next, block-level effects are applied to the state, sometimes making use of the midstate:
- The block reward output, aka "miner payout," is created.
- The Foundation subsidy is created.
- The siafund tax revenue recorded by the midstate is added to the total.
- The number of attestations recorded by the midstate is added to the total..
- The Foundation addresses are updated, if applicable.
- The Accumulator is updated to incorporate new elements and modifications to existing elements.
- The block's difficulty is added to the total work.
- The difficulty is adjusted.
- The Oak time and work totals are updated.
- The block's timestamp is added to the sliding window of 11 previous timestamps.
After all transaction- and block-level effects have been applied, the result is a new state and an "update" object, which contains a record of all elements impacted by the block. This allows e.g. a wallet to track its outputs, or a host to track its outstanding file contracts.
Reversion
During a reorg, it is necessary to revert the current block, essentially rewinding to an earlier state. In most blockchains, this is achieved by doing the inverse of all the steps listed above. For example, every output that was created is un-created; every file contract that was revised is un-revised; and so on. But because Sia's state is so small, it is feasible to store the resulting state for every block in the chain, making this complicated un-application process superfluous: instead, we simply fetch the desired state, and set it as the current state.
However, reversion is still necessary for subsystems that care about the effects of blocks, such as wallets, renters, and hosts. A wallet needs to know which outputs it can currently spend; therefore, if a block is reverted, and that block spent an output controlled by the wallet, the wallet needs to mark that output as spendable again. To facilitate this, we simply apply the block, exposing the same updates to the caller as before, but this time with the context that they are being reverted. Thus, if the update records an output being created, this should be interpreted as an output being un-created, and likewise for all other effects.
In v1
Like most non-Utreexo blockchains, v1 performed validation while simultaneously mutating the database. This meant that validation could only be performed against the current state, and doing so required an exclusive lock on the database. If validation failed at any point, the database transaction would be rolled back. In contrast, v2 validation and application require no I/O at all, and many instances can occur simultaneously on different states.
An illustrative example of the Utreexo paradigm is how v2 handles file contract resolution. In v1, when a file contract's proof window elapsed without a successful proof, the "missed" outputs were automatically created. This was possible because the v1 consensus database tracked the full set of outstanding file contracts. It maintained a separate index which, for each block height, stored the set of contracts that were due to expire at that height. (Immature outputs were handled similarly.) In contrast, nothing in v2 happens automatically: missed outputs are only created when a transaction is mined that explicitly resolves the contract. This spares all Sia nodes from the burden of storing and updating contracts that are irrelevant to them, and also ensures that the effects of a block are strictly proportional to the size of that block.