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.

152 lines
4.0 KiB

  1. # ADR 030: Consensus Refactor
  2. ## Context
  3. One of the biggest challenges this project faces is to proof that the
  4. implementations of the specifications are correct, much like we strive to
  5. formaly verify our alogrithms and protocols we should work towards high
  6. confidence about the correctness of our program code. One of those is the core
  7. of Tendermint - Consensus - which currently resides in the `consensus` package.
  8. Over time there has been high friction making changes to the package due to the
  9. algorithm being scattered in a side-effectful container (the current
  10. `ConsensusState`). In order to test the algorithm a large object-graph needs to
  11. be set up and even than the non-deterministic parts of the container makes will
  12. prevent high certainty. Where ideally we have a 1-to-1 representation of the
  13. [spec](https://github.com/tendermint/spec), ready and easy to test for domain
  14. experts.
  15. Addresses:
  16. - [#1495](https://github.com/tendermint/tendermint/issues/1495)
  17. - [#1692](https://github.com/tendermint/tendermint/issues/1692)
  18. ## Decision
  19. To remedy these issues we plan a gradual, non-invasive refactoring of the
  20. `consensus` package. Starting of by isolating the consensus alogrithm into
  21. a pure function and a finite state machine to address the most pressuring issue
  22. of lack of confidence. Doing so while leaving the rest of the package in tact
  23. and have follow-up optional changes to improve the sepration of concerns.
  24. ### Implementation changes
  25. The core of Consensus can be modelled as a function with clear defined inputs:
  26. * `State` - data container for current round, height, etc.
  27. * `Event`- significant events in the network
  28. producing clear outputs;
  29. * `State` - updated input
  30. * `Message` - signal what actions to perform
  31. ```go
  32. type Event int
  33. const (
  34. EventUnknown Event = iota
  35. EventProposal
  36. Majority23PrevotesBlock
  37. Majority23PrecommitBlock
  38. Majority23PrevotesAny
  39. Majority23PrecommitAny
  40. TimeoutNewRound
  41. TimeoutPropose
  42. TimeoutPrevotes
  43. TimeoutPrecommit
  44. )
  45. type Message int
  46. const (
  47. MeesageUnknown Message = iota
  48. MessageProposal
  49. MessageVotes
  50. MessageDecision
  51. )
  52. type State struct {
  53. height uint64
  54. round uint64
  55. step uint64
  56. lockedValue interface{} // TODO: Define proper type.
  57. lockedRound interface{} // TODO: Define proper type.
  58. validValue interface{} // TODO: Define proper type.
  59. validRound interface{} // TODO: Define proper type.
  60. // From the original notes: valid(v)
  61. valid interface{} // TODO: Define proper type.
  62. // From the original notes: proposer(h, r)
  63. proposer interface{} // TODO: Define proper type.
  64. }
  65. func Consensus(Event, State) (State, Message) {
  66. // Consolidate implementation.
  67. }
  68. ```
  69. Tracking of relevant information to feed `Event` into the function and act on
  70. the output is left to the `ConsensusExecutor` (formerly `ConsensusState`).
  71. Benefits for testing surfacing nicely as testing for a sequence of events
  72. against algorithm could be as simple as the following example:
  73. ``` go
  74. func TestConsensusXXX(t *testing.T) {
  75. type expected struct {
  76. message Message
  77. state State
  78. }
  79. // Setup order of events, initial state and expectation.
  80. var (
  81. events = []struct {
  82. event Event
  83. want expected
  84. }{
  85. // ...
  86. }
  87. state = State{
  88. // ...
  89. }
  90. )
  91. for _, e := range events {
  92. sate, msg = Consensus(e.event, state)
  93. // Test message expectation.
  94. if msg != e.want.message {
  95. t.Fatalf("have %v, want %v", msg, e.want.message)
  96. }
  97. // Test state expectation.
  98. if !reflect.DeepEqual(state, e.want.state) {
  99. t.Fatalf("have %v, want %v", state, e.want.state)
  100. }
  101. }
  102. }
  103. ```
  104. ### Implementation roadmap
  105. * implement proposed implementation
  106. * replace currently scattered calls in `ConsensusState` with calls to the new
  107. `Consensus` function
  108. * rename `ConsensusState` to `ConsensusExecutor` to avoid confusion
  109. * propose design for improved separation and clear information flow between
  110. `ConsensusExecutor` and `ConsensusReactor`
  111. ## Status
  112. Draft.
  113. ## Consequences
  114. ### Positive
  115. - isolated implementation of the algorithm
  116. - improved testability - simpler to proof correctness
  117. - clearer separation of concerns - easier to reason
  118. ### Negative
  119. ### Neutral