Block Sync Design for Supporting both PoW and Tendermint

Park Juhyung
CodeChain
Published in
6 min readApr 26, 2019
Photo by Charlie Marusiak on Unsplash

PoW (Proof of Work) is a consensus algorithm used by Bitcoin and Ethereum. All nodes will continue to run random computations until a certain score is reached. If the calculation is successful, it propagates successful results and blocks to the network through block synchronization. When other nodes receive a successful block, they build a new block over that block and repeat the random calculation again.

Tendermint Consensus is a BFT style consensus algorithm, in which a predetermined number of validation nodes negotiate blocks through the steps of Propose, Pre-vote, and Pre-commit. Each validator node becomes a proposer according to a predetermined order and generates a proposal block and propagates it to other nodes. The non-proposer validator nodes process a Pre-vote vote, a Pre-commit vote to determine the block. Finally, a proposal block that receives 2/3 or more Pre-commit votes of all nodes is committed and included in the chain.

The CodeChain mainnet uses the Tendermint consensus, but CodeChain itself is a blockchain engine that supports both Tendermint and PoW. Block synchronization, transaction propagation, block and transaction verification, and block generation code share the same implementation in both consensuses.

This article summarizes the problems and solutions we have encountered while designing and implementing block-synchronization code that works well with both Tendermint and PoW consensus.

Storing Precommits in the Chain.

(Figure 1 The Pre-commit for the current proposal is included in the current block. The contents of the pre-commits’ fields of the proposal block and the confirmed block are different.)

The blocks in Tendermint must contain more than ⅔ of pre-commits about that block in order to be confirmed, and thus, the chain must store the blocks’ pre-commits. The most intuitive way is to store the pre-commit in the header of the block. Each node collects 2/3 or more pre-commits, completes the block, and propagates the completed block through block synchronization. With this method, it is easy to synchronize blocks because you can verify the validity of the block with the information in the block just like PoW.

(Figure 2 Blocks with three or more pre-commits each, when the total number of validation nodes is four. They are all valid blocks, but each hash value is different.)

However, this method has problems when calculating the hash of the block. If you calculate the hash of a block, including all the pre-commits in the block, there would be several valid blocks at a height, each containing slightly different pre-commits. This would be a violation since a fixed block of a certain height must have a single hash.

Another way to calculate a block hash is to use the hash of the block excluding the pre-commit in the block, as a hash of the block. At this point, a block with 2/3 or more pre-commit separate from the block hash becomes a valid block. With this approach, the pre-commit in the blocks for each node in the network changes slightly. However, because the pre-commit content in the block is used to penalize nodes that have not participated in the consensus later, all nodes must have the same content. Therefore, there is no way to calculate the hash of a block without pre-commits.

(Figure 3: The pre-commits for the previous block are contained in the next block. The contents of the proposal block and the confirmed block are the same.)

To solve this problem, we included the pre-commit of the previous block in the header of the next block. Using this method, a proposer that has seen more than 2/3 of the pre-commits of the previous block will create the block with those pre-commits stored in the next block. With this method, the hash of the proposal and the committed block is the same, and all nodes share the same pre-commit. Instead, there always has to be a block ahead in order to confirm the previous block.

Is It Possible to Propagate a Block that Does Not Have a Pre-commit

Block synchronization is the process by which each node shares the block with the highest score (best block) that it knows. Each node shares its score with connected nodes whenever it changes the block with the highest score that it knows, and requests for a block if the score of the other node is higher than its score.

The block score refers to the difficulty of mining in the PoW consensus and the height of the block in the Tendermint consensus. (If there are multiple proposals at the same block height in Tendermint consensus, there will be some arbitrary score amongst the proposals. I will skip this topic because it is not important for this article). When a block is well synchronized, the blocks with the highest score of the connected nodes become equal.

However, since blocks can only be verified if there exists a next block, even if the node has propagated the highest-scoring block known to it, it is impossible for the node receiving that block to verify it. Therefore, the latest updated block is different for each connected node.

(Figure 4 If you do not have a Best Proposal Block, you can not deliver the most recent block’s pre-commit, making it impossible to adjust the height between nodes.)

For example, let’s say that node A has a height of 3 and node B has a height of 5 as shown in Figure 4. Even if node A receives blocks of height 4 and 5 from B, since it cannot retrieve the pre-commits of height 5, and thus, the height of A becomes 4. Node A can never be at the same height as Node B, since the pre-commit of the chain’s latest block can never exist.

Best Block and Best Proposal Block

The CodeChain team introduced a best proposal block, which is different from the best block, to solve this problem. Best proposal block is defined as the block that has the highest score among the blocks that are not yet confirmed but are likely to be confirmed. In the PoW consensus, the best proposal block is always the best block, and in the Tendermint consensus, the proposal block becomes the best proposal block.

(Figure 5 If you receive the Best Proposal Block, it is possible to adjust the height between nodes.)

Existing block synchronization technology only delivers to the best block, and the best block of the receiving node is one level below the best block of the delivering node. After introducing the best proposal block, each node delivers it to the best proposal block, and the node receiving the best proposal block reads the pre-commit of the best block from the best proposal block and verifies the best block. Now the blocks are safely synchronized all the way up to the best block.

For example, in Figure 5, Node A receives block 4, block 5, and proposal block 6. Block 4 is verified with the pre-commit in block 5 and block 5 is verified with the pre-commit of proposal block 6. Node A has a height of 5, and is the same height as Node B that delivered the block.

Conclusion

Consensus can be thought of as only a part of the process of making blocks come to an agreement in a blockchain, but it has major impacts on other components of the blockchain as well. The CodeChain team has also introduced a new concept called the best proposal block to implement block synchronization that works in both PoW and Tendermint.

CodeChain is currently running on the main network and the test network using the Tendermint consensus. You can see the block explorer at explorer.codechain.io and corgi.codechain.io/explorer for each respective network.

--

--