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  }