github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/orderer/consensus/solo/consensus_test.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package solo 18 19 import ( 20 "testing" 21 "time" 22 23 mockconfig "github.com/hyperledger/fabric/common/mocks/config" 24 "github.com/hyperledger/fabric/orderer/common/multichannel" 25 mockblockcutter "github.com/hyperledger/fabric/orderer/mocks/common/blockcutter" 26 mockmultichannel "github.com/hyperledger/fabric/orderer/mocks/common/multichannel" 27 cb "github.com/hyperledger/fabric/protos/common" 28 29 logging "github.com/op/go-logging" 30 "github.com/stretchr/testify/assert" 31 ) 32 33 func init() { 34 logging.SetLevel(logging.DEBUG, "") 35 } 36 37 var testMessage = &cb.Envelope{Payload: []byte("TEST_MESSAGE")} 38 39 func syncQueueMessage(msg *cb.Envelope, chain *chain, bc *mockblockcutter.Receiver) { 40 chain.Enqueue(msg) 41 bc.Block <- struct{}{} 42 } 43 44 type waitableGo struct { 45 done chan struct{} 46 } 47 48 func goWithWait(target func()) *waitableGo { 49 wg := &waitableGo{ 50 done: make(chan struct{}), 51 } 52 go func() { 53 target() 54 close(wg.done) 55 }() 56 return wg 57 } 58 59 // This test checks that if consenter is halted before a timer fires, nothing is actually written. 60 func TestHaltBeforeTimeout(t *testing.T) { 61 batchTimeout, _ := time.ParseDuration("1ms") 62 support := &mockmultichannel.ConsenterSupport{ 63 Blocks: make(chan *cb.Block), 64 BlockCutterVal: mockblockcutter.NewReceiver(), 65 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 66 } 67 defer close(support.BlockCutterVal.Block) 68 bs := newChain(support) 69 wg := goWithWait(bs.main) 70 defer bs.Halt() 71 72 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 73 bs.Halt() 74 select { 75 case <-support.Blocks: 76 t.Fatalf("Expected no invocations of Append") 77 case <-wg.done: 78 } 79 } 80 81 func TestStart(t *testing.T) { 82 batchTimeout, _ := time.ParseDuration("1ms") 83 support := &mockmultichannel.ConsenterSupport{ 84 Blocks: make(chan *cb.Block), 85 BlockCutterVal: mockblockcutter.NewReceiver(), 86 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 87 } 88 close(support.BlockCutterVal.Block) 89 bs, _ := New().HandleChain(support, nil) 90 bs.Start() 91 defer bs.Halt() 92 93 support.BlockCutterVal.CutNext = true 94 bs.Enqueue(testMessage) 95 select { 96 case <-support.Blocks: 97 case <-bs.Errored(): 98 t.Fatalf("Expected not to exit") 99 } 100 } 101 102 func TestEnqueueAfterHalt(t *testing.T) { 103 batchTimeout, _ := time.ParseDuration("1ms") 104 support := &mockmultichannel.ConsenterSupport{ 105 Blocks: make(chan *cb.Block), 106 BlockCutterVal: mockblockcutter.NewReceiver(), 107 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 108 } 109 defer close(support.BlockCutterVal.Block) 110 bs := newChain(support) 111 bs.Halt() 112 assert.False(t, bs.Enqueue(testMessage), "Enqueue should not be accepted after halt") 113 select { 114 case <-bs.Errored(): 115 default: 116 t.Fatalf("Expected Errored to be closed by halt") 117 } 118 } 119 120 func TestBatchTimer(t *testing.T) { 121 batchTimeout, _ := time.ParseDuration("1ms") 122 support := &mockmultichannel.ConsenterSupport{ 123 Blocks: make(chan *cb.Block), 124 BlockCutterVal: mockblockcutter.NewReceiver(), 125 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 126 } 127 defer close(support.BlockCutterVal.Block) 128 bs := newChain(support) 129 wg := goWithWait(bs.main) 130 defer bs.Halt() 131 132 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 133 134 select { 135 case <-support.Blocks: 136 case <-time.After(time.Second): 137 t.Fatalf("Expected a block to be cut because of batch timer expiration but did not") 138 } 139 140 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 141 select { 142 case <-support.Blocks: 143 case <-time.After(time.Second): 144 t.Fatalf("Did not create the second batch, indicating that the timer was not appopriately reset") 145 } 146 147 support.SharedConfigVal.BatchTimeoutVal, _ = time.ParseDuration("10s") 148 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 149 select { 150 case <-support.Blocks: 151 t.Fatalf("Created another batch, indicating that the timer was not appopriately re-read") 152 case <-time.After(100 * time.Millisecond): 153 } 154 155 bs.Halt() 156 select { 157 case <-support.Blocks: 158 t.Fatalf("Expected no invocations of Append") 159 case <-wg.done: 160 } 161 } 162 163 func TestBatchTimerHaltOnFilledBatch(t *testing.T) { 164 batchTimeout, _ := time.ParseDuration("1h") 165 support := &mockmultichannel.ConsenterSupport{ 166 Blocks: make(chan *cb.Block), 167 BlockCutterVal: mockblockcutter.NewReceiver(), 168 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 169 } 170 defer close(support.BlockCutterVal.Block) 171 172 bs := newChain(support) 173 wg := goWithWait(bs.main) 174 defer bs.Halt() 175 176 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 177 support.BlockCutterVal.CutNext = true 178 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 179 180 select { 181 case <-support.Blocks: 182 case <-time.After(time.Second): 183 t.Fatalf("Expected a block to be cut because the batch was filled, but did not") 184 } 185 186 // Change the batch timeout to be near instant, if the timer was not reset, it will still be waiting an hour 187 support.SharedConfigVal.BatchTimeoutVal = time.Millisecond 188 189 support.BlockCutterVal.CutNext = false 190 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 191 192 select { 193 case <-support.Blocks: 194 case <-time.After(time.Second): 195 t.Fatalf("Did not create the second batch, indicating that the old timer was still running") 196 } 197 198 bs.Halt() 199 select { 200 case <-time.After(time.Second): 201 t.Fatalf("Should have exited") 202 case <-wg.done: 203 } 204 } 205 206 func TestLargeMsgStyleMultiBatch(t *testing.T) { 207 batchTimeout, _ := time.ParseDuration("1h") 208 support := &mockmultichannel.ConsenterSupport{ 209 Blocks: make(chan *cb.Block), 210 BlockCutterVal: mockblockcutter.NewReceiver(), 211 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 212 } 213 defer close(support.BlockCutterVal.Block) 214 bs := newChain(support) 215 wg := goWithWait(bs.main) 216 defer bs.Halt() 217 218 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 219 support.BlockCutterVal.IsolatedTx = true 220 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 221 222 select { 223 case <-support.Blocks: 224 case <-time.After(time.Second): 225 t.Fatalf("Expected two blocks to be cut but never got the first") 226 } 227 228 select { 229 case <-support.Blocks: 230 case <-time.After(time.Second): 231 t.Fatalf("Expected the config type tx to create two blocks, but only go the first") 232 } 233 234 bs.Halt() 235 select { 236 case <-time.After(time.Second): 237 t.Fatalf("Should have exited") 238 case <-wg.done: 239 } 240 } 241 242 func TestConfigMsg(t *testing.T) { 243 batchTimeout, _ := time.ParseDuration("1h") 244 support := &mockmultichannel.ConsenterSupport{ 245 Blocks: make(chan *cb.Block), 246 BlockCutterVal: mockblockcutter.NewReceiver(), 247 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 248 } 249 defer close(support.BlockCutterVal.Block) 250 bs := newChain(support) 251 wg := goWithWait(bs.main) 252 defer bs.Halt() 253 254 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 255 support.ClassifyMsgVal = multichannel.ConfigUpdateMsg 256 bs.Enqueue(testMessage) 257 258 select { 259 case <-support.Blocks: 260 case <-time.After(time.Second): 261 t.Fatalf("Expected two blocks to be cut but never got the first") 262 } 263 264 select { 265 case <-support.Blocks: 266 case <-time.After(time.Second): 267 t.Fatalf("Expected the config type tx to create two blocks, but only go the first") 268 } 269 270 bs.Halt() 271 select { 272 case <-time.After(time.Second): 273 t.Fatalf("Should have exited") 274 case <-wg.done: 275 } 276 } 277 278 // This test checks that solo consenter could recover from an erroneous situation 279 // where empty batch is cut 280 func TestRecoverFromError(t *testing.T) { 281 batchTimeout, _ := time.ParseDuration("1ms") 282 support := &mockmultichannel.ConsenterSupport{ 283 Blocks: make(chan *cb.Block), 284 BlockCutterVal: mockblockcutter.NewReceiver(), 285 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 286 } 287 defer close(support.BlockCutterVal.Block) 288 bs := newChain(support) 289 _ = goWithWait(bs.main) 290 defer bs.Halt() 291 292 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 293 support.BlockCutterVal.CurBatch = nil 294 295 select { 296 case <-support.Blocks: 297 t.Fatalf("Expected no invocations of Append") 298 case <-time.After(2 * time.Millisecond): 299 } 300 301 support.BlockCutterVal.CutNext = true 302 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 303 select { 304 case <-support.Blocks: 305 case <-time.After(time.Second): 306 t.Fatalf("Expected block to be cut") 307 } 308 }