github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/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 bcResetState: 114 state.context.setState(event.state) 115 return noOp, nil 116 117 case scFinishedEv: 118 if state.synced() { 119 return pcFinished{tmState: state.context.tmState(), blocksSynced: state.blocksSynced}, nil 120 } 121 state.draining = true 122 return noOp, nil 123 124 case scPeerError: 125 state.purgePeer(event.peerID) 126 return noOp, nil 127 128 case scBlockReceived: 129 if event.block == nil { 130 return noOp, nil 131 } 132 133 // enqueue block if height is higher than state height, else ignore it 134 if event.block.Height > state.height() { 135 state.enqueue(event.peerID, event.block, event.block.Height) 136 } 137 return noOp, nil 138 139 case rProcessBlock: 140 tmState := state.context.tmState() 141 firstItem, secondItem, err := state.nextTwo() 142 if err != nil { 143 if state.draining { 144 return pcFinished{tmState: tmState, blocksSynced: state.blocksSynced}, nil 145 } 146 return noOp, nil 147 } 148 first, second := firstItem.block, secondItem.block 149 150 firstParts := first.MakePartSet(types.BlockPartSizeBytes) 151 firstPartSetHeader := firstParts.Header() 152 firstID := types.BlockID{Hash: first.Hash(), PartSetHeader: firstPartSetHeader} 153 154 err = state.context.verifyCommit(tmState.ChainID, firstID, first.Height, second.LastCommit) 155 if err != nil { 156 state.purgePeer(firstItem.peerID) 157 state.purgePeer(secondItem.peerID) 158 return pcBlockVerificationFailure{ 159 height: first.Height, firstPeerID: firstItem.peerID, secondPeerID: secondItem.peerID}, 160 nil 161 } 162 163 state.context.saveBlock(first, firstParts, second.LastCommit) 164 165 if err := state.context.applyBlock(firstID, first); err != nil { 166 panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err)) 167 } 168 169 delete(state.queue, first.Height) 170 state.blocksSynced++ 171 172 return pcBlockProcessed{height: first.Height, peerID: firstItem.peerID}, nil 173 174 } 175 176 return noOp, nil 177 }