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.

159 lines
4.2 KiB

  1. # ADR 039: Peer Behaviour Interface
  2. ## Changelog
  3. * 07-03-2019: Initial draft
  4. * 14-03-2019: Updates from feedback
  5. ## Context
  6. The responsibility for signaling and acting upon peer behaviour lacks a single
  7. owning component and is heavily coupled with the network stack[<sup>1</sup>](#references). Reactors
  8. maintain a reference to the `p2p.Switch` which they use to call
  9. `switch.StopPeerForError(...)` when a peer misbehaves and
  10. `switch.MarkAsGood(...)` when a peer contributes in some meaningful way.
  11. While the switch handles `StopPeerForError` internally, the `MarkAsGood`
  12. method delegates to another component, `p2p.AddrBook`. This scheme of delegation
  13. across Switch obscures the responsibility for handling peer behaviour
  14. and ties up the reactors in a larger dependency graph when testing.
  15. ## Decision
  16. Introduce a `PeerBehaviour` interface and concrete implementations which
  17. provide methods for reactors to signal peer behaviour without direct
  18. coupling `p2p.Switch`. Introduce a ErrorBehaviourPeer to provide
  19. concrete reasons for stopping peers. Introduce GoodBehaviourPeer to provide
  20. concrete ways in which a peer contributes.
  21. ### Implementation Changes
  22. PeerBehaviour then becomes an interface for signaling peer errors as well
  23. as for marking peers as `good`.
  24. ```go
  25. type PeerBehaviour interface {
  26. Behaved(peer Peer, reason GoodBehaviourPeer)
  27. Errored(peer Peer, reason ErrorBehaviourPeer)
  28. }
  29. ```
  30. Instead of signaling peers to stop with arbitrary reasons:
  31. `reason interface{}`
  32. We introduce a concrete error type ErrorBehaviourPeer:
  33. ```go
  34. type ErrorBehaviourPeer int
  35. const (
  36. ErrorBehaviourUnknown = iota
  37. ErrorBehaviourBadMessage
  38. ErrorBehaviourMessageOutofOrder
  39. ...
  40. )
  41. ```
  42. To provide additional information on the ways a peer contributed, we introduce
  43. the GoodBehaviourPeer type.
  44. ```go
  45. type GoodBehaviourPeer int
  46. const (
  47. GoodBehaviourVote = iota
  48. GoodBehaviourBlockPart
  49. ...
  50. )
  51. ```
  52. As a first iteration we provide a concrete implementation which wraps
  53. the switch:
  54. ```go
  55. type SwitchedPeerBehaviour struct {
  56. sw *Switch
  57. }
  58. func (spb *SwitchedPeerBehaviour) Errored(peer Peer, reason ErrorBehaviourPeer) {
  59. spb.sw.StopPeerForError(peer, reason)
  60. }
  61. func (spb *SwitchedPeerBehaviour) Behaved(peer Peer, reason GoodBehaviourPeer) {
  62. spb.sw.MarkPeerAsGood(peer)
  63. }
  64. func NewSwitchedPeerBehaviour(sw *Switch) *SwitchedPeerBehaviour {
  65. return &SwitchedPeerBehaviour{
  66. sw: sw,
  67. }
  68. }
  69. ```
  70. Reactors, which are often difficult to unit test[<sup>2</sup>](#references) could use an implementation which exposes the signals produced by the reactor in
  71. manufactured scenarios:
  72. ```go
  73. type ErrorBehaviours map[Peer][]ErrorBehaviourPeer
  74. type GoodBehaviours map[Peer][]GoodBehaviourPeer
  75. type StorePeerBehaviour struct {
  76. eb ErrorBehaviours
  77. gb GoodBehaviours
  78. }
  79. func NewStorePeerBehaviour() *StorePeerBehaviour{
  80. return &StorePeerBehaviour{
  81. eb: make(ErrorBehaviours),
  82. gb: make(GoodBehaviours),
  83. }
  84. }
  85. func (spb StorePeerBehaviour) Errored(peer Peer, reason ErrorBehaviourPeer) {
  86. if _, ok := spb.eb[peer]; !ok {
  87. spb.eb[peer] = []ErrorBehaviours{reason}
  88. } else {
  89. spb.eb[peer] = append(spb.eb[peer], reason)
  90. }
  91. }
  92. func (mpb *StorePeerBehaviour) GetErrored() ErrorBehaviours {
  93. return mpb.eb
  94. }
  95. func (spb StorePeerBehaviour) Behaved(peer Peer, reason GoodBehaviourPeer) {
  96. if _, ok := spb.gb[peer]; !ok {
  97. spb.gb[peer] = []GoodBehaviourPeer{reason}
  98. } else {
  99. spb.gb[peer] = append(spb.gb[peer], reason)
  100. }
  101. }
  102. func (spb *StorePeerBehaviour) GetBehaved() GoodBehaviours {
  103. return spb.gb
  104. }
  105. ```
  106. ## Status
  107. Accepted
  108. ## Consequences
  109. ### Positive
  110. * De-couple signaling from acting upon peer behaviour.
  111. * Reduce the coupling of reactors and the Switch and the network
  112. stack
  113. * The responsibility of managing peer behaviour can be migrated to
  114. a single component instead of split between the switch and the
  115. address book.
  116. ### Negative
  117. * The first iteration will simply wrap the Switch and introduce a
  118. level of indirection.
  119. ### Neutral
  120. ## References
  121. 1. Issue [#2067](https://github.com/tendermint/tendermint/issues/2067): P2P Refactor
  122. 2. PR: [#3506](https://github.com/tendermint/tendermint/pull/3506): ADR 036: Blockchain Reactor Refactor