There is a lot of activity going on building the Ethereum 2.0 beacon chain, including the .NET client I am working on, Nethermind.
The beacon chain consists of blocks and a progressive state. Blocks are generated, signed, and transmitted across the network, then applied to transition the state. The following diagram shows the main relationships.
The diagram is based on version 0.10.1 of the Eth 2.0 specification. Version 0.11.0 has just been released, and there is a minor difference in the way Domain is calculated from the diagram above, but otherwise the relationships are the same.
Creating a new block
To create a new block, start with the current head of the chain.
If there is a short term fork, e.g. recent blocks differ by a missing slot whose block was not propagated fast enough, then there is a choice algorithm that will select the “best” head (the one with the most weighted attestations from stakers).
Then, if there are any missed slots, the state is advanced (without any operations) as needed.
All the operations that need to be applied (the deposits, attestations, exits, and other events that need to be included) are collected in the BeaconBlockBody and applied to the state, to generate the new BeaconState.
The slot, parent block root, and hash root of the operations (the body root) are added to the state in the BeaconBlockHeader* element. Note that the state root in the element is zero (0x000….), because the state can not recursively include it’s own hash.
The hash root of the resulting state (with the 0x000…) is calculated and added to the block, which is then hashed with the domain of the chain, to prevent collisions across different main and test networks, signed, and transmitted.
Applying state transition
When a SignedBeaconBlock is received, several validations are performed, including checking the signature is valid and that you have the state corresponding to the parent block (identified by the parent root).
The state is then advanced by advancing the state slot to that of the block (there may be skipped slots), and applying the operations in the BeaconBlockBody (such as deposits, attestations, exits, etc).
Note that skipped slots still generate internal states, advancing one slot at a time, just with no operations applied.
The resulting state should be the same as that of the block generator, so the hash of the new BeaconState (with the 0x000…) is calculated and verified against the state root in the received block.
Note that the BeaconState includes three of the four details of the block that it is based on – the slot, the parent root, and the hash root of the BeaconBlockBody (the operations).
The latest block slot is separate from the state slot because for internal state calculations they may differ due to skipped slots, e.g. skipped state with slot 7, will still have latest block slot 6, along with the parent root and body root of the block for slot 6.
The grouping is identified as “BeaconBlockHeader*” in the diagram, because it uses the BeaconBlockHeader structure, but the generated block states will always have header state root of zero (0x000…), because trying to include their own hash would be recursive.
It does, however, mean that the block root can easily be calculated give the state – calculate the hash root of the state (with the 0x000…), then create a copy of the header inserting the correct state root, then calculate the hash of the header (which will be the same as the hash of the block).
Chained blocks increase trust
A key feature of a blockchain is replacing individual trust (counterparty, third party), with systemic trust (majority of processors, the algorithm).
Systemic trust can be increased through several features:
- Large number of processors (e.g. public blockchain) – the more decentralized the processing, the more trust we have in the majority.
- Multiple clients (e.g. development teams) – avoids centralising control of the algorithm, so processors have a choice of clients.
- Open source – both allows inspection of the algorithm, plus allows easy forking if the majority want a different direction.
Chaining blocks together also increases systemic trust, as the older a block is the more majority weight it has. Such chaining is not needed in a plain distributed ledger/distributed database, that is not relying on systemic trust.
A 51% attack is useful against the most recent block, but if you want to change a block 100 slots ago, you need to have 51% majority for all of those 100 slots.
For short term forks, there may be some network confusion as to who has the majority, e.g. if two competing blocks propagate at different speeds around the network.
However, because blocks are chained together, a clear winning chain will relatively quickly emerge, and the chance of any other chain overtaking drop to zero.
This allows finalization, after which stake can be withdrawn without having to worry about a “nothing at stake” attack.