github.com/vipernet-xyz/tm@v0.34.24/evidence/reactor.go (about)

     1  package evidence
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/gogo/protobuf/proto"
     8  	clist "github.com/vipernet-xyz/tm/libs/clist"
     9  	"github.com/vipernet-xyz/tm/libs/log"
    10  	"github.com/vipernet-xyz/tm/p2p"
    11  	tmproto "github.com/vipernet-xyz/tm/proto/tendermint/types"
    12  	"github.com/vipernet-xyz/tm/types"
    13  )
    14  
    15  const (
    16  	EvidenceChannel = byte(0x38)
    17  
    18  	maxMsgSize = 1048576 // 1MB TODO make it configurable
    19  
    20  	// broadcast all uncommitted evidence this often. This sets when the reactor
    21  	// goes back to the start of the list and begins sending the evidence again.
    22  	// Most evidence should be committed in the very next block that is why we wait
    23  	// just over the block production rate before sending evidence again.
    24  	broadcastEvidenceIntervalS = 10
    25  	// If a message fails wait this much before sending it again
    26  	peerRetryMessageIntervalMS = 100
    27  )
    28  
    29  // Reactor handles evpool evidence broadcasting amongst peers.
    30  type Reactor struct {
    31  	p2p.BaseReactor
    32  	evpool   *Pool
    33  	eventBus *types.EventBus
    34  }
    35  
    36  // NewReactor returns a new Reactor with the given config and evpool.
    37  func NewReactor(evpool *Pool) *Reactor {
    38  	evR := &Reactor{
    39  		evpool: evpool,
    40  	}
    41  	evR.BaseReactor = *p2p.NewBaseReactor("Evidence", evR)
    42  	return evR
    43  }
    44  
    45  // SetLogger sets the Logger on the reactor and the underlying Evidence.
    46  func (evR *Reactor) SetLogger(l log.Logger) {
    47  	evR.Logger = l
    48  	evR.evpool.SetLogger(l)
    49  }
    50  
    51  // GetChannels implements Reactor.
    52  // It returns the list of channels for this reactor.
    53  func (evR *Reactor) GetChannels() []*p2p.ChannelDescriptor {
    54  	return []*p2p.ChannelDescriptor{
    55  		{
    56  			ID:                  EvidenceChannel,
    57  			Priority:            6,
    58  			RecvMessageCapacity: maxMsgSize,
    59  			MessageType:         &tmproto.EvidenceList{},
    60  		},
    61  	}
    62  }
    63  
    64  // AddPeer implements Reactor.
    65  func (evR *Reactor) AddPeer(peer p2p.Peer) {
    66  	go evR.broadcastEvidenceRoutine(peer)
    67  }
    68  
    69  // Receive implements Reactor.
    70  // It adds any received evidence to the evpool.
    71  func (evR *Reactor) ReceiveEnvelope(e p2p.Envelope) {
    72  	evis, err := evidenceListFromProto(e.Message)
    73  	if err != nil {
    74  		evR.Logger.Error("Error decoding message", "src", e.Src, "chId", e.ChannelID, "err", err)
    75  		evR.Switch.StopPeerForError(e.Src, err)
    76  		return
    77  	}
    78  
    79  	for _, ev := range evis {
    80  		err := evR.evpool.AddEvidence(ev)
    81  		switch err.(type) {
    82  		case *types.ErrInvalidEvidence:
    83  			evR.Logger.Error(err.Error())
    84  			// punish peer
    85  			evR.Switch.StopPeerForError(e.Src, err)
    86  			return
    87  		case nil:
    88  		default:
    89  			// continue to the next piece of evidence
    90  			evR.Logger.Error("Evidence has not been added", "evidence", evis, "err", err)
    91  		}
    92  	}
    93  }
    94  
    95  func (evR *Reactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) {
    96  	msg := &tmproto.EvidenceList{}
    97  	err := proto.Unmarshal(msgBytes, msg)
    98  	if err != nil {
    99  		panic(err)
   100  	}
   101  	evR.ReceiveEnvelope(p2p.Envelope{
   102  		ChannelID: chID,
   103  		Src:       peer,
   104  		Message:   msg,
   105  	})
   106  }
   107  
   108  // SetEventBus implements events.Eventable.
   109  func (evR *Reactor) SetEventBus(b *types.EventBus) {
   110  	evR.eventBus = b
   111  }
   112  
   113  // Modeled after the mempool routine.
   114  // - Evidence accumulates in a clist.
   115  // - Each peer has a routine that iterates through the clist,
   116  // sending available evidence to the peer.
   117  // - If we're waiting for new evidence and the list is not empty,
   118  // start iterating from the beginning again.
   119  func (evR *Reactor) broadcastEvidenceRoutine(peer p2p.Peer) {
   120  	var next *clist.CElement
   121  	for {
   122  		// This happens because the CElement we were looking at got garbage
   123  		// collected (removed). That is, .NextWait() returned nil. Go ahead and
   124  		// start from the beginning.
   125  		if next == nil {
   126  			select {
   127  			case <-evR.evpool.EvidenceWaitChan(): // Wait until evidence is available
   128  				if next = evR.evpool.EvidenceFront(); next == nil {
   129  					continue
   130  				}
   131  			case <-peer.Quit():
   132  				return
   133  			case <-evR.Quit():
   134  				return
   135  			}
   136  		} else if !peer.IsRunning() || !evR.IsRunning() {
   137  			return
   138  		}
   139  
   140  		ev := next.Value.(types.Evidence)
   141  		evis := evR.prepareEvidenceMessage(peer, ev)
   142  		if len(evis) > 0 {
   143  			evR.Logger.Debug("Gossiping evidence to peer", "ev", ev, "peer", peer)
   144  			evp, err := evidenceListToProto(evis)
   145  			if err != nil {
   146  				panic(err)
   147  			}
   148  
   149  			success := p2p.SendEnvelopeShim(peer, p2p.Envelope{ //nolint: staticcheck
   150  				ChannelID: EvidenceChannel,
   151  				Message:   evp,
   152  			}, evR.Logger)
   153  			if !success {
   154  				time.Sleep(peerRetryMessageIntervalMS * time.Millisecond)
   155  				continue
   156  			}
   157  		}
   158  
   159  		afterCh := time.After(time.Second * broadcastEvidenceIntervalS)
   160  		select {
   161  		case <-afterCh:
   162  			// start from the beginning every tick.
   163  			// TODO: only do this if we're at the end of the list!
   164  			next = nil
   165  		case <-next.NextWaitChan():
   166  			// see the start of the for loop for nil check
   167  			next = next.Next()
   168  		case <-peer.Quit():
   169  			return
   170  		case <-evR.Quit():
   171  			return
   172  		}
   173  	}
   174  }
   175  
   176  // Returns the message to send to the peer, or nil if the evidence is invalid for the peer.
   177  // If message is nil, we should sleep and try again.
   178  func (evR Reactor) prepareEvidenceMessage(
   179  	peer p2p.Peer,
   180  	ev types.Evidence,
   181  ) (evis []types.Evidence) {
   182  
   183  	// make sure the peer is up to date
   184  	evHeight := ev.Height()
   185  	peerState, ok := peer.Get(types.PeerStateKey).(PeerState)
   186  	if !ok {
   187  		// Peer does not have a state yet. We set it in the consensus reactor, but
   188  		// when we add peer in Switch, the order we call reactors#AddPeer is
   189  		// different every time due to us using a map. Sometimes other reactors
   190  		// will be initialized before the consensus reactor. We should wait a few
   191  		// milliseconds and retry.
   192  		return nil
   193  	}
   194  
   195  	// NOTE: We only send evidence to peers where
   196  	// peerHeight - maxAge < evidenceHeight < peerHeight
   197  	var (
   198  		peerHeight   = peerState.GetHeight()
   199  		params       = evR.evpool.State().ConsensusParams.Evidence
   200  		ageNumBlocks = peerHeight - evHeight
   201  	)
   202  
   203  	if peerHeight <= evHeight { // peer is behind. sleep while he catches up
   204  		return nil
   205  	} else if ageNumBlocks > params.MaxAgeNumBlocks { // evidence is too old relative to the peer, skip
   206  
   207  		// NOTE: if evidence is too old for an honest peer, then we're behind and
   208  		// either it already got committed or it never will!
   209  		evR.Logger.Info("Not sending peer old evidence",
   210  			"peerHeight", peerHeight,
   211  			"evHeight", evHeight,
   212  			"maxAgeNumBlocks", params.MaxAgeNumBlocks,
   213  			"lastBlockTime", evR.evpool.State().LastBlockTime,
   214  			"maxAgeDuration", params.MaxAgeDuration,
   215  			"peer", peer,
   216  		)
   217  
   218  		return nil
   219  	}
   220  
   221  	// send evidence
   222  	return []types.Evidence{ev}
   223  }
   224  
   225  // PeerState describes the state of a peer.
   226  type PeerState interface {
   227  	GetHeight() int64
   228  }
   229  
   230  // encodemsg takes a array of evidence
   231  // returns the byte encoding of the List Message
   232  func evidenceListToProto(evis []types.Evidence) (*tmproto.EvidenceList, error) {
   233  	evi := make([]tmproto.Evidence, len(evis))
   234  	for i := 0; i < len(evis); i++ {
   235  		ev, err := types.EvidenceToProto(evis[i])
   236  		if err != nil {
   237  			return nil, err
   238  		}
   239  		evi[i] = *ev
   240  	}
   241  	epl := tmproto.EvidenceList{
   242  		Evidence: evi,
   243  	}
   244  	return &epl, nil
   245  }
   246  
   247  func evidenceListFromProto(m proto.Message) ([]types.Evidence, error) {
   248  	lm := m.(*tmproto.EvidenceList)
   249  
   250  	evis := make([]types.Evidence, len(lm.Evidence))
   251  	for i := 0; i < len(lm.Evidence); i++ {
   252  		ev, err := types.EvidenceFromProto(&lm.Evidence[i])
   253  		if err != nil {
   254  			return nil, err
   255  		}
   256  		evis[i] = ev
   257  	}
   258  
   259  	for i, ev := range evis {
   260  		if err := ev.ValidateBasic(); err != nil {
   261  			return nil, fmt.Errorf("invalid evidence (#%d): %v", i, err)
   262  		}
   263  	}
   264  
   265  	return evis, nil
   266  }