github.com/Finschia/ostracon@v1.1.5/evidence/reactor.go (about)

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