github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/blockchain/v2/processor_test.go (about) 1 package v2 2 3 import ( 4 "testing" 5 6 "github.com/stretchr/testify/assert" 7 8 "github.com/tendermint/tendermint/p2p" 9 tmState "github.com/tendermint/tendermint/state" 10 "github.com/tendermint/tendermint/types" 11 ) 12 13 // pcBlock is a test helper structure with simple types. Its purpose is to help with test readability. 14 type pcBlock struct { 15 pid string 16 height int64 17 } 18 19 // params is a test structure used to create processor state. 20 type params struct { 21 height int64 22 items []pcBlock 23 blocksSynced int 24 verBL []int64 25 appBL []int64 26 draining bool 27 } 28 29 // makePcBlock makes an empty block. 30 func makePcBlock(height int64) *types.Block { 31 return &types.Block{Header: types.Header{Height: height}} 32 } 33 34 // makeState takes test parameters and creates a specific processor state. 35 func makeState(p *params) *pcState { 36 var ( 37 tmState = tmState.State{LastBlockHeight: p.height} 38 context = newMockProcessorContext(tmState, p.verBL, p.appBL) 39 ) 40 state := newPcState(context) 41 42 for _, item := range p.items { 43 state.enqueue(p2p.ID(item.pid), makePcBlock(item.height), item.height) 44 } 45 46 state.blocksSynced = p.blocksSynced 47 state.draining = p.draining 48 return state 49 } 50 51 func mBlockResponse(peerID p2p.ID, height int64) scBlockReceived { 52 return scBlockReceived{ 53 peerID: peerID, 54 block: makePcBlock(height), 55 } 56 } 57 58 type pcFsmMakeStateValues struct { 59 currentState *params 60 event Event 61 wantState *params 62 wantNextEvent Event 63 wantErr error 64 wantPanic bool 65 } 66 67 type testFields struct { 68 name string 69 steps []pcFsmMakeStateValues 70 } 71 72 func executeProcessorTests(t *testing.T, tests []testFields) { 73 for _, tt := range tests { 74 tt := tt 75 t.Run(tt.name, func(t *testing.T) { 76 var state *pcState 77 for _, step := range tt.steps { 78 defer func() { 79 r := recover() 80 if (r != nil) != step.wantPanic { 81 t.Errorf("recover = %v, wantPanic = %v", r, step.wantPanic) 82 } 83 }() 84 85 // First step must always initialise the currentState as state. 86 if step.currentState != nil { 87 state = makeState(step.currentState) 88 } 89 if state == nil { 90 panic("Bad (initial?) step") 91 } 92 93 nextEvent, err := state.handle(step.event) 94 t.Log(state) 95 assert.Equal(t, step.wantErr, err) 96 assert.Equal(t, makeState(step.wantState), state) 97 assert.Equal(t, step.wantNextEvent, nextEvent) 98 // Next step may use the wantedState as their currentState. 99 state = makeState(step.wantState) 100 } 101 }) 102 } 103 } 104 105 func TestRProcessPeerError(t *testing.T) { 106 tests := []testFields{ 107 { 108 name: "error for existing peer", 109 steps: []pcFsmMakeStateValues{ 110 { 111 currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, 112 event: scPeerError{peerID: "P2"}, 113 wantState: ¶ms{items: []pcBlock{{"P1", 1}}}, 114 wantNextEvent: noOp, 115 }, 116 }, 117 }, 118 { 119 name: "error for unknown peer", 120 steps: []pcFsmMakeStateValues{ 121 { 122 currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, 123 event: scPeerError{peerID: "P3"}, 124 wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, 125 wantNextEvent: noOp, 126 }, 127 }, 128 }, 129 } 130 131 executeProcessorTests(t, tests) 132 } 133 134 func TestPcBlockResponse(t *testing.T) { 135 tests := []testFields{ 136 { 137 name: "add one block", 138 steps: []pcFsmMakeStateValues{ 139 { 140 currentState: ¶ms{}, event: mBlockResponse("P1", 1), 141 wantState: ¶ms{items: []pcBlock{{"P1", 1}}}, wantNextEvent: noOp, 142 }, 143 }, 144 }, 145 146 { 147 name: "add two blocks", 148 steps: []pcFsmMakeStateValues{ 149 { 150 currentState: ¶ms{}, event: mBlockResponse("P1", 3), 151 wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: noOp, 152 }, 153 { // use previous wantState as currentState, 154 event: mBlockResponse("P1", 4), 155 wantState: ¶ms{items: []pcBlock{{"P1", 3}, {"P1", 4}}}, wantNextEvent: noOp, 156 }, 157 }, 158 }, 159 } 160 161 executeProcessorTests(t, tests) 162 } 163 164 func TestRProcessBlockSuccess(t *testing.T) { 165 tests := []testFields{ 166 { 167 name: "noop - no blocks over current height", 168 steps: []pcFsmMakeStateValues{ 169 { 170 currentState: ¶ms{}, event: rProcessBlock{}, 171 wantState: ¶ms{}, wantNextEvent: noOp, 172 }, 173 }, 174 }, 175 { 176 name: "noop - high new blocks", 177 steps: []pcFsmMakeStateValues{ 178 { 179 currentState: ¶ms{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, event: rProcessBlock{}, 180 wantState: ¶ms{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, wantNextEvent: noOp, 181 }, 182 }, 183 }, 184 { 185 name: "blocks H+1 and H+2 present", 186 steps: []pcFsmMakeStateValues{ 187 { 188 currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, event: rProcessBlock{}, 189 wantState: ¶ms{height: 1, items: []pcBlock{{"P2", 2}}, blocksSynced: 1}, 190 wantNextEvent: pcBlockProcessed{height: 1, peerID: "P1"}, 191 }, 192 }, 193 }, 194 { 195 name: "blocks H+1 and H+2 present after draining", 196 steps: []pcFsmMakeStateValues{ 197 { // some contiguous blocks - on stop check draining is set 198 currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}}, 199 event: scFinishedEv{}, 200 wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}, draining: true}, 201 wantNextEvent: noOp, 202 }, 203 { 204 event: rProcessBlock{}, 205 wantState: ¶ms{height: 1, items: []pcBlock{{"P2", 2}, {"P1", 4}}, blocksSynced: 1, draining: true}, 206 wantNextEvent: pcBlockProcessed{height: 1, peerID: "P1"}, 207 }, 208 { // finish when H+1 or/and H+2 are missing 209 event: rProcessBlock{}, 210 wantState: ¶ms{height: 1, items: []pcBlock{{"P2", 2}, {"P1", 4}}, blocksSynced: 1, draining: true}, 211 wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 1}, blocksSynced: 1}, 212 }, 213 }, 214 }, 215 } 216 217 executeProcessorTests(t, tests) 218 } 219 220 func TestRProcessBlockFailures(t *testing.T) { 221 tests := []testFields{ 222 { 223 name: "blocks H+1 and H+2 present from different peers - H+1 verification fails ", 224 steps: []pcFsmMakeStateValues{ 225 { 226 currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, verBL: []int64{1}}, event: rProcessBlock{}, 227 wantState: ¶ms{items: []pcBlock{}, verBL: []int64{1}}, 228 wantNextEvent: pcBlockVerificationFailure{height: 1, firstPeerID: "P1", secondPeerID: "P2"}, 229 }, 230 }, 231 }, 232 { 233 name: "blocks H+1 and H+2 present from same peer - H+1 applyBlock fails ", 234 steps: []pcFsmMakeStateValues{ 235 { 236 currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, appBL: []int64{1}}, event: rProcessBlock{}, 237 wantState: ¶ms{items: []pcBlock{}, appBL: []int64{1}}, wantPanic: true, 238 }, 239 }, 240 }, 241 { 242 name: "blocks H+1 and H+2 present from same peers - H+1 verification fails ", 243 steps: []pcFsmMakeStateValues{ 244 { 245 currentState: ¶ms{height: 0, items: []pcBlock{{"P1", 1}, {"P1", 2}, {"P2", 3}}, 246 verBL: []int64{1}}, event: rProcessBlock{}, 247 wantState: ¶ms{height: 0, items: []pcBlock{{"P2", 3}}, verBL: []int64{1}}, 248 wantNextEvent: pcBlockVerificationFailure{height: 1, firstPeerID: "P1", secondPeerID: "P1"}, 249 }, 250 }, 251 }, 252 { 253 name: "blocks H+1 and H+2 present from different peers - H+1 applyBlock fails ", 254 steps: []pcFsmMakeStateValues{ 255 { 256 currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P2", 3}}, appBL: []int64{1}}, 257 event: rProcessBlock{}, 258 wantState: ¶ms{items: []pcBlock{{"P2", 3}}, appBL: []int64{1}}, wantPanic: true, 259 }, 260 }, 261 }, 262 } 263 264 executeProcessorTests(t, tests) 265 } 266 267 func TestScFinishedEv(t *testing.T) { 268 tests := []testFields{ 269 { 270 name: "no blocks", 271 steps: []pcFsmMakeStateValues{ 272 { 273 currentState: ¶ms{height: 100, items: []pcBlock{}, blocksSynced: 100}, event: scFinishedEv{}, 274 wantState: ¶ms{height: 100, items: []pcBlock{}, blocksSynced: 100}, 275 wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 100}, blocksSynced: 100}, 276 }, 277 }, 278 }, 279 { 280 name: "maxHeight+1 block present", 281 steps: []pcFsmMakeStateValues{ 282 { 283 currentState: ¶ms{height: 100, items: []pcBlock{ 284 {"P1", 101}}, blocksSynced: 100}, event: scFinishedEv{}, 285 wantState: ¶ms{height: 100, items: []pcBlock{{"P1", 101}}, blocksSynced: 100}, 286 wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 100}, blocksSynced: 100}, 287 }, 288 }, 289 }, 290 { 291 name: "more blocks present", 292 steps: []pcFsmMakeStateValues{ 293 { 294 currentState: ¶ms{height: 100, items: []pcBlock{ 295 {"P1", 101}, {"P1", 102}}, blocksSynced: 100}, event: scFinishedEv{}, 296 wantState: ¶ms{height: 100, items: []pcBlock{ 297 {"P1", 101}, {"P1", 102}}, blocksSynced: 100, draining: true}, 298 wantNextEvent: noOp, 299 wantErr: nil, 300 }, 301 }, 302 }, 303 } 304 305 executeProcessorTests(t, tests) 306 }