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  }