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 }