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