github.com/okex/exchain@v1.8.0/libs/tendermint/evidence/reactor.go (about)

     1  package evidence
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"time"
     7  
     8  	amino "github.com/tendermint/go-amino"
     9  
    10  	clist "github.com/okex/exchain/libs/tendermint/libs/clist"
    11  	"github.com/okex/exchain/libs/tendermint/libs/log"
    12  	"github.com/okex/exchain/libs/tendermint/p2p"
    13  	"github.com/okex/exchain/libs/tendermint/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  // Reactor handles evpool evidence broadcasting amongst peers.
    26  type Reactor struct {
    27  	p2p.BaseReactor
    28  	evpool   *Pool
    29  	eventBus *types.EventBus
    30  }
    31  
    32  // NewReactor returns a new Reactor with the given config and evpool.
    33  func NewReactor(evpool *Pool) *Reactor {
    34  	evR := &Reactor{
    35  		evpool: evpool,
    36  	}
    37  	evR.BaseReactor = *p2p.NewBaseReactor("Evidence", evR)
    38  	return evR
    39  }
    40  
    41  // SetLogger sets the Logger on the reactor and the underlying Evidence.
    42  func (evR *Reactor) 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 *Reactor) 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 *Reactor) 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 *Reactor) 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 *ListMessage:
    83  		for _, ev := range msg.Evidence {
    84  			err := evR.evpool.AddEvidence(ev)
    85  			switch err.(type) {
    86  			case ErrInvalidEvidence:
    87  				evR.Logger.Error("Evidence is not valid", "evidence", msg.Evidence, "err", err)
    88  				// punish peer
    89  				evR.Switch.StopPeerForError(src, err)
    90  				return
    91  			case ErrEvidenceAlreadyStored:
    92  				evR.Logger.Debug("Evidence already exists", "evidence", msg.Evidence)
    93  			case nil:
    94  			default:
    95  				evR.Logger.Error("Evidence has not been added", "evidence", msg.Evidence, "err", err)
    96  				return
    97  			}
    98  		}
    99  	default:
   100  		evR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
   101  	}
   102  }
   103  
   104  // SetEventBus implements events.Eventable.
   105  func (evR *Reactor) SetEventBus(b *types.EventBus) {
   106  	evR.eventBus = b
   107  }
   108  
   109  // Modeled after the mempool routine.
   110  // - Evidence accumulates in a clist.
   111  // - Each peer has a routine that iterates through the clist,
   112  // sending available evidence to the peer.
   113  // - If we're waiting for new evidence and the list is not empty,
   114  // start iterating from the beginning again.
   115  func (evR *Reactor) broadcastEvidenceRoutine(peer p2p.Peer) {
   116  	var next *clist.CElement
   117  	var retryByte []byte
   118  	for {
   119  		// This happens because the CElement we were looking at got garbage
   120  		// collected (removed). That is, .NextWait() returned nil. Go ahead and
   121  		// start from the beginning.
   122  		if next == nil {
   123  			select {
   124  			case <-evR.evpool.EvidenceWaitChan(): // Wait until evidence is available
   125  				if next = evR.evpool.EvidenceFront(); next == nil {
   126  					continue
   127  				}
   128  			case <-peer.Quit():
   129  				return
   130  			case <-evR.Quit():
   131  				return
   132  			}
   133  		}
   134  
   135  		var retry bool
   136  		var msg Message
   137  
   138  		//try to get msg from evidence
   139  		if retryByte == nil {
   140  			ev := next.Value.(types.Evidence)
   141  			msg, retry = evR.checkSendEvidenceMessage(peer, ev)
   142  			if msg != nil {
   143  				//cache retry byte
   144  				retryByte = cdc.MustMarshalBinaryBare(msg)
   145  			}
   146  		}
   147  
   148  		// send out evidence
   149  		if !retry && retryByte != nil {
   150  			success := peer.Send(EvidenceChannel, retryByte)
   151  			retry = !success
   152  		}
   153  
   154  		if retry {
   155  			time.Sleep(peerCatchupSleepIntervalMS * time.Millisecond)
   156  			continue
   157  		}
   158  
   159  		//clean retry byte
   160  		retryByte = nil
   161  
   162  		afterCh := time.After(time.Second * broadcastEvidenceIntervalS)
   163  		select {
   164  		case <-afterCh:
   165  			// start from the beginning every tick.
   166  			// TODO: only do this if we're at the end of the list!
   167  			next = nil
   168  		case <-next.NextWaitChan():
   169  			// see the start of the for loop for nil check
   170  			next = next.Next()
   171  		case <-peer.Quit():
   172  			return
   173  		case <-evR.Quit():
   174  			return
   175  		}
   176  	}
   177  }
   178  
   179  // Returns the message to send the peer, or nil if the evidence is invalid for the peer.
   180  // If message is nil, return true if we should sleep and try again.
   181  func (evR Reactor) checkSendEvidenceMessage(
   182  	peer p2p.Peer,
   183  	ev types.Evidence,
   184  ) (msg Message, retry bool) {
   185  
   186  	// make sure the peer is up to date
   187  	evHeight := ev.Height()
   188  	peerState, ok := peer.Get(types.PeerStateKey).(PeerState)
   189  	if !ok {
   190  		// Peer does not have a state yet. We set it in the consensus reactor, but
   191  		// when we add peer in Switch, the order we call reactors#AddPeer is
   192  		// different every time due to us using a map. Sometimes other reactors
   193  		// will be initialized before the consensus reactor. We should wait a few
   194  		// milliseconds and retry.
   195  		return nil, true
   196  	}
   197  
   198  	// NOTE: We only send evidence to peers where
   199  	// peerHeight - maxAge < evidenceHeight < peerHeight
   200  	// and
   201  	// lastBlockTime - maxDuration < evidenceTime
   202  	var (
   203  		peerHeight = peerState.GetHeight()
   204  
   205  		params = evR.evpool.State().ConsensusParams.Evidence
   206  
   207  		ageDuration  = evR.evpool.State().LastBlockTime.Sub(ev.Time())
   208  		ageNumBlocks = peerHeight - evHeight
   209  	)
   210  
   211  	if peerHeight < evHeight { // peer is behind. sleep while he catches up
   212  		return nil, true
   213  	} else if ageNumBlocks > params.MaxAgeNumBlocks &&
   214  		ageDuration > params.MaxAgeDuration { // evidence is too old, skip
   215  
   216  		// NOTE: if evidence is too old for an honest peer, then we're behind and
   217  		// either it already got committed or it never will!
   218  		evR.Logger.Info("Not sending peer old evidence",
   219  			"peerHeight", peerHeight,
   220  			"evHeight", evHeight,
   221  			"maxAgeNumBlocks", params.MaxAgeNumBlocks,
   222  			"lastBlockTime", evR.evpool.State().LastBlockTime,
   223  			"evTime", ev.Time(),
   224  			"maxAgeDuration", params.MaxAgeDuration,
   225  			"peer", peer,
   226  		)
   227  
   228  		return nil, false
   229  	}
   230  
   231  	// send evidence
   232  	msg = &ListMessage{[]types.Evidence{ev}}
   233  	return msg, false
   234  }
   235  
   236  // PeerState describes the state of a peer.
   237  type PeerState interface {
   238  	GetHeight() int64
   239  }
   240  
   241  //-----------------------------------------------------------------------------
   242  // Messages
   243  
   244  // Message is a message sent or received by the Reactor.
   245  type Message interface {
   246  	ValidateBasic() error
   247  }
   248  
   249  func RegisterMessages(cdc *amino.Codec) {
   250  	cdc.RegisterInterface((*Message)(nil), nil)
   251  	cdc.RegisterConcrete(&ListMessage{},
   252  		"tendermint/evidence/ListMessage", nil)
   253  }
   254  
   255  func decodeMsg(bz []byte) (msg Message, err error) {
   256  	if len(bz) > maxMsgSize {
   257  		return msg, fmt.Errorf("msg exceeds max size (%d > %d)", len(bz), maxMsgSize)
   258  	}
   259  	err = cdc.UnmarshalBinaryBare(bz, &msg)
   260  	return
   261  }
   262  
   263  //-------------------------------------
   264  
   265  // ListMessage contains a list of evidence.
   266  type ListMessage struct {
   267  	Evidence []types.Evidence
   268  }
   269  
   270  // ValidateBasic performs basic validation.
   271  func (m *ListMessage) ValidateBasic() error {
   272  	for i, ev := range m.Evidence {
   273  		if err := ev.ValidateBasic(); err != nil {
   274  			return fmt.Errorf("invalid evidence (#%d): %v", i, err)
   275  		}
   276  	}
   277  	return nil
   278  }
   279  
   280  // String returns a string representation of the ListMessage.
   281  func (m *ListMessage) String() string {
   282  	return fmt.Sprintf("[ListMessage %v]", m.Evidence)
   283  }