github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/core/deliverservice/client_test.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 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 deliverclient
    18  
    19  import (
    20  	"crypto/sha256"
    21  	"errors"
    22  	"math"
    23  	"sync"
    24  	"sync/atomic"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/hyperledger/fabric/core/comm"
    29  	"github.com/hyperledger/fabric/core/deliverservice/blocksprovider"
    30  	"github.com/hyperledger/fabric/core/deliverservice/mocks"
    31  	"github.com/hyperledger/fabric/protos/common"
    32  	"github.com/hyperledger/fabric/protos/orderer"
    33  	"github.com/hyperledger/fabric/protos/utils"
    34  	"github.com/stretchr/testify/assert"
    35  	"golang.org/x/net/context"
    36  	"google.golang.org/grpc"
    37  )
    38  
    39  var connNumber = 0
    40  
    41  func newConnection() *grpc.ClientConn {
    42  	connNumber++
    43  	// The balancer is in order to check connection leaks.
    44  	// When grpc.ClientConn.Close() is called, it calls the balancer's Close()
    45  	// method which decrements the connNumber
    46  	cc, _ := grpc.Dial("", grpc.WithInsecure(), grpc.WithBalancer(&balancer{}))
    47  	return cc
    48  }
    49  
    50  type balancer struct {
    51  }
    52  
    53  func (*balancer) Start(target string) error {
    54  	return nil
    55  }
    56  
    57  func (*balancer) Up(addr grpc.Address) (down func(error)) {
    58  	return func(error) {}
    59  }
    60  
    61  func (*balancer) Get(ctx context.Context, opts grpc.BalancerGetOptions) (addr grpc.Address, put func(), err error) {
    62  	return grpc.Address{}, func() {}, errors.New("")
    63  }
    64  
    65  func (*balancer) Notify() <-chan []grpc.Address {
    66  	return nil
    67  }
    68  
    69  func (*balancer) Close() error {
    70  	connNumber--
    71  	return nil
    72  }
    73  
    74  type blocksDelivererConsumer func(blocksprovider.BlocksDeliverer) error
    75  
    76  var blockDelivererConsumerWithRecv = func(bd blocksprovider.BlocksDeliverer) error {
    77  	_, err := bd.Recv()
    78  	return err
    79  }
    80  
    81  var blockDelivererConsumerWithSend = func(bd blocksprovider.BlocksDeliverer) error {
    82  	return bd.Send(&common.Envelope{})
    83  }
    84  
    85  type abc struct {
    86  	shouldFail bool
    87  	grpc.ClientStream
    88  }
    89  
    90  func (a *abc) Send(*common.Envelope) error {
    91  	if a.shouldFail {
    92  		return errors.New("Failed sending")
    93  	}
    94  	return nil
    95  }
    96  
    97  func (a *abc) Recv() (*orderer.DeliverResponse, error) {
    98  	if a.shouldFail {
    99  		return nil, errors.New("Failed Recv")
   100  	}
   101  	return &orderer.DeliverResponse{}, nil
   102  }
   103  
   104  type abclient struct {
   105  	shouldFail bool
   106  	stream     *abc
   107  }
   108  
   109  func (ac *abclient) Broadcast(ctx context.Context, opts ...grpc.CallOption) (orderer.AtomicBroadcast_BroadcastClient, error) {
   110  	panic("Not implemented")
   111  }
   112  
   113  func (ac *abclient) Deliver(ctx context.Context, opts ...grpc.CallOption) (orderer.AtomicBroadcast_DeliverClient, error) {
   114  	if ac.stream != nil {
   115  		return ac.stream, nil
   116  	}
   117  	if ac.shouldFail {
   118  		return nil, errors.New("Failed creating ABC")
   119  	}
   120  	return &abc{}, nil
   121  }
   122  
   123  type connProducer struct {
   124  	shouldFail      bool
   125  	connAttempts    int
   126  	connTime        time.Duration
   127  	ordererEndpoint string
   128  }
   129  
   130  func (cp *connProducer) realConnection() (*grpc.ClientConn, string, error) {
   131  	cc, err := grpc.Dial(cp.ordererEndpoint, grpc.WithInsecure())
   132  	if err != nil {
   133  		return nil, "", err
   134  	}
   135  	return cc, cp.ordererEndpoint, nil
   136  }
   137  
   138  func (cp *connProducer) NewConnection() (*grpc.ClientConn, string, error) {
   139  	time.Sleep(cp.connTime)
   140  	cp.connAttempts++
   141  	if cp.ordererEndpoint != "" {
   142  		return cp.realConnection()
   143  	}
   144  	if cp.shouldFail {
   145  		return nil, "", errors.New("Failed connecting")
   146  	}
   147  	return newConnection(), "localhost:5611", nil
   148  }
   149  
   150  // UpdateEndpoints updates the endpoints of the ConnectionProducer
   151  // to be the given endpoints
   152  func (cp *connProducer) UpdateEndpoints(endpoints []string) {
   153  	panic("Not implemented")
   154  }
   155  
   156  func TestOrderingServiceConnFailure(t *testing.T) {
   157  	testOrderingServiceConnFailure(t, blockDelivererConsumerWithRecv)
   158  	testOrderingServiceConnFailure(t, blockDelivererConsumerWithSend)
   159  	assert.Equal(t, 0, connNumber)
   160  }
   161  
   162  func testOrderingServiceConnFailure(t *testing.T, bdc blocksDelivererConsumer) {
   163  	// Scenario: Create a broadcast client and call Recv/Send.
   164  	// Connection to the ordering service should be possible only at second attempt
   165  	cp := &connProducer{shouldFail: true}
   166  	clFactory := func(*grpc.ClientConn) orderer.AtomicBroadcastClient {
   167  		return &abclient{}
   168  	}
   169  	setupInvoked := 0
   170  	setup := func(blocksprovider.BlocksDeliverer) error {
   171  		setupInvoked++
   172  		return nil
   173  	}
   174  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   175  		// When called with the second attempt (iteration number 1),
   176  		// we should be able to connect to the ordering service.
   177  		// Set connection provider mock shouldFail flag to false
   178  		// to enable next connection attempt to succeed
   179  		if attemptNum == 1 {
   180  			cp.shouldFail = false
   181  		}
   182  
   183  		return time.Duration(0), attemptNum < 2
   184  	}
   185  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   186  	defer bc.Close()
   187  	err := bdc(bc)
   188  	assert.NoError(t, err)
   189  	assert.Equal(t, 2, cp.connAttempts)
   190  	assert.Equal(t, 1, setupInvoked)
   191  }
   192  
   193  func TestOrderingServiceStreamFailure(t *testing.T) {
   194  	testOrderingServiceStreamFailure(t, blockDelivererConsumerWithRecv)
   195  	testOrderingServiceStreamFailure(t, blockDelivererConsumerWithSend)
   196  	assert.Equal(t, 0, connNumber)
   197  }
   198  
   199  func testOrderingServiceStreamFailure(t *testing.T, bdc blocksDelivererConsumer) {
   200  	// Scenario: Create a broadcast client and call Recv/Send.
   201  	// Connection to the ordering service should be possible at first attempt,
   202  	// but the atomic broadcast client creation fails at first attempt
   203  	abcClient := &abclient{shouldFail: true}
   204  	cp := &connProducer{}
   205  	clFactory := func(*grpc.ClientConn) orderer.AtomicBroadcastClient {
   206  		return abcClient
   207  	}
   208  	setupInvoked := 0
   209  	setup := func(blocksprovider.BlocksDeliverer) error {
   210  		setupInvoked++
   211  		return nil
   212  	}
   213  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   214  		// When called with the second attempt (iteration number 1),
   215  		// we should be able to finally call Deliver() by the atomic broadcast client
   216  		if attemptNum == 1 {
   217  			abcClient.shouldFail = false
   218  		}
   219  		return time.Duration(0), attemptNum < 2
   220  	}
   221  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   222  	defer bc.Close()
   223  	err := bdc(bc)
   224  	assert.NoError(t, err)
   225  	assert.Equal(t, 2, cp.connAttempts)
   226  	assert.Equal(t, 1, setupInvoked)
   227  }
   228  
   229  func TestOrderingServiceSetupFailure(t *testing.T) {
   230  	testOrderingServiceSetupFailure(t, blockDelivererConsumerWithRecv)
   231  	testOrderingServiceSetupFailure(t, blockDelivererConsumerWithSend)
   232  	assert.Equal(t, 0, connNumber)
   233  }
   234  
   235  func testOrderingServiceSetupFailure(t *testing.T, bdc blocksDelivererConsumer) {
   236  	// Scenario: Create a broadcast client and call Recv/Send.
   237  	// Connection to the ordering service should be possible,
   238  	// the atomic broadcast client creation succeeds, but invoking the setup function
   239  	// fails at the first attempt and succeeds at the second one.
   240  	cp := &connProducer{}
   241  	clFactory := func(*grpc.ClientConn) orderer.AtomicBroadcastClient {
   242  		return &abclient{}
   243  	}
   244  	setupInvoked := 0
   245  	setup := func(blocksprovider.BlocksDeliverer) error {
   246  		setupInvoked++
   247  		if setupInvoked == 1 {
   248  			return errors.New("Setup failed")
   249  		}
   250  		return nil
   251  	}
   252  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   253  		return time.Duration(0), true
   254  	}
   255  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   256  	defer bc.Close()
   257  	err := bdc(bc)
   258  	assert.NoError(t, err)
   259  	assert.Equal(t, 2, cp.connAttempts)
   260  	assert.Equal(t, 2, setupInvoked)
   261  }
   262  
   263  func TestOrderingServiceFirstOperationFailure(t *testing.T) {
   264  	testOrderingServiceFirstOperationFailure(t, blockDelivererConsumerWithRecv)
   265  	testOrderingServiceFirstOperationFailure(t, blockDelivererConsumerWithSend)
   266  	assert.Equal(t, 0, connNumber)
   267  }
   268  
   269  func testOrderingServiceFirstOperationFailure(t *testing.T, bdc blocksDelivererConsumer) {
   270  	// Scenario: Creation and connection to the ordering service succeeded
   271  	// but the first Recv/Send failed.
   272  	// The client should reconnect to the ordering service
   273  	cp := &connProducer{}
   274  	abStream := &abc{shouldFail: true}
   275  	abcClient := &abclient{stream: abStream}
   276  	clFactory := func(*grpc.ClientConn) orderer.AtomicBroadcastClient {
   277  		return abcClient
   278  	}
   279  
   280  	setupInvoked := 0
   281  	setup := func(blocksprovider.BlocksDeliverer) error {
   282  		// Fix stream success logic at 2nd attempt
   283  		if setupInvoked == 1 {
   284  			abStream.shouldFail = false
   285  		}
   286  		setupInvoked++
   287  		return nil
   288  	}
   289  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   290  		return time.Duration(0), true
   291  	}
   292  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   293  	defer bc.Close()
   294  	err := bdc(bc)
   295  	assert.NoError(t, err)
   296  	assert.Equal(t, 2, setupInvoked)
   297  	assert.Equal(t, cp.connAttempts, 2)
   298  }
   299  
   300  func TestOrderingServiceCrashAndRecover(t *testing.T) {
   301  	testOrderingServiceCrashAndRecover(t, blockDelivererConsumerWithRecv)
   302  	testOrderingServiceCrashAndRecover(t, blockDelivererConsumerWithSend)
   303  	assert.Equal(t, 0, connNumber)
   304  }
   305  
   306  func testOrderingServiceCrashAndRecover(t *testing.T, bdc blocksDelivererConsumer) {
   307  	// Scenario: The ordering service is OK at first usage of Recv/Send,
   308  	// but subsequent calls fails because of connection error.
   309  	// A reconnect is needed and only then Recv/Send should succeed
   310  	cp := &connProducer{}
   311  	abStream := &abc{}
   312  	abcClient := &abclient{stream: abStream}
   313  	clFactory := func(*grpc.ClientConn) orderer.AtomicBroadcastClient {
   314  		return abcClient
   315  	}
   316  
   317  	setupInvoked := 0
   318  	setup := func(blocksprovider.BlocksDeliverer) error {
   319  		// Fix stream success logic at 2nd attempt
   320  		if setupInvoked == 1 {
   321  			abStream.shouldFail = false
   322  		}
   323  		setupInvoked++
   324  		return nil
   325  	}
   326  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   327  		return time.Duration(0), true
   328  	}
   329  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   330  	defer bc.Close()
   331  	err := bdc(bc)
   332  	assert.NoError(t, err)
   333  	// Now fail the subsequent Recv/Send
   334  	abStream.shouldFail = true
   335  	err = bdc(bc)
   336  	assert.NoError(t, err)
   337  	assert.Equal(t, 2, cp.connAttempts)
   338  	assert.Equal(t, 2, setupInvoked)
   339  }
   340  
   341  func TestOrderingServicePermanentCrash(t *testing.T) {
   342  	testOrderingServicePermanentCrash(t, blockDelivererConsumerWithRecv)
   343  	testOrderingServicePermanentCrash(t, blockDelivererConsumerWithSend)
   344  	assert.Equal(t, 0, connNumber)
   345  }
   346  
   347  func testOrderingServicePermanentCrash(t *testing.T, bdc blocksDelivererConsumer) {
   348  	// Scenario: The ordering service is OK at first usage of Recv/Send,
   349  	// but subsequent calls fail because it crashes.
   350  	// The client should give up after 10 attempts in the second reconnect
   351  	cp := &connProducer{}
   352  	abStream := &abc{}
   353  	abcClient := &abclient{stream: abStream}
   354  	clFactory := func(*grpc.ClientConn) orderer.AtomicBroadcastClient {
   355  		return abcClient
   356  	}
   357  
   358  	setupInvoked := 0
   359  	setup := func(blocksprovider.BlocksDeliverer) error {
   360  		setupInvoked++
   361  		return nil
   362  	}
   363  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   364  		return time.Duration(0), attemptNum < 10
   365  	}
   366  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   367  	defer bc.Close()
   368  	err := bdc(bc)
   369  	assert.NoError(t, err)
   370  	// Now fail the subsequent Recv/Send
   371  	abStream.shouldFail = true
   372  	cp.shouldFail = true
   373  	err = bdc(bc)
   374  	assert.Error(t, err)
   375  	assert.Equal(t, 10, cp.connAttempts)
   376  	assert.Equal(t, 1, setupInvoked)
   377  }
   378  
   379  func TestLimitedConnAttempts(t *testing.T) {
   380  	testLimitedConnAttempts(t, blockDelivererConsumerWithRecv)
   381  	testLimitedConnAttempts(t, blockDelivererConsumerWithSend)
   382  	assert.Equal(t, 0, connNumber)
   383  }
   384  
   385  func testLimitedConnAttempts(t *testing.T, bdc blocksDelivererConsumer) {
   386  	// Scenario: The ordering service isn't available, and the backoff strategy
   387  	// specifies an upper bound of 10 connection attempts
   388  	cp := &connProducer{shouldFail: true}
   389  	clFactory := func(*grpc.ClientConn) orderer.AtomicBroadcastClient {
   390  		return &abclient{}
   391  	}
   392  	setupInvoked := 0
   393  	setup := func(blocksprovider.BlocksDeliverer) error {
   394  		setupInvoked++
   395  		return nil
   396  	}
   397  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   398  		return time.Duration(0), attemptNum < 10
   399  	}
   400  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   401  	defer bc.Close()
   402  	err := bdc(bc)
   403  	assert.Error(t, err)
   404  	assert.Equal(t, 10, cp.connAttempts)
   405  	assert.Equal(t, 0, setupInvoked)
   406  }
   407  
   408  func TestLimitedTotalConnTimeRcv(t *testing.T) {
   409  	testLimitedTotalConnTime(t, blockDelivererConsumerWithRecv)
   410  	assert.Equal(t, 0, connNumber)
   411  }
   412  
   413  func TestLimitedTotalConnTimeSnd(t *testing.T) {
   414  	testLimitedTotalConnTime(t, blockDelivererConsumerWithSend)
   415  	assert.Equal(t, 0, connNumber)
   416  }
   417  
   418  func testLimitedTotalConnTime(t *testing.T, bdc blocksDelivererConsumer) {
   419  	// Scenario: The ordering service isn't available, and the backoff strategy
   420  	// specifies an upper bound of 1 second
   421  	// The first attempt fails, and the second attempt doesn't take place
   422  	// becuse the creation of connection takes 1.5 seconds.
   423  	cp := &connProducer{shouldFail: true, connTime: 1500 * time.Millisecond}
   424  	clFactory := func(*grpc.ClientConn) orderer.AtomicBroadcastClient {
   425  		return &abclient{}
   426  	}
   427  	setupInvoked := 0
   428  	setup := func(blocksprovider.BlocksDeliverer) error {
   429  		setupInvoked++
   430  		return nil
   431  	}
   432  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   433  		return 0, elapsedTime.Nanoseconds() < time.Second.Nanoseconds()
   434  	}
   435  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   436  	defer bc.Close()
   437  	err := bdc(bc)
   438  	assert.Error(t, err)
   439  	assert.Equal(t, 1, cp.connAttempts)
   440  	assert.Equal(t, 0, setupInvoked)
   441  }
   442  
   443  func TestGreenPath(t *testing.T) {
   444  	testGreenPath(t, blockDelivererConsumerWithRecv)
   445  	testGreenPath(t, blockDelivererConsumerWithSend)
   446  	assert.Equal(t, 0, connNumber)
   447  }
   448  
   449  func testGreenPath(t *testing.T, bdc blocksDelivererConsumer) {
   450  	// Scenario: Everything succeeds
   451  	cp := &connProducer{}
   452  	abcClient := &abclient{}
   453  	clFactory := func(*grpc.ClientConn) orderer.AtomicBroadcastClient {
   454  		return abcClient
   455  	}
   456  
   457  	setupInvoked := 0
   458  	setup := func(blocksprovider.BlocksDeliverer) error {
   459  		setupInvoked++
   460  		return nil
   461  	}
   462  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   463  		return time.Duration(0), attemptNum < 1
   464  	}
   465  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   466  	defer bc.Close()
   467  	err := bdc(bc)
   468  	assert.NoError(t, err)
   469  	assert.Equal(t, 1, cp.connAttempts)
   470  	assert.Equal(t, 1, setupInvoked)
   471  }
   472  
   473  func TestCloseWhileRecv(t *testing.T) {
   474  	// Scenario: Recv is being called and after a while,
   475  	// the connection is closed.
   476  	// The Recv should return immediately in such a case
   477  	fakeOrderer := mocks.NewOrderer(5611, t)
   478  	time.Sleep(time.Second)
   479  	defer fakeOrderer.Shutdown()
   480  	cp := &connProducer{ordererEndpoint: "localhost:5611"}
   481  	clFactory := func(conn *grpc.ClientConn) orderer.AtomicBroadcastClient {
   482  		return orderer.NewAtomicBroadcastClient(conn)
   483  	}
   484  
   485  	setup := func(blocksprovider.BlocksDeliverer) error {
   486  		return nil
   487  	}
   488  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   489  		return 0, true
   490  	}
   491  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   492  	var flag int32
   493  	time.AfterFunc(time.Second, func() {
   494  		atomic.StoreInt32(&flag, int32(1))
   495  		bc.Close()
   496  		bc.Close() // Try to close a second time
   497  	})
   498  	resp, err := bc.Recv()
   499  	// Ensure we returned because bc.Close() was called and not because some other reason
   500  	assert.Equal(t, int32(1), atomic.LoadInt32(&flag), "Recv returned before bc.Close() was called")
   501  	assert.Nil(t, resp)
   502  	assert.Error(t, err)
   503  	assert.Contains(t, "Client is closing", err.Error())
   504  }
   505  
   506  func TestCloseWhileSleep(t *testing.T) {
   507  	testCloseWhileSleep(t, blockDelivererConsumerWithRecv)
   508  	testCloseWhileSleep(t, blockDelivererConsumerWithSend)
   509  	assert.Equal(t, 0, connNumber)
   510  }
   511  
   512  func testCloseWhileSleep(t *testing.T, bdc blocksDelivererConsumer) {
   513  	// Scenario: Recv/Send is being called while sleeping because
   514  	// of the backoff policy is programmed to sleep 1000000 seconds
   515  	// between retries.
   516  	// The Recv/Send should return pretty soon
   517  	cp := &connProducer{}
   518  	abcClient := &abclient{shouldFail: true}
   519  	clFactory := func(*grpc.ClientConn) orderer.AtomicBroadcastClient {
   520  		return abcClient
   521  	}
   522  
   523  	setupInvoked := 0
   524  	setup := func(blocksprovider.BlocksDeliverer) error {
   525  		setupInvoked++
   526  		return nil
   527  	}
   528  	var wg sync.WaitGroup
   529  	wg.Add(1)
   530  	backoffStrategy := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   531  		if attemptNum == 1 {
   532  			go func() {
   533  				time.Sleep(time.Second)
   534  				wg.Done()
   535  			}()
   536  		}
   537  		return time.Second * 1000000, true
   538  	}
   539  	bc := NewBroadcastClient(cp, clFactory, setup, backoffStrategy)
   540  	go func() {
   541  		wg.Wait()
   542  		bc.Close()
   543  		bc.Close() // Try to close a second time
   544  	}()
   545  	err := bdc(bc)
   546  	assert.Error(t, err)
   547  	assert.Equal(t, 1, cp.connAttempts)
   548  	assert.Equal(t, 0, setupInvoked)
   549  }
   550  
   551  type signerMock struct {
   552  }
   553  
   554  func (s *signerMock) NewSignatureHeader() (*common.SignatureHeader, error) {
   555  	return &common.SignatureHeader{}, nil
   556  }
   557  
   558  func (s *signerMock) Sign(message []byte) ([]byte, error) {
   559  	hasher := sha256.New()
   560  	hasher.Write(message)
   561  	return hasher.Sum(nil), nil
   562  }
   563  
   564  func TestProductionUsage(t *testing.T) {
   565  	defer ensureNoGoroutineLeak(t)()
   566  	// This test configures the client in a similar fashion as will be
   567  	// in production, and tests against a live gRPC server.
   568  	os := mocks.NewOrderer(5612, t)
   569  	os.SetNextExpectedSeek(5)
   570  
   571  	connFact := func(endpoint string) (*grpc.ClientConn, error) {
   572  		return grpc.Dial(endpoint, grpc.WithInsecure(), grpc.WithBlock())
   573  	}
   574  	prod := comm.NewConnectionProducer(connFact, []string{"localhost:5612"})
   575  	clFact := func(cc *grpc.ClientConn) orderer.AtomicBroadcastClient {
   576  		return orderer.NewAtomicBroadcastClient(cc)
   577  	}
   578  	onConnect := func(bd blocksprovider.BlocksDeliverer) error {
   579  		env, err := utils.CreateSignedEnvelope(common.HeaderType_CONFIG_UPDATE,
   580  			"TEST",
   581  			&signerMock{}, newTestSeekInfo(), 0, 0)
   582  		assert.NoError(t, err)
   583  		return bd.Send(env)
   584  	}
   585  	retryPol := func(attemptNum int, elapsedTime time.Duration) (time.Duration, bool) {
   586  		return time.Second * 3, attemptNum < 2
   587  	}
   588  	cl := NewBroadcastClient(prod, clFact, onConnect, retryPol)
   589  	go os.SendBlock(5)
   590  	resp, err := cl.Recv()
   591  	assert.NoError(t, err)
   592  	assert.NotNil(t, resp)
   593  	assert.Equal(t, uint64(5), resp.GetBlock().Header.Number)
   594  	os.Shutdown()
   595  	cl.Close()
   596  }
   597  
   598  func newTestSeekInfo() *orderer.SeekInfo {
   599  	return &orderer.SeekInfo{Start: &orderer.SeekPosition{Type: &orderer.SeekPosition_Specified{Specified: &orderer.SeekSpecified{Number: 5}}},
   600  		Stop:     &orderer.SeekPosition{Type: &orderer.SeekPosition_Specified{Specified: &orderer.SeekSpecified{Number: math.MaxUint64}}},
   601  		Behavior: orderer.SeekInfo_BLOCK_UNTIL_READY,
   602  	}
   603  }