github.com/number571/tendermint@v0.34.11-gost/internal/blockchain/v2/processor.go (about)

     1  package v2
     2  
     3  import (
     4  	"fmt"
     5  
     6  	tmState "github.com/number571/tendermint/state"
     7  	"github.com/number571/tendermint/types"
     8  )
     9  
    10  // Events generated by the processor:
    11  // block execution failure, event will indicate the peer(s) that caused the error
    12  type pcBlockVerificationFailure struct {
    13  	priorityNormal
    14  	height       int64
    15  	firstPeerID  types.NodeID
    16  	secondPeerID types.NodeID
    17  }
    18  
    19  func (e pcBlockVerificationFailure) String() string {
    20  	return fmt.Sprintf("pcBlockVerificationFailure{%d 1st peer: %v, 2nd peer: %v}",
    21  		e.height, e.firstPeerID, e.secondPeerID)
    22  }
    23  
    24  // successful block execution
    25  type pcBlockProcessed struct {
    26  	priorityNormal
    27  	height int64
    28  	peerID types.NodeID
    29  }
    30  
    31  func (e pcBlockProcessed) String() string {
    32  	return fmt.Sprintf("pcBlockProcessed{%d peer: %v}", e.height, e.peerID)
    33  }
    34  
    35  // processor has finished
    36  type pcFinished struct {
    37  	priorityNormal
    38  	blocksSynced int
    39  	tmState      tmState.State
    40  }
    41  
    42  func (p pcFinished) Error() string {
    43  	return "finished"
    44  }
    45  
    46  type queueItem struct {
    47  	block  *types.Block
    48  	peerID types.NodeID
    49  }
    50  
    51  type blockQueue map[int64]queueItem
    52  
    53  type pcState struct {
    54  	// blocks waiting to be processed
    55  	queue blockQueue
    56  
    57  	// draining indicates that the next rProcessBlock event with a queue miss constitutes completion
    58  	draining bool
    59  
    60  	// the number of blocks successfully synced by the processor
    61  	blocksSynced int
    62  
    63  	// the processorContext which contains the processor dependencies
    64  	context processorContext
    65  }
    66  
    67  func (state *pcState) String() string {
    68  	return fmt.Sprintf("height: %d queue length: %d draining: %v blocks synced: %d",
    69  		state.height(), len(state.queue), state.draining, state.blocksSynced)
    70  }
    71  
    72  // newPcState returns a pcState initialized with the last verified block enqueued
    73  func newPcState(context processorContext) *pcState {
    74  	return &pcState{
    75  		queue:        blockQueue{},
    76  		draining:     false,
    77  		blocksSynced: 0,
    78  		context:      context,
    79  	}
    80  }
    81  
    82  // nextTwo returns the next two unverified blocks
    83  func (state *pcState) nextTwo() (queueItem, queueItem, error) {
    84  	if first, ok := state.queue[state.height()+1]; ok {
    85  		if second, ok := state.queue[state.height()+2]; ok {
    86  			return first, second, nil
    87  		}
    88  	}
    89  	return queueItem{}, queueItem{}, fmt.Errorf("not found")
    90  }
    91  
    92  // synced returns true when at most the last verified block remains in the queue
    93  func (state *pcState) synced() bool {
    94  	return len(state.queue) <= 1
    95  }
    96  
    97  func (state *pcState) enqueue(peerID types.NodeID, block *types.Block, height int64) {
    98  	if item, ok := state.queue[height]; ok {
    99  		panic(fmt.Sprintf(
   100  			"duplicate block %d (%X) enqueued by processor (sent by %v; existing block %X from %v)",
   101  			height, block.Hash(), peerID, item.block.Hash(), item.peerID))
   102  	}
   103  
   104  	state.queue[height] = queueItem{block: block, peerID: peerID}
   105  }
   106  
   107  func (state *pcState) height() int64 {
   108  	return state.context.tmState().LastBlockHeight
   109  }
   110  
   111  // purgePeer moves all unprocessed blocks from the queue
   112  func (state *pcState) purgePeer(peerID types.NodeID) {
   113  	// what if height is less than state.height?
   114  	for height, item := range state.queue {
   115  		if item.peerID == peerID {
   116  			delete(state.queue, height)
   117  		}
   118  	}
   119  }
   120  
   121  // handle processes FSM events
   122  func (state *pcState) handle(event Event) (Event, error) {
   123  	switch event := event.(type) {
   124  	case bcResetState:
   125  		state.context.setState(event.state)
   126  		return noOp, nil
   127  
   128  	case scFinishedEv:
   129  		if state.synced() {
   130  			return pcFinished{tmState: state.context.tmState(), blocksSynced: state.blocksSynced}, nil
   131  		}
   132  		state.draining = true
   133  		return noOp, nil
   134  
   135  	case scPeerError:
   136  		state.purgePeer(event.peerID)
   137  		return noOp, nil
   138  
   139  	case scBlockReceived:
   140  		if event.block == nil {
   141  			return noOp, nil
   142  		}
   143  
   144  		// enqueue block if height is higher than state height, else ignore it
   145  		if event.block.Height > state.height() {
   146  			state.enqueue(event.peerID, event.block, event.block.Height)
   147  		}
   148  		return noOp, nil
   149  
   150  	case rProcessBlock:
   151  		tmState := state.context.tmState()
   152  		firstItem, secondItem, err := state.nextTwo()
   153  		if err != nil {
   154  			if state.draining {
   155  				return pcFinished{tmState: tmState, blocksSynced: state.blocksSynced}, nil
   156  			}
   157  			return noOp, nil
   158  		}
   159  
   160  		var (
   161  			first, second = firstItem.block, secondItem.block
   162  			firstParts    = first.MakePartSet(types.BlockPartSizeBytes)
   163  			firstID       = types.BlockID{Hash: first.Hash(), PartSetHeader: firstParts.Header()}
   164  		)
   165  
   166  		// verify if +second+ last commit "confirms" +first+ block
   167  		err = state.context.verifyCommit(tmState.ChainID, firstID, first.Height, second.LastCommit)
   168  		if err != nil {
   169  			state.purgePeer(firstItem.peerID)
   170  			if firstItem.peerID != secondItem.peerID {
   171  				state.purgePeer(secondItem.peerID)
   172  			}
   173  			return pcBlockVerificationFailure{
   174  					height: first.Height, firstPeerID: firstItem.peerID, secondPeerID: secondItem.peerID},
   175  				nil
   176  		}
   177  
   178  		state.context.saveBlock(first, firstParts, second.LastCommit)
   179  
   180  		if err := state.context.applyBlock(firstID, first); err != nil {
   181  			panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
   182  		}
   183  
   184  		state.context.recordConsMetrics(first)
   185  
   186  		delete(state.queue, first.Height)
   187  		state.blocksSynced++
   188  
   189  		return pcBlockProcessed{height: first.Height, peerID: firstItem.peerID}, nil
   190  	}
   191  
   192  	return noOp, nil
   193  }