In the last article of the Road to Neo3 series we explored the current lack of global state on Neo2 and the issues it introduces for light clients. Additionally, we noted the first proposed solution, the inclusion of a state root in block headers. In today’s article, we’ll cover the downsides to this approach and introduce Neo’s unique bug fixing capabilities.
Including a state root in block headers provides valuable trust guarantees for stored data, especially for light clients such as user wallets and for cross-chain transactions. However, this approach also tightly couples block and state persistence, an undesirable trait that the initial discussions sought to rectify due to the potential for significant performance loss.
As was explained by core developer Igor Coelho, in order to add the latest state root to the block header, the current state has to be calculated by the consensus node acting as speaker before the block can be proposed.
After spending time ordering and verifying transactions in order to construct a block, the consensus node would need to process all state changes included in those transactions before the final state could be determined. Only after the state root has been calculated can it be included in the block header, so all this computation must be done during the time between the block’s creation and proposal to other nodes.
In some cases, this could use up all the available time in a given consensus round, causing the other consensus nodes to agree to change view, delaying the next block. The delay would reduce the overall throughput of the network, since it the block time would double for each failed consensus round.
One potential solution was suggested by fellow core developer Shargon, who noted that new block headers could instead contain the previous state. This could negate the potential performance drawbacks, since nodes would not need to spend resources computing state while consensus is happening. This process could be done afterwards, with the state of the new block being persisted in the next block header.
Although this approach makes better use of node resources and helps ensure that block production is prioritized, it would also disable a unique strength of the Neo blockchain—the ability for protocol bugs to be fixed without affecting the blockchain’s immutability.
Neo’s unique bug fixing capability
An interesting trait on Neo2 is the ability for bugs in code to be fixed without affecting the block history. This principle was first noted by Neo co-founder Erik Zhang, later summarized by Igor Coelho:
“Neo Blockchain has an interesting philosophy that we can keep applying fixes to it, and state is connected to the protocol specification itself, not the code. So, if [the] code has a bug, and we “lock” the state on block, we could never correct the issue.”
An example of this bug-fixing capability was provided by Zhang, who offered a demonstration of how NeoVM could produce an incorrect contract result, potentially ending in a faulty modification to the contract’s storage. Events such as asset loss or hacks that occur due to bugs such as these can be resolved on most PoW/PoS blockchains that undergo a forking procedure, however forks are inherently polarizing events that can involve blocks being “rolled back.”
This threatens the assumed finality of transactions on the network, calling into question the real immutability of the ledger and potentially causing ecosystem disruption or splintering, as has notably happened in the past with Bitcoin and Ethereum.
If a similar issue occurred on Neo2, the bug in NeoVM’s code could be fixed in an update and rolled out to nodes on the network. Each updated node would re-synchronize the same blockchain as before, preserving the immutability of transactions, but the incorrect contract execution would no longer occur. This would change the state root from that moment onwards, but this would not affect the network since currently a global state is not persisted.
In essence, this provides Neo with the unique ability to fix bugs in its protocol without affecting the immutability of the blockchain or encountering the significant overheads that may arise during conventional forks. However, it currently comes at the cost of not having an agreed global state. Aside from the performance benefits, this is the other advantage of a decoupled state.
This consideration stands as the main argument against writing state roots into the block header. Once the state has been finalized in a block, it will always be a part of that block. If an update to the protocol fixed a bug that had occurred in the past, nodes that update and replay the blockchain would end up with a state that no longer matched the state recorded in past block headers.
Consensus on a single global state is an important step in the reliability and adoption of the Neo blockchain. In the next article, we’ll look at a proposed solution to state persistence that satisfies the need for a global state whilst also preserving the ability for protocol bugs to be fixed without interfering with the blockchain itself.