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  }