github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/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 bs.Halt() 147 select { 148 case <-support.Blocks: 149 t.Fatalf("Expected no invocations of Append") 150 case <-wg.done: 151 } 152 } 153 154 func TestBatchTimerHaltOnFilledBatch(t *testing.T) { 155 batchTimeout, _ := time.ParseDuration("1h") 156 support := &mockmultichain.ConsenterSupport{ 157 Blocks: make(chan *cb.Block), 158 BlockCutterVal: mockblockcutter.NewReceiver(), 159 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 160 } 161 defer close(support.BlockCutterVal.Block) 162 163 bs := newChain(support) 164 wg := goWithWait(bs.main) 165 defer bs.Halt() 166 167 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 168 support.BlockCutterVal.CutNext = true 169 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 170 171 select { 172 case <-support.Blocks: 173 case <-time.After(time.Second): 174 t.Fatalf("Expected a block to be cut because the batch was filled, but did not") 175 } 176 177 // Change the batch timeout to be near instant, if the timer was not reset, it will still be waiting an hour 178 bs.batchTimeout = time.Millisecond 179 180 support.BlockCutterVal.CutNext = false 181 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 182 183 select { 184 case <-support.Blocks: 185 case <-time.After(time.Second): 186 t.Fatalf("Did not create the second batch, indicating that the old timer was still running") 187 } 188 189 bs.Halt() 190 select { 191 case <-time.After(time.Second): 192 t.Fatalf("Should have exited") 193 case <-wg.done: 194 } 195 } 196 197 func TestConfigStyleMultiBatch(t *testing.T) { 198 batchTimeout, _ := time.ParseDuration("1h") 199 support := &mockmultichain.ConsenterSupport{ 200 Blocks: make(chan *cb.Block), 201 BlockCutterVal: mockblockcutter.NewReceiver(), 202 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 203 } 204 defer close(support.BlockCutterVal.Block) 205 bs := newChain(support) 206 wg := goWithWait(bs.main) 207 defer bs.Halt() 208 209 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 210 support.BlockCutterVal.IsolatedTx = true 211 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 212 213 select { 214 case <-support.Blocks: 215 case <-time.After(time.Second): 216 t.Fatalf("Expected two blocks to be cut but never got the first") 217 } 218 219 select { 220 case <-support.Blocks: 221 case <-time.After(time.Second): 222 t.Fatalf("Expected the config type tx to create two blocks, but only go the first") 223 } 224 225 bs.Halt() 226 select { 227 case <-time.After(time.Second): 228 t.Fatalf("Should have exited") 229 case <-wg.done: 230 } 231 } 232 233 // This test checks that solo consenter could recover from an erroneous situation 234 // where empty batch is cut 235 func TestRecoverFromError(t *testing.T) { 236 batchTimeout, _ := time.ParseDuration("1ms") 237 support := &mockmultichain.ConsenterSupport{ 238 Blocks: make(chan *cb.Block), 239 BlockCutterVal: mockblockcutter.NewReceiver(), 240 SharedConfigVal: &mockconfig.Orderer{BatchTimeoutVal: batchTimeout}, 241 } 242 defer close(support.BlockCutterVal.Block) 243 bs := newChain(support) 244 _ = goWithWait(bs.main) 245 defer bs.Halt() 246 247 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 248 support.BlockCutterVal.CurBatch = nil 249 250 select { 251 case <-support.Blocks: 252 t.Fatalf("Expected no invocations of Append") 253 case <-time.After(2 * time.Millisecond): 254 } 255 256 support.BlockCutterVal.CutNext = true 257 syncQueueMessage(testMessage, bs, support.BlockCutterVal) 258 select { 259 case <-support.Blocks: 260 case <-time.After(time.Second): 261 t.Fatalf("Expected block to be cut") 262 } 263 }