github.com/kaituanwang/hyperledger@v2.0.1+incompatible/orderer/consensus/solo/consensus_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package solo 8 9 import ( 10 "fmt" 11 "testing" 12 "time" 13 14 cb "github.com/hyperledger/fabric-protos-go/common" 15 "github.com/hyperledger/fabric/common/channelconfig" 16 "github.com/hyperledger/fabric/common/flogging" 17 "github.com/hyperledger/fabric/orderer/consensus/solo/mocks" 18 mockblockcutter "github.com/hyperledger/fabric/orderer/mocks/common/blockcutter" 19 mockmultichannel "github.com/hyperledger/fabric/orderer/mocks/common/multichannel" 20 "github.com/hyperledger/fabric/protoutil" 21 "github.com/stretchr/testify/assert" 22 ) 23 24 //go:generate counterfeiter -o mocks/orderer_config.go --fake-name OrdererConfig . ordererConfig 25 26 type ordererConfig interface { 27 channelconfig.Orderer 28 } 29 30 func init() { 31 flogging.ActivateSpec("orderer.consensus.solo=DEBUG") 32 } 33 34 var testMessage = &cb.Envelope{ 35 Payload: protoutil.MarshalOrPanic(&cb.Payload{ 36 Header: &cb.Header{ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{ChannelId: "foo"})}, 37 Data: []byte("TEST_MESSAGE"), 38 }), 39 } 40 41 func syncQueueMessage(msg *cb.Envelope, chain *chain, bc *mockblockcutter.Receiver) { 42 chain.Order(msg, 0) 43 bc.Block <- struct{}{} 44 } 45 46 type waitableGo struct { 47 done chan struct{} 48 } 49 50 func goWithWait(target func()) *waitableGo { 51 wg := &waitableGo{ 52 done: make(chan struct{}), 53 } 54 go func() { 55 target() 56 close(wg.done) 57 }() 58 return wg 59 } 60 61 // This test checks that if consenter is halted before a timer fires, nothing is actually written. 62 func TestHaltBeforeTimeout(t *testing.T) { 63 mockOrderer := &mocks.OrdererConfig{} 64 mockOrderer.BatchTimeoutReturns(time.Hour) 65 support := &mockmultichannel.ConsenterSupport{ 66 Blocks: make(chan *cb.Block), 67 BlockCutterVal: mockblockcutter.NewReceiver(), 68 SharedConfigVal: mockOrderer, 69 } 70 defer close(support.BlockCutterVal.Block) 71 bs := newChain(support) 72 wg := goWithWait(bs.main) 73 defer bs.Halt() 74 75 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 76 bs.Halt() 77 select { 78 case <-support.Blocks: 79 t.Fatalf("Expected no invocations of Append") 80 case <-wg.done: 81 } 82 } 83 84 func TestStart(t *testing.T) { 85 mockOrderer := &mocks.OrdererConfig{} 86 mockOrderer.BatchTimeoutReturns(time.Millisecond) 87 support := &mockmultichannel.ConsenterSupport{ 88 Blocks: make(chan *cb.Block), 89 BlockCutterVal: mockblockcutter.NewReceiver(), 90 SharedConfigVal: mockOrderer, 91 } 92 close(support.BlockCutterVal.Block) 93 bs, _ := New().HandleChain(support, nil) 94 bs.Start() 95 defer bs.Halt() 96 97 support.BlockCutterVal.CutNext = true 98 assert.Nil(t, bs.Order(testMessage, 0)) 99 select { 100 case <-support.Blocks: 101 case <-bs.Errored(): 102 t.Fatalf("Expected not to exit") 103 } 104 } 105 106 func TestOrderAfterHalt(t *testing.T) { 107 mockOrderer := &mocks.OrdererConfig{} 108 mockOrderer.BatchTimeoutReturns(time.Millisecond) 109 support := &mockmultichannel.ConsenterSupport{ 110 Blocks: make(chan *cb.Block), 111 BlockCutterVal: mockblockcutter.NewReceiver(), 112 SharedConfigVal: mockOrderer, 113 } 114 defer close(support.BlockCutterVal.Block) 115 bs := newChain(support) 116 bs.Halt() 117 assert.NotNil(t, bs.Order(testMessage, 0), "Order should not be accepted after halt") 118 select { 119 case <-bs.Errored(): 120 default: 121 t.Fatalf("Expected Errored to be closed by halt") 122 } 123 } 124 125 func TestBatchTimer(t *testing.T) { 126 mockOrderer := &mocks.OrdererConfig{} 127 mockOrderer.BatchTimeoutReturns(time.Millisecond) 128 support := &mockmultichannel.ConsenterSupport{ 129 Blocks: make(chan *cb.Block), 130 BlockCutterVal: mockblockcutter.NewReceiver(), 131 SharedConfigVal: mockOrderer, 132 } 133 defer close(support.BlockCutterVal.Block) 134 bs := newChain(support) 135 wg := goWithWait(bs.main) 136 defer bs.Halt() 137 138 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 139 140 select { 141 case <-support.Blocks: 142 case <-time.After(time.Second): 143 t.Fatalf("Expected a block to be cut because of batch timer expiration but did not") 144 } 145 146 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 147 select { 148 case <-support.Blocks: 149 case <-time.After(time.Second): 150 t.Fatalf("Did not create the second batch, indicating that the timer was not appropriately reset") 151 } 152 153 mockOrderer.BatchTimeoutReturns(10 * time.Second) 154 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 155 select { 156 case <-support.Blocks: 157 t.Fatalf("Created another batch, indicating that the timer was not appropriately re-read") 158 case <-time.After(100 * time.Millisecond): 159 } 160 161 bs.Halt() 162 select { 163 case <-support.Blocks: 164 t.Fatalf("Expected no invocations of Append") 165 case <-wg.done: 166 } 167 } 168 169 func TestBatchTimerHaltOnFilledBatch(t *testing.T) { 170 mockOrderer := &mocks.OrdererConfig{} 171 mockOrderer.BatchTimeoutReturns(time.Hour) 172 support := &mockmultichannel.ConsenterSupport{ 173 Blocks: make(chan *cb.Block), 174 BlockCutterVal: mockblockcutter.NewReceiver(), 175 SharedConfigVal: mockOrderer, 176 } 177 defer close(support.BlockCutterVal.Block) 178 179 bs := newChain(support) 180 wg := goWithWait(bs.main) 181 defer bs.Halt() 182 183 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 184 support.BlockCutterVal.CutNext = true 185 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 186 187 select { 188 case <-support.Blocks: 189 case <-time.After(time.Second): 190 t.Fatalf("Expected a block to be cut because the batch was filled, but did not") 191 } 192 193 // Change the batch timeout to be near instant, if the timer was not reset, it will still be waiting an hour 194 mockOrderer.BatchTimeoutReturns(time.Millisecond) 195 196 support.BlockCutterVal.CutNext = false 197 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 198 199 select { 200 case <-support.Blocks: 201 case <-time.After(time.Second): 202 t.Fatalf("Did not create the second batch, indicating that the old timer was still running") 203 } 204 205 bs.Halt() 206 select { 207 case <-time.After(time.Second): 208 t.Fatalf("Should have exited") 209 case <-wg.done: 210 } 211 } 212 213 func TestLargeMsgStyleMultiBatch(t *testing.T) { 214 mockOrderer := &mocks.OrdererConfig{} 215 mockOrderer.BatchTimeoutReturns(time.Hour) 216 support := &mockmultichannel.ConsenterSupport{ 217 Blocks: make(chan *cb.Block), 218 BlockCutterVal: mockblockcutter.NewReceiver(), 219 SharedConfigVal: mockOrderer, 220 } 221 defer close(support.BlockCutterVal.Block) 222 bs := newChain(support) 223 wg := goWithWait(bs.main) 224 defer bs.Halt() 225 226 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 227 support.BlockCutterVal.IsolatedTx = true 228 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 229 230 select { 231 case <-support.Blocks: 232 case <-time.After(time.Second): 233 t.Fatalf("Expected two blocks to be cut but never got the first") 234 } 235 236 select { 237 case <-support.Blocks: 238 case <-time.After(time.Second): 239 t.Fatalf("Expected the config type tx to create two blocks, but only go the first") 240 } 241 242 bs.Halt() 243 select { 244 case <-time.After(time.Second): 245 t.Fatalf("Should have exited") 246 case <-wg.done: 247 } 248 } 249 250 func TestConfigMsg(t *testing.T) { 251 mockOrderer := &mocks.OrdererConfig{} 252 mockOrderer.BatchTimeoutReturns(time.Hour) 253 support := &mockmultichannel.ConsenterSupport{ 254 Blocks: make(chan *cb.Block), 255 BlockCutterVal: mockblockcutter.NewReceiver(), 256 SharedConfigVal: mockOrderer, 257 } 258 defer close(support.BlockCutterVal.Block) 259 bs := newChain(support) 260 wg := goWithWait(bs.main) 261 defer bs.Halt() 262 263 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 264 assert.Nil(t, bs.Configure(testMessage, 0)) 265 266 select { 267 case <-support.Blocks: 268 case <-time.After(time.Second): 269 t.Fatalf("Expected two blocks to be cut but never got the first") 270 } 271 272 select { 273 case <-support.Blocks: 274 case <-time.After(time.Second): 275 t.Fatalf("Expected the config type tx to create two blocks, but only go the first") 276 } 277 278 bs.Halt() 279 select { 280 case <-time.After(time.Second): 281 t.Fatalf("Should have exited") 282 case <-wg.done: 283 } 284 } 285 286 // This test checks that solo consenter could recover from an erroneous situation 287 // where empty batch is cut 288 func TestRecoverFromError(t *testing.T) { 289 mockOrderer := &mocks.OrdererConfig{} 290 mockOrderer.BatchTimeoutReturns(time.Millisecond) 291 support := &mockmultichannel.ConsenterSupport{ 292 Blocks: make(chan *cb.Block), 293 BlockCutterVal: mockblockcutter.NewReceiver(), 294 SharedConfigVal: mockOrderer, 295 } 296 defer close(support.BlockCutterVal.Block) 297 bs := newChain(support) 298 go bs.main() 299 defer bs.Halt() 300 301 support.BlockCutterVal.SkipAppendCurBatch = true 302 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 303 304 select { 305 case <-support.Blocks: 306 t.Fatalf("Expected no invocations of Append") 307 case <-time.After(100 * time.Millisecond): 308 } 309 310 support.BlockCutterVal.SkipAppendCurBatch = false 311 support.BlockCutterVal.CutNext = true 312 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 313 select { 314 case <-support.Blocks: 315 case <-time.After(time.Second): 316 t.Fatalf("Expected block to be cut") 317 } 318 } 319 320 // This test checks that solo consenter re-validates message if config sequence has advanced 321 func TestRevalidation(t *testing.T) { 322 mockOrderer := &mocks.OrdererConfig{} 323 mockOrderer.BatchTimeoutReturns(time.Hour) 324 support := &mockmultichannel.ConsenterSupport{ 325 Blocks: make(chan *cb.Block), 326 BlockCutterVal: mockblockcutter.NewReceiver(), 327 SharedConfigVal: mockOrderer, 328 SequenceVal: uint64(1), 329 } 330 defer close(support.BlockCutterVal.Block) 331 bs := newChain(support) 332 wg := goWithWait(bs.main) 333 defer bs.Halt() 334 335 t.Run("ConfigMsg", func(t *testing.T) { 336 support.ProcessConfigMsgVal = testMessage 337 338 t.Run("Valid", func(t *testing.T) { 339 assert.Nil(t, bs.Configure(testMessage, 0)) 340 341 select { 342 case <-support.Blocks: 343 case <-time.After(time.Second): 344 t.Fatalf("Expected one block to be cut but never got it") 345 } 346 }) 347 348 t.Run("Invalid", func(t *testing.T) { 349 support.ProcessConfigMsgErr = fmt.Errorf("Config message is not valid") 350 assert.Nil(t, bs.Configure(testMessage, 0)) 351 352 select { 353 case <-support.Blocks: 354 t.Fatalf("Expected no block to be cut") 355 case <-time.After(100 * time.Millisecond): 356 } 357 }) 358 359 }) 360 361 t.Run("NormalMsg", func(t *testing.T) { 362 support.BlockCutterVal.CutNext = true 363 364 t.Run("Valid", func(t *testing.T) { 365 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 366 367 select { 368 case <-support.Blocks: 369 case <-time.After(time.Second): 370 t.Fatalf("Expected one block to be cut but never got it") 371 } 372 }) 373 374 t.Run("Invalid", func(t *testing.T) { 375 support.ProcessNormalMsgErr = fmt.Errorf("Normal message is not valid") 376 // We are not calling `syncQueueMessage` here because we don't expect 377 // `Ordered` to be invoked at all in this case, so we don't need to 378 // synchronize on `support.BlockCutterVal.Block`. 379 assert.Nil(t, bs.Order(testMessage, 0)) 380 381 select { 382 case <-support.Blocks: 383 t.Fatalf("Expected no block to be cut") 384 case <-time.After(100 * time.Millisecond): 385 } 386 }) 387 }) 388 389 bs.Halt() 390 select { 391 case <-time.After(time.Second): 392 t.Fatalf("Should have exited") 393 case <-wg.done: 394 } 395 } 396 397 func TestPendingMsgCutByTimeout(t *testing.T) { 398 mockOrderer := &mocks.OrdererConfig{} 399 mockOrderer.BatchTimeoutReturns(500 * time.Millisecond) 400 support := &mockmultichannel.ConsenterSupport{ 401 Blocks: make(chan *cb.Block), 402 BlockCutterVal: mockblockcutter.NewReceiver(), 403 SharedConfigVal: mockOrderer, 404 } 405 defer close(support.BlockCutterVal.Block) 406 407 bs := newChain(support) 408 wg := goWithWait(bs.main) 409 defer bs.Halt() 410 411 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 412 support.BlockCutterVal.CutAncestors = true 413 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 414 415 select { 416 case <-support.Blocks: 417 case <-time.After(time.Second): 418 t.Fatalf("Expected first block to be cut") 419 } 420 421 select { 422 case <-support.Blocks: 423 case <-time.After(time.Second): 424 t.Fatalf("Expected second block to be cut because of batch timer expiration but did not") 425 } 426 427 bs.Halt() 428 select { 429 case <-time.After(time.Second): 430 t.Fatalf("Should have exited") 431 case <-wg.done: 432 } 433 }