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