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.

150 lines
6.0 KiB

  1. # ADR 025 Commit
  2. ## Context
  3. Currently the `Commit` structure contains a lot of potentially redundant or unnecessary data.
  4. It contains a list of precommits from every validator, where the precommit
  5. includes the whole `Vote` structure. Thus each of the commit height, round,
  6. type, and blockID are repeated for every validator, and could be deduplicated,
  7. leading to very significant savings in block size.
  8. ```
  9. type Commit struct {
  10. BlockID BlockID `json:"block_id"`
  11. Precommits []*Vote `json:"precommits"`
  12. }
  13. type Vote struct {
  14. ValidatorAddress Address `json:"validator_address"`
  15. ValidatorIndex int `json:"validator_index"`
  16. Height int64 `json:"height"`
  17. Round int `json:"round"`
  18. Timestamp time.Time `json:"timestamp"`
  19. Type byte `json:"type"`
  20. BlockID BlockID `json:"block_id"`
  21. Signature []byte `json:"signature"`
  22. }
  23. ```
  24. The original tracking issue for this is [#1648](https://github.com/tendermint/tendermint/issues/1648).
  25. We have discussed replacing the `Vote` type in `Commit` with a new `CommitSig`
  26. type, which includes at minimum the vote signature. The `Vote` type will
  27. continue to be used in the consensus reactor and elsewhere.
  28. A primary question is what should be included in the `CommitSig` beyond the
  29. signature. One current constraint is that we must include a timestamp, since
  30. this is how we calculuate BFT time, though we may be able to change this [in the
  31. future](https://github.com/tendermint/tendermint/issues/2840).
  32. Other concerns here include:
  33. - Validator Address [#3596](https://github.com/tendermint/tendermint/issues/3596) -
  34. Should the CommitSig include the validator address? It is very convenient to
  35. do so, but likely not necessary. This was also discussed in [#2226](https://github.com/tendermint/tendermint/issues/2226).
  36. - Absent Votes [#3591](https://github.com/tendermint/tendermint/issues/3591) -
  37. How to represent absent votes? Currently they are just present as `nil` in the
  38. Precommits list, which is actually problematic for serialization
  39. - Other BlockIDs [#3485](https://github.com/tendermint/tendermint/issues/3485) -
  40. How to represent votes for nil and for other block IDs? We currently allow
  41. votes for nil and votes for alternative block ids, but just ignore them
  42. ## Decision
  43. Deduplicate the fields and introduce `CommitSig`:
  44. ```
  45. type Commit struct {
  46. Height int64
  47. Round int
  48. BlockID BlockID `json:"block_id"`
  49. Precommits []CommitSig `json:"precommits"`
  50. }
  51. type CommitSig struct {
  52. BlockID BlockIDFlag
  53. ValidatorAddress Address
  54. Timestamp time.Time
  55. Signature []byte
  56. }
  57. // indicate which BlockID the signature is for
  58. type BlockIDFlag int
  59. const (
  60. BlockIDFlagAbsent BlockIDFlag = iota // vote is not included in the Commit.Precommits
  61. BlockIDFlagCommit // voted for the Commit.BlockID
  62. BlockIDFlagNil // voted for nil
  63. )
  64. ```
  65. Re the concerns outlined in the context:
  66. **Timestamp**: Leave the timestamp for now. Removing it and switching to
  67. proposer based time will take more analysis and work, and will be left for a
  68. future breaking change. In the meantime, the concerns with the current approach to
  69. BFT time [can be
  70. mitigated](https://github.com/tendermint/tendermint/issues/2840#issuecomment-529122431).
  71. **ValidatorAddress**: we include it in the `CommitSig` for now. While this
  72. does increase the block size unecessarily (20-bytes per validator), it has some ergonomic and debugging advantages:
  73. - `Commit` contains everything necessary to reconstruct `[]Vote`, and doesn't depend on additional access to a `ValidatorSet`
  74. - Lite clients can check if they know the validators in a commit without
  75. re-downloading the validator set
  76. - Easy to see directly in a commit which validators signed what without having
  77. to fetch the validator set
  78. If and when we change the `CommitSig` again, for instance to remove the timestamp,
  79. we can reconsider whether the ValidatorAddress should be removed.
  80. **Absent Votes**: we include absent votes explicitly with no Signature or
  81. Timestamp but with the ValidatorAddress. This should resolve the serialization
  82. issues and make it easy to see which validator's votes failed to be included.
  83. **Other BlockIDs**: We use a single byte to indicate which blockID a `CommitSig`
  84. is for. The only options are:
  85. - `Absent` - no vote received from the this validator, so no signature
  86. - `Nil` - validator voted Nil - meaning they did not see a polka in time
  87. - `Commit` - validator voted for this block
  88. Note this means we don't allow votes for any other blockIDs. If a signature is
  89. included in a commit, it is either for nil or the correct blockID. According to
  90. the Tendermint protocol and assumptions, there is no way for a correct validator to
  91. precommit for a conflicting blockID in the same round an actual commit was
  92. created. This was the consensus from
  93. [#3485](https://github.com/tendermint/tendermint/issues/3485)
  94. We may want to consider supporting other blockIDs later, as a way to capture
  95. evidence that might be helpful. We should clarify if/when/how doing so would
  96. actually help first. To implement it, we could change the `Commit.BlockID`
  97. field to a slice, where the first entry is the correct block ID and the other
  98. entries are other BlockIDs that validators precommited before. The BlockIDFlag
  99. enum can be extended to represent these additional block IDs on a per block
  100. basis.
  101. ## Status
  102. Implemented
  103. ## Consequences
  104. ### Positive
  105. Removing the Type/Height/Round/Index and the BlockID saves roughly 80 bytes per precommit.
  106. It varies because some integers are varint. The BlockID contains two 32-byte hashes an integer,
  107. and the Height is 8-bytes.
  108. For a chain with 100 validators, that's up to 8kB in savings per block!
  109. ### Negative
  110. - Large breaking change to the block and commit structure
  111. - Requires differentiating in code between the Vote and CommitSig objects, which may add some complexity (votes need to be reconstructed to be verified and gossiped)
  112. ### Neutral
  113. - Commit.Precommits no longer contains nil values