github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/consensus/solo/consensus_test.go (about) 1 /* 2 Copyright hechain. 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 "github.com/hechain20/hechain/common/channelconfig" 15 "github.com/hechain20/hechain/common/flogging" 16 "github.com/hechain20/hechain/orderer/consensus/solo/mocks" 17 mockblockcutter "github.com/hechain20/hechain/orderer/mocks/common/blockcutter" 18 mockmultichannel "github.com/hechain20/hechain/orderer/mocks/common/multichannel" 19 "github.com/hechain20/hechain/protoutil" 20 cb "github.com/hyperledger/fabric-protos-go/common" 21 "github.com/stretchr/testify/require" 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 require.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 require.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 require.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 require.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 require.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 t.Run("NormalMsg", func(t *testing.T) { 361 support.BlockCutterVal.CutNext = true 362 363 t.Run("Valid", func(t *testing.T) { 364 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 365 366 select { 367 case <-support.Blocks: 368 case <-time.After(time.Second): 369 t.Fatalf("Expected one block to be cut but never got it") 370 } 371 }) 372 373 t.Run("Invalid", func(t *testing.T) { 374 support.ProcessNormalMsgErr = fmt.Errorf("Normal message is not valid") 375 // We are not calling `syncQueueMessage` here because we don't expect 376 // `Ordered` to be invoked at all in this case, so we don't need to 377 // synchronize on `support.BlockCutterVal.Block`. 378 require.Nil(t, bs.Order(testMessage, 0)) 379 380 select { 381 case <-support.Blocks: 382 t.Fatalf("Expected no block to be cut") 383 case <-time.After(100 * time.Millisecond): 384 } 385 }) 386 }) 387 388 bs.Halt() 389 select { 390 case <-time.After(time.Second): 391 t.Fatalf("Should have exited") 392 case <-wg.done: 393 } 394 } 395 396 func TestPendingMsgCutByTimeout(t *testing.T) { 397 mockOrderer := &mocks.OrdererConfig{} 398 mockOrderer.BatchTimeoutReturns(500 * time.Millisecond) 399 support := &mockmultichannel.ConsenterSupport{ 400 Blocks: make(chan *cb.Block), 401 BlockCutterVal: mockblockcutter.NewReceiver(), 402 SharedConfigVal: mockOrderer, 403 } 404 defer close(support.BlockCutterVal.Block) 405 406 bs := newChain(support) 407 wg := goWithWait(bs.main) 408 defer bs.Halt() 409 410 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 411 support.BlockCutterVal.CutAncestors = true 412 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 413 414 select { 415 case <-support.Blocks: 416 case <-time.After(time.Second): 417 t.Fatalf("Expected first block to be cut") 418 } 419 420 select { 421 case <-support.Blocks: 422 case <-time.After(time.Second): 423 t.Fatalf("Expected second block to be cut because of batch timer expiration but did not") 424 } 425 426 bs.Halt() 427 select { 428 case <-time.After(time.Second): 429 t.Fatalf("Should have exited") 430 case <-wg.done: 431 } 432 }