github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/orderer/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 mockblockcutter "github.com/hyperledger/fabric/orderer/mocks/blockcutter" 25 mockmultichain "github.com/hyperledger/fabric/orderer/mocks/multichain" 26 cb "github.com/hyperledger/fabric/protos/common" 27 28 logging "github.com/op/go-logging" 29 "github.com/stretchr/testify/assert" 30 ) 31 32 func init() { 33 logging.SetLevel(logging.DEBUG, "") 34 } 35 36 var testMessage = &cb.Envelope{Payload: []byte("TEST_MESSAGE")} 37 38 func syncQueueMessage(msg *cb.Envelope, chain *chain, bc *mockblockcutter.Receiver) { 39 chain.Enqueue(msg) 40 bc.Block <- struct{}{} 41 } 42 43 type waitableGo struct { 44 done chan struct{} 45 } 46 47 func goWithWait(target func()) *waitableGo { 48 wg := &waitableGo{ 49 done: make(chan struct{}), 50 } 51 go func() { 52 target() 53 close(wg.done) 54 }() 55 return wg 56 } 57 58 // This test checks that if consenter is halted before a timer fires, nothing is actually written. 59 func TestHaltBeforeTimeout(t *testing.T) { 60 batchTimeout, _ := time.ParseDuration("1ms") 61 support := &mockmultichain.ConsenterSupport{ 62 Blocks: make(chan *cb.Block), 63 BlockCutterVal: mockblockcutter.NewReceiver(), 64 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 65 } 66 defer close(support.BlockCutterVal.Block) 67 bs := newChain(support) 68 wg := goWithWait(bs.main) 69 defer bs.Halt() 70 71 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 72 bs.Halt() 73 select { 74 case <-support.Blocks: 75 t.Fatalf("Expected no invocations of Append") 76 case <-wg.done: 77 } 78 } 79 80 func TestStart(t *testing.T) { 81 batchTimeout, _ := time.ParseDuration("1ms") 82 support := &mockmultichain.ConsenterSupport{ 83 Blocks: make(chan *cb.Block), 84 BlockCutterVal: mockblockcutter.NewReceiver(), 85 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 86 } 87 close(support.BlockCutterVal.Block) 88 bs, _ := New().HandleChain(support, nil) 89 bs.Start() 90 defer bs.Halt() 91 92 support.BlockCutterVal.CutNext = true 93 bs.Enqueue(testMessage) 94 select { 95 case <-support.Blocks: 96 case <-bs.Errored(): 97 t.Fatalf("Expected not to exit") 98 } 99 } 100 101 func TestEnqueueAfterHalt(t *testing.T) { 102 batchTimeout, _ := time.ParseDuration("1ms") 103 support := &mockmultichain.ConsenterSupport{ 104 Blocks: make(chan *cb.Block), 105 BlockCutterVal: mockblockcutter.NewReceiver(), 106 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 107 } 108 defer close(support.BlockCutterVal.Block) 109 bs := newChain(support) 110 bs.Halt() 111 assert.False(t, bs.Enqueue(testMessage), "Enqueue should not be accepted after halt") 112 select { 113 case <-bs.Errored(): 114 default: 115 t.Fatalf("Expected Errored to be closed by halt") 116 } 117 } 118 119 func TestBatchTimer(t *testing.T) { 120 batchTimeout, _ := time.ParseDuration("1ms") 121 support := &mockmultichain.ConsenterSupport{ 122 Blocks: make(chan *cb.Block), 123 BlockCutterVal: mockblockcutter.NewReceiver(), 124 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 125 } 126 defer close(support.BlockCutterVal.Block) 127 bs := newChain(support) 128 wg := goWithWait(bs.main) 129 defer bs.Halt() 130 131 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 132 133 select { 134 case <-support.Blocks: 135 case <-time.After(time.Second): 136 t.Fatalf("Expected a block to be cut because of batch timer expiration but did not") 137 } 138 139 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 140 select { 141 case <-support.Blocks: 142 case <-time.After(time.Second): 143 t.Fatalf("Did not create the second batch, indicating that the timer was not appopriately reset") 144 } 145 146 support.SharedConfigVal.BatchTimeoutVal, _ = time.ParseDuration("10s") 147 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 148 select { 149 case <-support.Blocks: 150 t.Fatalf("Created another batch, indicating that the timer was not appopriately re-read") 151 case <-time.After(100 * time.Millisecond): 152 } 153 154 bs.Halt() 155 select { 156 case <-support.Blocks: 157 t.Fatalf("Expected no invocations of Append") 158 case <-wg.done: 159 } 160 } 161 162 func TestBatchTimerHaltOnFilledBatch(t *testing.T) { 163 batchTimeout, _ := time.ParseDuration("1h") 164 support := &mockmultichain.ConsenterSupport{ 165 Blocks: make(chan *cb.Block), 166 BlockCutterVal: mockblockcutter.NewReceiver(), 167 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 168 } 169 defer close(support.BlockCutterVal.Block) 170 171 bs := newChain(support) 172 wg := goWithWait(bs.main) 173 defer bs.Halt() 174 175 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 176 support.BlockCutterVal.CutNext = true 177 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 178 179 select { 180 case <-support.Blocks: 181 case <-time.After(time.Second): 182 t.Fatalf("Expected a block to be cut because the batch was filled, but did not") 183 } 184 185 // Change the batch timeout to be near instant, if the timer was not reset, it will still be waiting an hour 186 support.SharedConfigVal.BatchTimeoutVal = time.Millisecond 187 188 support.BlockCutterVal.CutNext = false 189 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 190 191 select { 192 case <-support.Blocks: 193 case <-time.After(time.Second): 194 t.Fatalf("Did not create the second batch, indicating that the old timer was still running") 195 } 196 197 bs.Halt() 198 select { 199 case <-time.After(time.Second): 200 t.Fatalf("Should have exited") 201 case <-wg.done: 202 } 203 } 204 205 func TestConfigStyleMultiBatch(t *testing.T) { 206 batchTimeout, _ := time.ParseDuration("1h") 207 support := &mockmultichain.ConsenterSupport{ 208 Blocks: make(chan *cb.Block), 209 BlockCutterVal: mockblockcutter.NewReceiver(), 210 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 211 } 212 defer close(support.BlockCutterVal.Block) 213 bs := newChain(support) 214 wg := goWithWait(bs.main) 215 defer bs.Halt() 216 217 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 218 support.BlockCutterVal.IsolatedTx = true 219 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 220 221 select { 222 case <-support.Blocks: 223 case <-time.After(time.Second): 224 t.Fatalf("Expected two blocks to be cut but never got the first") 225 } 226 227 select { 228 case <-support.Blocks: 229 case <-time.After(time.Second): 230 t.Fatalf("Expected the config type tx to create two blocks, but only go the first") 231 } 232 233 bs.Halt() 234 select { 235 case <-time.After(time.Second): 236 t.Fatalf("Should have exited") 237 case <-wg.done: 238 } 239 } 240 241 // This test checks that solo consenter could recover from an erroneous situation 242 // where empty batch is cut 243 func TestRecoverFromError(t *testing.T) { 244 batchTimeout, _ := time.ParseDuration("1ms") 245 support := &mockmultichain.ConsenterSupport{ 246 Blocks: make(chan *cb.Block), 247 BlockCutterVal: mockblockcutter.NewReceiver(), 248 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 249 } 250 defer close(support.BlockCutterVal.Block) 251 bs := newChain(support) 252 _ = goWithWait(bs.main) 253 defer bs.Halt() 254 255 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 256 support.BlockCutterVal.CurBatch = nil 257 258 select { 259 case <-support.Blocks: 260 t.Fatalf("Expected no invocations of Append") 261 case <-time.After(2 * time.Millisecond): 262 } 263 264 support.BlockCutterVal.CutNext = true 265 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 266 select { 267 case <-support.Blocks: 268 case <-time.After(time.Second): 269 t.Fatalf("Expected block to be cut") 270 } 271 }