You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
16 KiB

  1. # Byzantine Consensus Algorithm
  2. _The draft 0.6 whitepaper is outdated. The new algorithm is detailed below. See [revisions](#revisions)_
  3. ## Terms
  4. - The network is composed of optionally connected _nodes_. Nodes directly connected to a particular node are called _peers_.
  5. - The consensus process in deciding the next block (at some _height_ `H`) is composed of one or many _rounds_.
  6. - `NewHeight`, `Propose`, `Prevote`, `Precommit`, and `Commit` represent state machine states of a round. (aka `RoundStep` or just "step").
  7. - A node is said to be _at_ a given height, round, and step, or at `(H,R,S)`, or at `(H,R)` in short to omit the step.
  8. - To _prevote_ or _precommit_ something means to broadcast a [prevote vote](/docs/specs/block-structure#vote) or [precommit vote](/docs/specs/block-structure#precommit-vote) for something.
  9. - A vote _at_ `(H,R)` is a vote signed with the bytes for `H` and `R` included in its [`sign-bytes`](/docs/specs/block-structure#vote-sign-bytes).
  10. - _+2/3_ is short for "more than 2/3"
  11. - _1/3+_ is short for "1/3 or more"
  12. - A set of +2/3 of prevotes for a particular block or `<nil>` at `(H,R)` is called a _proof-of-lock-change_ or _PoLC_ for short.
  13. ## State Machine Overview
  14. At each height of the blockchain a round-based protocol is run to determine
  15. the next block. Each round is composed of three _steps_ (`Propose`, `Prevote`, and
  16. `Precommit`), along with two special steps `Commit` and `NewHeight`.
  17. In the optimal scenario, the order of steps is:
  18. ```
  19. NewHeight -> (Propose -> Prevote -> Precommit)+ -> Commit -> NewHeight ->...
  20. ```
  21. The sequence `(Propose -> Prevote -> Precommit)` is called a _round_. There may be more than one round required to commit a block at a given height. Examples for why more rounds may be required include:
  22. - The designated proposer was not online.
  23. - The block proposed by the designated proposer was not valid.
  24. - The block proposed by the designated proposer did not propagate in time.
  25. - The block proposed was valid, but +2/3 of prevotes for the proposed block were not received in time for enough validator nodes by the time they reached the `Precommit` step. Even though +2/3 of prevotes are necessary to progress to the next step, at least one validator may have voted `<nil>` or maliciously voted for something else.
  26. - The block proposed was valid, and +2/3 of prevotes were received for enough nodes, but +2/3 of precommits for the proposed block were not received for enough validator nodes.
  27. Some of these problems are resolved by moving onto the next round & proposer. Others are resolved by increasing certain round timeout parameters over each successive round.
  28. ## State Machine Diagram
  29. ```
  30. +-------------------------------------+
  31. v |(Wait til `CommmitTime+timeoutCommit`)
  32. +-----------+ +-----+-----+
  33. +----------> | Propose +--------------+ | NewHeight |
  34. | +-----------+ | +-----------+
  35. | | ^
  36. |(Else, after timeoutPrecommit) v |
  37. +-----+-----+ +-----------+ |
  38. | Precommit | <------------------------+ Prevote | |
  39. +-----+-----+ +-----------+ |
  40. |(When +2/3 Precommits for block found) |
  41. v |
  42. +--------------------------------------------------------------------+
  43. | Commit |
  44. | |
  45. | * Set CommitTime = now; |
  46. | * Wait for block, then stage/save/commit block; |
  47. +--------------------------------------------------------------------+
  48. ```
  49. ## Background Gossip
  50. A node may not have a corresponding validator private key, but it nevertheless plays an active role in the consensus process by relaying relevant meta-data, proposals, blocks, and votes to its peers. A node that has the private keys of an active validator and is engaged in signing votes is called a _validator-node_. All nodes (not just validator-nodes) have an associated state (the current height, round, and step) and work to make progress.
  51. Between two nodes there exists a `Connection`, and multiplexed on top of this connection are fairly throttled `Channel`s of information. An epidemic gossip protocol is implemented among some of these channels to bring peers up to speed on the most recent state of consensus. For example,
  52. - Nodes gossip `PartSet` parts of the current round's proposer's proposed block. A LibSwift inspired algorithm is used to quickly broadcast blocks across the gossip network.
  53. - Nodes gossip prevote/precommit votes. A node NODE_A that is ahead of NODE_B can send NODE_B prevotes or precommits for NODE_B's current (or future) round to enable it to progress forward.
  54. - Nodes gossip prevotes for the proposed PoLC (proof-of-lock-change) round if one is proposed.
  55. - Nodes gossip to nodes lagging in blockchain height with block [commits](/docs/specs/block-structure/commit) for older blocks.
  56. - Nodes opportunistically gossip `HasVote` messages to hint peers what votes it already has.
  57. - Nodes broadcast their current state to all neighboring peers. (but is not gossiped further)
  58. There's more, but let's not get ahead of ourselves here.
  59. ## Proposals
  60. A proposal is signed and published by the designated proposer at each round. The proposer is chosen by a deterministic and non-choking round robin selection algorithm that selects proposers in proportion to their voting power. (see [implementation](https://github.com/tendermint/tendermint/blob/develop/types/validator_set.go#L49))
  61. A proposal at `(H,R)` is composed of a block and an optional latest `PoLC-Round < R` which is included iff the proposer knows of one. This hints the network to allow nodes to unlock (when safe) to ensure the liveness property.
  62. ## State Machine Spec
  63. ### Propose Step (height:H,round:R)
  64. Upon entering `Propose`:
  65. - The designated proposer proposes a block at `(H,R)`.
  66. The `Propose` step ends:
  67. - After `timeoutProposeR` after entering `Propose`. --> goto `Prevote(H,R)`
  68. - After receiving proposal block and all prevotes at `PoLC-Round`. --> goto `Prevote(H,R)`
  69. - After [common exit conditions](#common-exit-conditions)
  70. ### Prevote Step (height:H,round:R)
  71. Upon entering `Prevote`, each validator broadcasts its prevote vote.
  72. - First, if the validator is locked on a block since `LastLockRound` but now has a PoLC for something else at round `PoLC-Round` where `LastLockRound < PoLC-Round < R`, then it unlocks.
  73. - If the validator is still locked on a block, it prevotes that.
  74. - Else, if the proposed block from `Propose(H,R)` is good, it prevotes that.
  75. - Else, if the proposal is invalid or wasn't received on time, it prevotes `<nil>`.
  76. The `Prevote` step ends:
  77. - After +2/3 prevotes for a particular block or `<nil>`. --> goto `Precommit(H,R)`
  78. - After `timeoutPrevote` after receiving any +2/3 prevotes. --> goto `Precommit(H,R)`
  79. - After [common exit conditions](#common-exit-conditions)
  80. ### Precommit Step (height:H,round:R)
  81. Upon entering `Precommit`, each validator broadcasts its precommit vote.
  82. - If the validator has a PoLC at `(H,R)` for a particular block `B`, it (re)locks (or changes lock to) and precommits `B` and sets `LastLockRound = R`.
  83. - Else, if the validator has a PoLC at `(H,R)` for `<nil>`, it unlocks and precommits `<nil>`.
  84. - Else, it keeps the lock unchanged and precommits `<nil>`.
  85. A precommit for `<nil>` means "I didn’t see a PoLC for this round, but I did get +2/3 prevotes and waited a bit".
  86. The Precommit step ends:
  87. - After +2/3 precommits for `<nil>`. --> goto `Propose(H,R+1)`
  88. - After `timeoutPrecommit` after receiving any +2/3 precommits. --> goto `Propose(H,R+1)`
  89. - After [common exit conditions](#common-exit-conditions)
  90. #### common exit conditions
  91. - After +2/3 precommits for a particular block. --> goto `Commit(H)`
  92. - After any +2/3 prevotes received at `(H,R+x)`. --> goto `Prevote(H,R+x)`
  93. - After any +2/3 precommits received at `(H,R+x)`. --> goto `Precommit(H,R+x)`
  94. ### Commit Step (height:H)
  95. - Set `CommitTime = now()`
  96. - Wait until block is received. --> goto `NewHeight(H+1)`
  97. ### NewHeight Step (height:H)
  98. - Move `Precommits` to `LastCommit` and increment height.
  99. - Set `StartTime = CommitTime+timeoutCommit`
  100. - Wait until `StartTime` to receive straggler commits. --> goto `Propose(H,0)`
  101. ## Proofs
  102. ### Proof of Safety
  103. Assume that at most -1/3 of the voting power of validators is byzantine. If a validator commits block `B` at round `R`, it's because it saw +2/3 of precommits at round `R`. This implies that 1/3+ of honest nodes are still locked at round `R' > R`. These locked validators will remain locked until they see a PoLC at `R' > R`, but this won't happen because 1/3+ are locked and honest, so at most -2/3 are available to vote for anything other than `B`.
  104. ### Proof of Liveness
  105. If 1/3+ honest validators are locked on two different blocks from different rounds, a proposers' `PoLC-Round` will eventually cause nodes locked from the earlier round to unlock. Eventually, the designated proposer will be one that is aware of a PoLC at the later round. Also, `timeoutProposalR` increments with round `R`, while the size of a proposal are capped, so eventually the network is able to "fully gossip" the whole proposal (e.g. the block & PoLC).
  106. ### Proof of Fork Accountability
  107. Define the JSet (justification-vote-set) at height `H` of a validator `V1` to be all the votes signed by the validator at `H` along with justification PoLC prevotes for each lock change. For example, if `V1` signed the following precommits: `Precommit(B1 @ round 0)`, `Precommit(<nil> @ round 1)`, `Precommit(B2 @ round 4)` (note that no precommits were signed for rounds 2 and 3, and that's ok), `Precommit(B1 @ round 0)` must be justified by a PoLC at round 0, and `Precommit(B2 @ round 4)` must be justified by a PoLC at round 4; but the precommit for `<nil>` at round 1 is not a lock-change by definition so the JSet for `V1` need not include any prevotes at round 1, 2, or 3 (unless `V1` happened to have prevoted for those rounds).
  108. Further, define the JSet at height `H` of a set of validators `VSet` to be the union of the JSets for each validator in `VSet`. For a given commit by honest validators at round `R` for block `B` we can construct a JSet to justify the commit for `B` at `R`.
  109. We say that a JSet _justifies_ a commit at `(H,R)` if all the committers (validators in the commit-set) are each justified in the JSet with no duplicitous vote signatures (by the committers).
  110. - **Lemma**: When a fork is detected by the existence of two conflicting [commits](/docs/specs/validators#commiting-a-block), the union of the JSets for both commits (if they can be compiled) must include double-signing by at least 1/3+ of the validator set. **Proof**: The commit cannot be at the same round, because that would immediately imply double-signing by 1/3+. Take the union of the JSets of both commits. If there is no double-signing by at least 1/3+ of the validator set in the union, then no honest validator could have precommitted any different block after the first commit. Yet, +2/3 did. Reductio ad absurdum.
  111. As a corollary, when there is a fork, an external process can determine the blame by requiring each validator to justify all of its round votes. Either we will find 1/3+ who cannot justify at least one of their votes, and/or, we will find 1/3+ who had double-signed.
  112. ### Alternative algorithm
  113. Alternatively, we can take the JSet of a commit to be the "full commit". That is, if light clients and validators do not consider a block to be committed unless the JSet of the commit is also known, then we get the desirable property that if there ever is a fork (e.g. there are two conflicting "full commits"), then 1/3+ of the validators are immediately punishable for double-signing.
  114. There are many ways to ensure that the gossip network efficiently share the JSet of a commit. One solution is to add a new message type that tells peers that this node has (or does not have) a +2/3 majority for B (or <nil>) at (H,R), and a bitarray of which votes contributed towards that majority. Peers can react by responding with appropriate votes.
  115. We will implement such an algorithm for the next iteration of the Tendermint consensus protocol.
  116. Other potential improvements include adding more data in votes such as the last known PoLC round that caused a lock change, and the last voted round/step (or, we may require that validators not skip any votes). This may make JSet verification/gossip logic easier to implement.
  117. ### Censorship Attacks
  118. Due to the definition of a block [commit](/docs/specs/validators#commiting-a-block), any 1/3+ coalition of validators can halt the blockchain by not broadcasting their votes. Such a coalition can also censor particular transactions by rejecting blocks that include these transactions, though this would result in a significant proportion of block proposals to be rejected, which would slow down the rate of block commits of the blockchain, reducing its utility and value. The malicious coalition might also broadcast votes in a trickle so as to grind blockchain block commits to a near halt, or engage in any combination of these attacks.
  119. If a global active adversary were also involved, it can partition the network in such a way that it may appear that the wrong subset of validators were responsible for the slowdown. This is not just a limitation of Tendermint, but rather a limitation of all consensus protocols whose network is potentially controlled by an active adversary.
  120. ### Overcoming Forks and Censorship Attacks
  121. For these types of attacks, a subset of the validators through external means
  122. should coordinate to sign a reorg-proposal that chooses a fork (and any evidence
  123. thereof) and the initial subset of validators with their signatures. Validators
  124. who sign such a reorg-proposal forego its collateral on all other forks.
  125. Clients should verify the signatures on the reorg-proposal, verify any evidence,
  126. and make a judgement or prompt the end-user for a decision. For example, a
  127. phone wallet app may prompt the user with a security warning, while a
  128. refrigerator may accept any reorg-proposal signed by +½ of the original
  129. validators.
  130. No non-synchronous Byzantine fault-tolerant algorithm can come to consensus when
  131. ⅓+ of validators are dishonest, yet a fork assumes that ⅓+ of validators have
  132. already been dishonest by double-signing or lock-changing without justification.
  133. So, signing the reorg-proposal is a coordination problem that cannot be solved
  134. by any non-synchronous protocol (i.e. automatically, and without making
  135. assumptions about the reliability of the underlying network). It must be
  136. provided by means external to the weakly-synchronous Tendermint consensus
  137. algorithm. For now, we leave the problem of reorg-proposal coordination to
  138. human coordination via internet media. Validators must take care to ensure that
  139. there are no significant network partitions, to avoid situations where two
  140. conflicting reorg-proposals are signed.
  141. Assuming that the external coordination medium and protocol is robust, it follows that forks are less of a concern than [censorship attacks](#censorship-attacks).
  142. ### Revisions
  143. #### 0.6 -> 0.7 (current)
  144. 1. Reduced the minimum number of signature steps from 3 to 2 by removing the "commit" vote and step.
  145. 2. The protocol is more asynchronous: instead of each round taking a predetermined duration of time, each step of a round progresses after +2/3 of the step's votes are found and a timeout is reached, or immediately after +2/3 of matching votes (e.g. a PoLC for prevotes, or a commit for precommits).