github.com/defanghe/fabric@v2.1.1+incompatible/orderer/consensus/solo/consensus_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package solo
     8  
     9  import (
    10  	"fmt"
    11  	"testing"
    12  	"time"
    13  
    14  	cb "github.com/hyperledger/fabric-protos-go/common"
    15  	"github.com/hyperledger/fabric/common/channelconfig"
    16  	"github.com/hyperledger/fabric/common/flogging"
    17  	"github.com/hyperledger/fabric/orderer/consensus/solo/mocks"
    18  	mockblockcutter "github.com/hyperledger/fabric/orderer/mocks/common/blockcutter"
    19  	mockmultichannel "github.com/hyperledger/fabric/orderer/mocks/common/multichannel"
    20  	"github.com/hyperledger/fabric/protoutil"
    21  	"github.com/stretchr/testify/assert"
    22  )
    23  
    24  //go:generate counterfeiter -o mocks/orderer_config.go --fake-name OrdererConfig . ordererConfig
    25  
    26  type ordererConfig interface {
    27  	channelconfig.Orderer
    28  }
    29  
    30  func init() {
    31  	flogging.ActivateSpec("orderer.consensus.solo=DEBUG")
    32  }
    33  
    34  var testMessage = &cb.Envelope{
    35  	Payload: protoutil.MarshalOrPanic(&cb.Payload{
    36  		Header: &cb.Header{ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{ChannelId: "foo"})},
    37  		Data:   []byte("TEST_MESSAGE"),
    38  	}),
    39  }
    40  
    41  func syncQueueMessage(msg *cb.Envelope, chain *chain, bc *mockblockcutter.Receiver) {
    42  	chain.Order(msg, 0)
    43  	bc.Block <- struct{}{}
    44  }
    45  
    46  type waitableGo struct {
    47  	done chan struct{}
    48  }
    49  
    50  func goWithWait(target func()) *waitableGo {
    51  	wg := &waitableGo{
    52  		done: make(chan struct{}),
    53  	}
    54  	go func() {
    55  		target()
    56  		close(wg.done)
    57  	}()
    58  	return wg
    59  }
    60  
    61  // This test checks that if consenter is halted before a timer fires, nothing is actually written.
    62  func TestHaltBeforeTimeout(t *testing.T) {
    63  	mockOrderer := &mocks.OrdererConfig{}
    64  	mockOrderer.BatchTimeoutReturns(time.Hour)
    65  	support := &mockmultichannel.ConsenterSupport{
    66  		Blocks:          make(chan *cb.Block),
    67  		BlockCutterVal:  mockblockcutter.NewReceiver(),
    68  		SharedConfigVal: mockOrderer,
    69  	}
    70  	defer close(support.BlockCutterVal.Block)
    71  	bs := newChain(support)
    72  	wg := goWithWait(bs.main)
    73  	defer bs.Halt()
    74  
    75  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
    76  	bs.Halt()
    77  	select {
    78  	case <-support.Blocks:
    79  		t.Fatalf("Expected no invocations of Append")
    80  	case <-wg.done:
    81  	}
    82  }
    83  
    84  func TestStart(t *testing.T) {
    85  	mockOrderer := &mocks.OrdererConfig{}
    86  	mockOrderer.BatchTimeoutReturns(time.Millisecond)
    87  	support := &mockmultichannel.ConsenterSupport{
    88  		Blocks:          make(chan *cb.Block),
    89  		BlockCutterVal:  mockblockcutter.NewReceiver(),
    90  		SharedConfigVal: mockOrderer,
    91  	}
    92  	close(support.BlockCutterVal.Block)
    93  	bs, _ := New().HandleChain(support, nil)
    94  	bs.Start()
    95  	defer bs.Halt()
    96  
    97  	support.BlockCutterVal.CutNext = true
    98  	assert.Nil(t, bs.Order(testMessage, 0))
    99  	select {
   100  	case <-support.Blocks:
   101  	case <-bs.Errored():
   102  		t.Fatalf("Expected not to exit")
   103  	}
   104  }
   105  
   106  func TestOrderAfterHalt(t *testing.T) {
   107  	mockOrderer := &mocks.OrdererConfig{}
   108  	mockOrderer.BatchTimeoutReturns(time.Millisecond)
   109  	support := &mockmultichannel.ConsenterSupport{
   110  		Blocks:          make(chan *cb.Block),
   111  		BlockCutterVal:  mockblockcutter.NewReceiver(),
   112  		SharedConfigVal: mockOrderer,
   113  	}
   114  	defer close(support.BlockCutterVal.Block)
   115  	bs := newChain(support)
   116  	bs.Halt()
   117  	assert.NotNil(t, bs.Order(testMessage, 0), "Order should not be accepted after halt")
   118  	select {
   119  	case <-bs.Errored():
   120  	default:
   121  		t.Fatalf("Expected Errored to be closed by halt")
   122  	}
   123  }
   124  
   125  func TestBatchTimer(t *testing.T) {
   126  	mockOrderer := &mocks.OrdererConfig{}
   127  	mockOrderer.BatchTimeoutReturns(time.Millisecond)
   128  	support := &mockmultichannel.ConsenterSupport{
   129  		Blocks:          make(chan *cb.Block),
   130  		BlockCutterVal:  mockblockcutter.NewReceiver(),
   131  		SharedConfigVal: mockOrderer,
   132  	}
   133  	defer close(support.BlockCutterVal.Block)
   134  	bs := newChain(support)
   135  	wg := goWithWait(bs.main)
   136  	defer bs.Halt()
   137  
   138  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   139  
   140  	select {
   141  	case <-support.Blocks:
   142  	case <-time.After(time.Second):
   143  		t.Fatalf("Expected a block to be cut because of batch timer expiration but did not")
   144  	}
   145  
   146  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   147  	select {
   148  	case <-support.Blocks:
   149  	case <-time.After(time.Second):
   150  		t.Fatalf("Did not create the second batch, indicating that the timer was not appropriately reset")
   151  	}
   152  
   153  	mockOrderer.BatchTimeoutReturns(10 * time.Second)
   154  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   155  	select {
   156  	case <-support.Blocks:
   157  		t.Fatalf("Created another batch, indicating that the timer was not appropriately re-read")
   158  	case <-time.After(100 * time.Millisecond):
   159  	}
   160  
   161  	bs.Halt()
   162  	select {
   163  	case <-support.Blocks:
   164  		t.Fatalf("Expected no invocations of Append")
   165  	case <-wg.done:
   166  	}
   167  }
   168  
   169  func TestBatchTimerHaltOnFilledBatch(t *testing.T) {
   170  	mockOrderer := &mocks.OrdererConfig{}
   171  	mockOrderer.BatchTimeoutReturns(time.Hour)
   172  	support := &mockmultichannel.ConsenterSupport{
   173  		Blocks:          make(chan *cb.Block),
   174  		BlockCutterVal:  mockblockcutter.NewReceiver(),
   175  		SharedConfigVal: mockOrderer,
   176  	}
   177  	defer close(support.BlockCutterVal.Block)
   178  
   179  	bs := newChain(support)
   180  	wg := goWithWait(bs.main)
   181  	defer bs.Halt()
   182  
   183  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   184  	support.BlockCutterVal.CutNext = true
   185  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   186  
   187  	select {
   188  	case <-support.Blocks:
   189  	case <-time.After(time.Second):
   190  		t.Fatalf("Expected a block to be cut because the batch was filled, but did not")
   191  	}
   192  
   193  	// Change the batch timeout to be near instant, if the timer was not reset, it will still be waiting an hour
   194  	mockOrderer.BatchTimeoutReturns(time.Millisecond)
   195  
   196  	support.BlockCutterVal.CutNext = false
   197  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   198  
   199  	select {
   200  	case <-support.Blocks:
   201  	case <-time.After(time.Second):
   202  		t.Fatalf("Did not create the second batch, indicating that the old timer was still running")
   203  	}
   204  
   205  	bs.Halt()
   206  	select {
   207  	case <-time.After(time.Second):
   208  		t.Fatalf("Should have exited")
   209  	case <-wg.done:
   210  	}
   211  }
   212  
   213  func TestLargeMsgStyleMultiBatch(t *testing.T) {
   214  	mockOrderer := &mocks.OrdererConfig{}
   215  	mockOrderer.BatchTimeoutReturns(time.Hour)
   216  	support := &mockmultichannel.ConsenterSupport{
   217  		Blocks:          make(chan *cb.Block),
   218  		BlockCutterVal:  mockblockcutter.NewReceiver(),
   219  		SharedConfigVal: mockOrderer,
   220  	}
   221  	defer close(support.BlockCutterVal.Block)
   222  	bs := newChain(support)
   223  	wg := goWithWait(bs.main)
   224  	defer bs.Halt()
   225  
   226  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   227  	support.BlockCutterVal.IsolatedTx = true
   228  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   229  
   230  	select {
   231  	case <-support.Blocks:
   232  	case <-time.After(time.Second):
   233  		t.Fatalf("Expected two blocks to be cut but never got the first")
   234  	}
   235  
   236  	select {
   237  	case <-support.Blocks:
   238  	case <-time.After(time.Second):
   239  		t.Fatalf("Expected the config type tx to create two blocks, but only go the first")
   240  	}
   241  
   242  	bs.Halt()
   243  	select {
   244  	case <-time.After(time.Second):
   245  		t.Fatalf("Should have exited")
   246  	case <-wg.done:
   247  	}
   248  }
   249  
   250  func TestConfigMsg(t *testing.T) {
   251  	mockOrderer := &mocks.OrdererConfig{}
   252  	mockOrderer.BatchTimeoutReturns(time.Hour)
   253  	support := &mockmultichannel.ConsenterSupport{
   254  		Blocks:          make(chan *cb.Block),
   255  		BlockCutterVal:  mockblockcutter.NewReceiver(),
   256  		SharedConfigVal: mockOrderer,
   257  	}
   258  	defer close(support.BlockCutterVal.Block)
   259  	bs := newChain(support)
   260  	wg := goWithWait(bs.main)
   261  	defer bs.Halt()
   262  
   263  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   264  	assert.Nil(t, bs.Configure(testMessage, 0))
   265  
   266  	select {
   267  	case <-support.Blocks:
   268  	case <-time.After(time.Second):
   269  		t.Fatalf("Expected two blocks to be cut but never got the first")
   270  	}
   271  
   272  	select {
   273  	case <-support.Blocks:
   274  	case <-time.After(time.Second):
   275  		t.Fatalf("Expected the config type tx to create two blocks, but only go the first")
   276  	}
   277  
   278  	bs.Halt()
   279  	select {
   280  	case <-time.After(time.Second):
   281  		t.Fatalf("Should have exited")
   282  	case <-wg.done:
   283  	}
   284  }
   285  
   286  // This test checks that solo consenter could recover from an erroneous situation
   287  // where empty batch is cut
   288  func TestRecoverFromError(t *testing.T) {
   289  	mockOrderer := &mocks.OrdererConfig{}
   290  	mockOrderer.BatchTimeoutReturns(time.Millisecond)
   291  	support := &mockmultichannel.ConsenterSupport{
   292  		Blocks:          make(chan *cb.Block),
   293  		BlockCutterVal:  mockblockcutter.NewReceiver(),
   294  		SharedConfigVal: mockOrderer,
   295  	}
   296  	defer close(support.BlockCutterVal.Block)
   297  	bs := newChain(support)
   298  	go bs.main()
   299  	defer bs.Halt()
   300  
   301  	support.BlockCutterVal.SkipAppendCurBatch = true
   302  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   303  
   304  	select {
   305  	case <-support.Blocks:
   306  		t.Fatalf("Expected no invocations of Append")
   307  	case <-time.After(100 * time.Millisecond):
   308  	}
   309  
   310  	support.BlockCutterVal.SkipAppendCurBatch = false
   311  	support.BlockCutterVal.CutNext = true
   312  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   313  	select {
   314  	case <-support.Blocks:
   315  	case <-time.After(time.Second):
   316  		t.Fatalf("Expected block to be cut")
   317  	}
   318  }
   319  
   320  // This test checks that solo consenter re-validates message if config sequence has advanced
   321  func TestRevalidation(t *testing.T) {
   322  	mockOrderer := &mocks.OrdererConfig{}
   323  	mockOrderer.BatchTimeoutReturns(time.Hour)
   324  	support := &mockmultichannel.ConsenterSupport{
   325  		Blocks:          make(chan *cb.Block),
   326  		BlockCutterVal:  mockblockcutter.NewReceiver(),
   327  		SharedConfigVal: mockOrderer,
   328  		SequenceVal:     uint64(1),
   329  	}
   330  	defer close(support.BlockCutterVal.Block)
   331  	bs := newChain(support)
   332  	wg := goWithWait(bs.main)
   333  	defer bs.Halt()
   334  
   335  	t.Run("ConfigMsg", func(t *testing.T) {
   336  		support.ProcessConfigMsgVal = testMessage
   337  
   338  		t.Run("Valid", func(t *testing.T) {
   339  			assert.Nil(t, bs.Configure(testMessage, 0))
   340  
   341  			select {
   342  			case <-support.Blocks:
   343  			case <-time.After(time.Second):
   344  				t.Fatalf("Expected one block to be cut but never got it")
   345  			}
   346  		})
   347  
   348  		t.Run("Invalid", func(t *testing.T) {
   349  			support.ProcessConfigMsgErr = fmt.Errorf("Config message is not valid")
   350  			assert.Nil(t, bs.Configure(testMessage, 0))
   351  
   352  			select {
   353  			case <-support.Blocks:
   354  				t.Fatalf("Expected no block to be cut")
   355  			case <-time.After(100 * time.Millisecond):
   356  			}
   357  		})
   358  
   359  	})
   360  
   361  	t.Run("NormalMsg", func(t *testing.T) {
   362  		support.BlockCutterVal.CutNext = true
   363  
   364  		t.Run("Valid", func(t *testing.T) {
   365  			syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   366  
   367  			select {
   368  			case <-support.Blocks:
   369  			case <-time.After(time.Second):
   370  				t.Fatalf("Expected one block to be cut but never got it")
   371  			}
   372  		})
   373  
   374  		t.Run("Invalid", func(t *testing.T) {
   375  			support.ProcessNormalMsgErr = fmt.Errorf("Normal message is not valid")
   376  			// We are not calling `syncQueueMessage` here because we don't expect
   377  			// `Ordered` to be invoked at all in this case, so we don't need to
   378  			// synchronize on `support.BlockCutterVal.Block`.
   379  			assert.Nil(t, bs.Order(testMessage, 0))
   380  
   381  			select {
   382  			case <-support.Blocks:
   383  				t.Fatalf("Expected no block to be cut")
   384  			case <-time.After(100 * time.Millisecond):
   385  			}
   386  		})
   387  	})
   388  
   389  	bs.Halt()
   390  	select {
   391  	case <-time.After(time.Second):
   392  		t.Fatalf("Should have exited")
   393  	case <-wg.done:
   394  	}
   395  }
   396  
   397  func TestPendingMsgCutByTimeout(t *testing.T) {
   398  	mockOrderer := &mocks.OrdererConfig{}
   399  	mockOrderer.BatchTimeoutReturns(500 * time.Millisecond)
   400  	support := &mockmultichannel.ConsenterSupport{
   401  		Blocks:          make(chan *cb.Block),
   402  		BlockCutterVal:  mockblockcutter.NewReceiver(),
   403  		SharedConfigVal: mockOrderer,
   404  	}
   405  	defer close(support.BlockCutterVal.Block)
   406  
   407  	bs := newChain(support)
   408  	wg := goWithWait(bs.main)
   409  	defer bs.Halt()
   410  
   411  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   412  	support.BlockCutterVal.CutAncestors = true
   413  	syncQueueMessage(testMessage, bs, support.BlockCutterVal)
   414  
   415  	select {
   416  	case <-support.Blocks:
   417  	case <-time.After(time.Second):
   418  		t.Fatalf("Expected first block to be cut")
   419  	}
   420  
   421  	select {
   422  	case <-support.Blocks:
   423  	case <-time.After(time.Second):
   424  		t.Fatalf("Expected second block to be cut because of batch timer expiration but did not")
   425  	}
   426  
   427  	bs.Halt()
   428  	select {
   429  	case <-time.After(time.Second):
   430  		t.Fatalf("Should have exited")
   431  	case <-wg.done:
   432  	}
   433  }