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.

145 lines
3.7 KiB

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