github.com/evdatsion/aphelion-dpos-bft@v0.32.1/evidence/reactor.go (about)

     1  package evidence
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"time"
     7  
     8  	amino "github.com/evdatsion/go-amino"
     9  
    10  	clist "github.com/evdatsion/aphelion-dpos-bft/libs/clist"
    11  	"github.com/evdatsion/aphelion-dpos-bft/libs/log"
    12  	"github.com/evdatsion/aphelion-dpos-bft/p2p"
    13  	"github.com/evdatsion/aphelion-dpos-bft/types"
    14  )
    15  
    16  const (
    17  	EvidenceChannel = byte(0x38)
    18  
    19  	maxMsgSize = 1048576 // 1MB TODO make it configurable
    20  
    21  	broadcastEvidenceIntervalS = 60  // broadcast uncommitted evidence this often
    22  	peerCatchupSleepIntervalMS = 100 // If peer is behind, sleep this amount
    23  )
    24  
    25  // EvidenceReactor handles evpool evidence broadcasting amongst peers.
    26  type EvidenceReactor struct {
    27  	p2p.BaseReactor
    28  	evpool   *EvidencePool
    29  	eventBus *types.EventBus
    30  }
    31  
    32  // NewEvidenceReactor returns a new EvidenceReactor with the given config and evpool.
    33  func NewEvidenceReactor(evpool *EvidencePool) *EvidenceReactor {
    34  	evR := &EvidenceReactor{
    35  		evpool: evpool,
    36  	}
    37  	evR.BaseReactor = *p2p.NewBaseReactor("EvidenceReactor", evR)
    38  	return evR
    39  }
    40  
    41  // SetLogger sets the Logger on the reactor and the underlying Evidence.
    42  func (evR *EvidenceReactor) SetLogger(l log.Logger) {
    43  	evR.Logger = l
    44  	evR.evpool.SetLogger(l)
    45  }
    46  
    47  // GetChannels implements Reactor.
    48  // It returns the list of channels for this reactor.
    49  func (evR *EvidenceReactor) GetChannels() []*p2p.ChannelDescriptor {
    50  	return []*p2p.ChannelDescriptor{
    51  		{
    52  			ID:       EvidenceChannel,
    53  			Priority: 5,
    54  		},
    55  	}
    56  }
    57  
    58  // AddPeer implements Reactor.
    59  func (evR *EvidenceReactor) AddPeer(peer p2p.Peer) {
    60  	go evR.broadcastEvidenceRoutine(peer)
    61  }
    62  
    63  // Receive implements Reactor.
    64  // It adds any received evidence to the evpool.
    65  func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
    66  	msg, err := decodeMsg(msgBytes)
    67  	if err != nil {
    68  		evR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
    69  		evR.Switch.StopPeerForError(src, err)
    70  		return
    71  	}
    72  
    73  	if err = msg.ValidateBasic(); err != nil {
    74  		evR.Logger.Error("Peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
    75  		evR.Switch.StopPeerForError(src, err)
    76  		return
    77  	}
    78  
    79  	evR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
    80  
    81  	switch msg := msg.(type) {
    82  	case *EvidenceListMessage:
    83  		for _, ev := range msg.Evidence {
    84  			err := evR.evpool.AddEvidence(ev)
    85  			if err != nil {
    86  				evR.Logger.Info("Evidence is not valid", "evidence", msg.Evidence, "err", err)
    87  				// punish peer
    88  				evR.Switch.StopPeerForError(src, err)
    89  			}
    90  		}
    91  	default:
    92  		evR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
    93  	}
    94  }
    95  
    96  // SetEventSwitch implements events.Eventable.
    97  func (evR *EvidenceReactor) SetEventBus(b *types.EventBus) {
    98  	evR.eventBus = b
    99  }
   100  
   101  // Modeled after the mempool routine.
   102  // - Evidence accumulates in a clist.
   103  // - Each peer has a routien that iterates through the clist,
   104  // sending available evidence to the peer.
   105  // - If we're waiting for new evidence and the list is not empty,
   106  // start iterating from the beginning again.
   107  func (evR *EvidenceReactor) broadcastEvidenceRoutine(peer p2p.Peer) {
   108  	var next *clist.CElement
   109  	for {
   110  		// This happens because the CElement we were looking at got garbage
   111  		// collected (removed). That is, .NextWait() returned nil. Go ahead and
   112  		// start from the beginning.
   113  		if next == nil {
   114  			select {
   115  			case <-evR.evpool.EvidenceWaitChan(): // Wait until evidence is available
   116  				if next = evR.evpool.EvidenceFront(); next == nil {
   117  					continue
   118  				}
   119  			case <-peer.Quit():
   120  				return
   121  			case <-evR.Quit():
   122  				return
   123  			}
   124  		}
   125  
   126  		ev := next.Value.(types.Evidence)
   127  		msg, retry := evR.checkSendEvidenceMessage(peer, ev)
   128  		if msg != nil {
   129  			success := peer.Send(EvidenceChannel, cdc.MustMarshalBinaryBare(msg))
   130  			retry = !success
   131  		}
   132  
   133  		if retry {
   134  			time.Sleep(peerCatchupSleepIntervalMS * time.Millisecond)
   135  			continue
   136  		}
   137  
   138  		afterCh := time.After(time.Second * broadcastEvidenceIntervalS)
   139  		select {
   140  		case <-afterCh:
   141  			// start from the beginning every tick.
   142  			// TODO: only do this if we're at the end of the list!
   143  			next = nil
   144  		case <-next.NextWaitChan():
   145  			// see the start of the for loop for nil check
   146  			next = next.Next()
   147  		case <-peer.Quit():
   148  			return
   149  		case <-evR.Quit():
   150  			return
   151  		}
   152  	}
   153  }
   154  
   155  // Returns the message to send the peer, or nil if the evidence is invalid for the peer.
   156  // If message is nil, return true if we should sleep and try again.
   157  func (evR EvidenceReactor) checkSendEvidenceMessage(peer p2p.Peer, ev types.Evidence) (msg EvidenceMessage, retry bool) {
   158  	// make sure the peer is up to date
   159  	evHeight := ev.Height()
   160  	peerState, ok := peer.Get(types.PeerStateKey).(PeerState)
   161  	if !ok {
   162  		// Peer does not have a state yet. We set it in the consensus reactor, but
   163  		// when we add peer in Switch, the order we call reactors#AddPeer is
   164  		// different every time due to us using a map. Sometimes other reactors
   165  		// will be initialized before the consensus reactor. We should wait a few
   166  		// milliseconds and retry.
   167  		return nil, true
   168  	}
   169  
   170  	// NOTE: We only send evidence to peers where
   171  	// peerHeight - maxAge < evidenceHeight < peerHeight
   172  	maxAge := evR.evpool.State().ConsensusParams.Evidence.MaxAge
   173  	peerHeight := peerState.GetHeight()
   174  	if peerHeight < evHeight {
   175  		// peer is behind. sleep while he catches up
   176  		return nil, true
   177  	} else if peerHeight > evHeight+maxAge {
   178  		// evidence is too old, skip
   179  		// NOTE: if evidence is too old for an honest peer,
   180  		// then we're behind and either it already got committed or it never will!
   181  		evR.Logger.Info("Not sending peer old evidence", "peerHeight", peerHeight, "evHeight", evHeight, "maxAge", maxAge, "peer", peer)
   182  		return nil, false
   183  	}
   184  
   185  	// send evidence
   186  	msg = &EvidenceListMessage{[]types.Evidence{ev}}
   187  	return msg, false
   188  }
   189  
   190  // PeerState describes the state of a peer.
   191  type PeerState interface {
   192  	GetHeight() int64
   193  }
   194  
   195  //-----------------------------------------------------------------------------
   196  // Messages
   197  
   198  // EvidenceMessage is a message sent or received by the EvidenceReactor.
   199  type EvidenceMessage interface {
   200  	ValidateBasic() error
   201  }
   202  
   203  func RegisterEvidenceMessages(cdc *amino.Codec) {
   204  	cdc.RegisterInterface((*EvidenceMessage)(nil), nil)
   205  	cdc.RegisterConcrete(&EvidenceListMessage{},
   206  		"tendermint/evidence/EvidenceListMessage", nil)
   207  }
   208  
   209  func decodeMsg(bz []byte) (msg EvidenceMessage, err error) {
   210  	if len(bz) > maxMsgSize {
   211  		return msg, fmt.Errorf("Msg exceeds max size (%d > %d)", len(bz), maxMsgSize)
   212  	}
   213  	err = cdc.UnmarshalBinaryBare(bz, &msg)
   214  	return
   215  }
   216  
   217  //-------------------------------------
   218  
   219  // EvidenceListMessage contains a list of evidence.
   220  type EvidenceListMessage struct {
   221  	Evidence []types.Evidence
   222  }
   223  
   224  // ValidateBasic performs basic validation.
   225  func (m *EvidenceListMessage) ValidateBasic() error {
   226  	for i, ev := range m.Evidence {
   227  		if err := ev.ValidateBasic(); err != nil {
   228  			return fmt.Errorf("Invalid evidence (#%d): %v", i, err)
   229  		}
   230  	}
   231  	return nil
   232  }
   233  
   234  // String returns a string representation of the EvidenceListMessage.
   235  func (m *EvidenceListMessage) String() string {
   236  	return fmt.Sprintf("[EvidenceListMessage %v]", m.Evidence)
   237  }