github.com/true-sqn/fabric@v2.1.1+incompatible/orderer/consensus/etcdraft/consenter_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package etcdraft_test
     8  
     9  import (
    10  	"encoding/pem"
    11  	"io/ioutil"
    12  	"os"
    13  	"path"
    14  	"strings"
    15  
    16  	"github.com/golang/protobuf/proto"
    17  	"github.com/hyperledger/fabric-protos-go/common"
    18  	"github.com/hyperledger/fabric-protos-go/orderer"
    19  	etcdraftproto "github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
    20  	"github.com/hyperledger/fabric/bccsp/sw"
    21  	"github.com/hyperledger/fabric/common/channelconfig"
    22  	"github.com/hyperledger/fabric/common/crypto/tlsgen"
    23  	"github.com/hyperledger/fabric/common/flogging"
    24  	"github.com/hyperledger/fabric/internal/pkg/comm"
    25  	"github.com/hyperledger/fabric/orderer/common/cluster"
    26  	clustermocks "github.com/hyperledger/fabric/orderer/common/cluster/mocks"
    27  	"github.com/hyperledger/fabric/orderer/common/multichannel"
    28  	"github.com/hyperledger/fabric/orderer/consensus/etcdraft"
    29  	"github.com/hyperledger/fabric/orderer/consensus/etcdraft/mocks"
    30  	consensusmocks "github.com/hyperledger/fabric/orderer/consensus/mocks"
    31  	"github.com/hyperledger/fabric/protoutil"
    32  	. "github.com/onsi/ginkgo"
    33  	. "github.com/onsi/gomega"
    34  	"github.com/stretchr/testify/mock"
    35  	"go.uber.org/zap"
    36  	"go.uber.org/zap/zapcore"
    37  )
    38  
    39  //go:generate counterfeiter -o mocks/orderer_capabilities.go --fake-name OrdererCapabilities . ordererCapabilities
    40  
    41  type ordererCapabilities interface {
    42  	channelconfig.OrdererCapabilities
    43  }
    44  
    45  //go:generate counterfeiter -o mocks/orderer_config.go --fake-name OrdererConfig . ordererConfig
    46  
    47  type ordererConfig interface {
    48  	channelconfig.Orderer
    49  }
    50  
    51  var _ = Describe("Consenter", func() {
    52  	var (
    53  		certAsPEM   []byte
    54  		chainGetter *mocks.ChainGetter
    55  		support     *consensusmocks.FakeConsenterSupport
    56  		dataDir     string
    57  		snapDir     string
    58  		walDir      string
    59  		err         error
    60  	)
    61  
    62  	BeforeEach(func() {
    63  		certAsPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: []byte("cert bytes")})
    64  		chainGetter = &mocks.ChainGetter{}
    65  		support = &consensusmocks.FakeConsenterSupport{}
    66  		dataDir, err = ioutil.TempDir("", "snap-")
    67  		Expect(err).NotTo(HaveOccurred())
    68  		walDir = path.Join(dataDir, "wal-")
    69  		snapDir = path.Join(dataDir, "snap-")
    70  
    71  		blockBytes, err := ioutil.ReadFile("testdata/mychannel.block")
    72  		Expect(err).NotTo(HaveOccurred())
    73  
    74  		goodConfigBlock := &common.Block{}
    75  		proto.Unmarshal(blockBytes, goodConfigBlock)
    76  
    77  		lastBlock := &common.Block{
    78  			Header: &common.BlockHeader{
    79  				Number: 1,
    80  			},
    81  			Data: goodConfigBlock.Data,
    82  			Metadata: &common.BlockMetadata{
    83  				Metadata: [][]byte{{}, protoutil.MarshalOrPanic(&common.Metadata{
    84  					Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: 0}),
    85  				})},
    86  			},
    87  		}
    88  
    89  		support.BlockReturns(lastBlock)
    90  	})
    91  
    92  	AfterEach(func() {
    93  		os.RemoveAll(dataDir)
    94  	})
    95  
    96  	When("the consenter is extracting the channel", func() {
    97  		It("extracts successfully from step requests", func() {
    98  			consenter := newConsenter(chainGetter)
    99  			ch := consenter.TargetChannel(&orderer.ConsensusRequest{Channel: "mychannel"})
   100  			Expect(ch).To(BeIdenticalTo("mychannel"))
   101  		})
   102  		It("extracts successfully from submit requests", func() {
   103  			consenter := newConsenter(chainGetter)
   104  			ch := consenter.TargetChannel(&orderer.SubmitRequest{Channel: "mychannel"})
   105  			Expect(ch).To(BeIdenticalTo("mychannel"))
   106  		})
   107  		It("returns an empty string for the rest of the messages", func() {
   108  			consenter := newConsenter(chainGetter)
   109  			ch := consenter.TargetChannel(&common.Block{})
   110  			Expect(ch).To(BeEmpty())
   111  		})
   112  	})
   113  
   114  	When("the consenter is asked for a chain", func() {
   115  		cryptoProvider, _ := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   116  		chainInstance := &etcdraft.Chain{CryptoProvider: cryptoProvider}
   117  		cs := &multichannel.ChainSupport{
   118  			Chain: chainInstance,
   119  			BCCSP: cryptoProvider,
   120  		}
   121  		BeforeEach(func() {
   122  			chainGetter.On("GetChain", "mychannel").Return(cs)
   123  			chainGetter.On("GetChain", "badChainObject").Return(&multichannel.ChainSupport{})
   124  			chainGetter.On("GetChain", "notmychannel").Return(nil)
   125  			chainGetter.On("GetChain", "notraftchain").Return(&multichannel.ChainSupport{
   126  				Chain: &multichannel.ChainSupport{},
   127  			})
   128  		})
   129  		It("calls the chain getter and returns the reference when it is found", func() {
   130  			consenter := newConsenter(chainGetter)
   131  			Expect(consenter).NotTo(BeNil())
   132  
   133  			chain := consenter.ReceiverByChain("mychannel")
   134  			Expect(chain).NotTo(BeNil())
   135  			Expect(chain).To(BeIdenticalTo(chainInstance))
   136  		})
   137  		It("calls the chain getter and returns nil when it's not found", func() {
   138  			consenter := newConsenter(chainGetter)
   139  			Expect(consenter).NotTo(BeNil())
   140  
   141  			chain := consenter.ReceiverByChain("notmychannel")
   142  			Expect(chain).To(BeNil())
   143  		})
   144  		It("calls the chain getter and returns nil when it's not a raft chain", func() {
   145  			consenter := newConsenter(chainGetter)
   146  			Expect(consenter).NotTo(BeNil())
   147  
   148  			chain := consenter.ReceiverByChain("notraftchain")
   149  			Expect(chain).To(BeNil())
   150  		})
   151  		It("calls the chain getter and panics when the chain has a bad internal state", func() {
   152  			consenter := newConsenter(chainGetter)
   153  			Expect(consenter).NotTo(BeNil())
   154  
   155  			Expect(func() {
   156  				consenter.ReceiverByChain("badChainObject")
   157  			}).To(Panic())
   158  		})
   159  	})
   160  
   161  	It("successfully constructs a Chain", func() {
   162  		// We append a line feed to our cert, just to ensure that we can still consume it and ignore.
   163  		certAsPEMWithLineFeed := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: []byte("cert bytes")})
   164  		certAsPEMWithLineFeed = append(certAsPEMWithLineFeed, []byte("\n")...)
   165  		m := &etcdraftproto.ConfigMetadata{
   166  			Consenters: []*etcdraftproto.Consenter{
   167  				{ServerTlsCert: certAsPEMWithLineFeed},
   168  			},
   169  			Options: &etcdraftproto.Options{
   170  				TickInterval:      "500ms",
   171  				ElectionTick:      10,
   172  				HeartbeatTick:     1,
   173  				MaxInflightBlocks: 5,
   174  			},
   175  		}
   176  		metadata := protoutil.MarshalOrPanic(m)
   177  		mockOrderer := &mocks.OrdererConfig{}
   178  		mockOrderer.ConsensusMetadataReturns(metadata)
   179  		mockOrderer.BatchSizeReturns(
   180  			&orderer.BatchSize{
   181  				PreferredMaxBytes: 2 * 1024 * 1024,
   182  			},
   183  		)
   184  		support.SharedConfigReturns(mockOrderer)
   185  
   186  		consenter := newConsenter(chainGetter)
   187  		consenter.EtcdRaftConfig.WALDir = walDir
   188  		consenter.EtcdRaftConfig.SnapDir = snapDir
   189  		// consenter.EtcdRaftConfig.EvictionSuspicion is missing
   190  		var defaultSuspicionFallback bool
   191  		consenter.Metrics = newFakeMetrics(newFakeMetricsFields())
   192  		consenter.Logger = consenter.Logger.WithOptions(zap.Hooks(func(entry zapcore.Entry) error {
   193  			if strings.Contains(entry.Message, "EvictionSuspicion not set, defaulting to 10m0s") {
   194  				defaultSuspicionFallback = true
   195  			}
   196  			return nil
   197  		}))
   198  
   199  		chain, err := consenter.HandleChain(support, nil)
   200  		Expect(err).NotTo(HaveOccurred())
   201  		Expect(chain).NotTo(BeNil())
   202  
   203  		Expect(chain.Start).NotTo(Panic())
   204  		Expect(defaultSuspicionFallback).To(BeTrue())
   205  	})
   206  
   207  	It("fails to handle chain if no matching cert found", func() {
   208  		m := &etcdraftproto.ConfigMetadata{
   209  			Consenters: []*etcdraftproto.Consenter{
   210  				{ServerTlsCert: pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: []byte("foo")})},
   211  			},
   212  			Options: &etcdraftproto.Options{
   213  				TickInterval:      "500ms",
   214  				ElectionTick:      10,
   215  				HeartbeatTick:     1,
   216  				MaxInflightBlocks: 5,
   217  			},
   218  		}
   219  		metadata := protoutil.MarshalOrPanic(m)
   220  		support := &consensusmocks.FakeConsenterSupport{}
   221  		mockOrderer := &mocks.OrdererConfig{}
   222  		mockOrderer.ConsensusMetadataReturns(metadata)
   223  		mockOrderer.BatchSizeReturns(
   224  			&orderer.BatchSize{
   225  				PreferredMaxBytes: 2 * 1024 * 1024,
   226  			},
   227  		)
   228  		support.SharedConfigReturns(mockOrderer)
   229  		support.ChannelIDReturns("foo")
   230  
   231  		consenter := newConsenter(chainGetter)
   232  
   233  		chain, err := consenter.HandleChain(support, &common.Metadata{})
   234  		Expect(chain).To(Not(BeNil()))
   235  		Expect(err).To(Not(HaveOccurred()))
   236  		Expect(chain.Order(nil, 0).Error()).To(Equal("channel foo is not serviced by me"))
   237  		consenter.icr.AssertNumberOfCalls(testingInstance, "TrackChain", 1)
   238  	})
   239  
   240  	It("fails to handle chain if etcdraft options have not been provided", func() {
   241  		m := &etcdraftproto.ConfigMetadata{
   242  			Consenters: []*etcdraftproto.Consenter{
   243  				{ServerTlsCert: []byte("cert.orderer1.org1")},
   244  			},
   245  		}
   246  		metadata := protoutil.MarshalOrPanic(m)
   247  		mockOrderer := &mocks.OrdererConfig{}
   248  		mockOrderer.ConsensusMetadataReturns(metadata)
   249  		mockOrderer.BatchSizeReturns(
   250  			&orderer.BatchSize{
   251  				PreferredMaxBytes: 2 * 1024 * 1024,
   252  			},
   253  		)
   254  		support.SharedConfigReturns(mockOrderer)
   255  
   256  		consenter := newConsenter(chainGetter)
   257  
   258  		chain, err := consenter.HandleChain(support, nil)
   259  		Expect(chain).To(BeNil())
   260  		Expect(err).To(MatchError("etcdraft options have not been provided"))
   261  	})
   262  
   263  	It("fails to handle chain if tick interval is invalid", func() {
   264  		m := &etcdraftproto.ConfigMetadata{
   265  			Consenters: []*etcdraftproto.Consenter{
   266  				{ServerTlsCert: certAsPEM},
   267  			},
   268  			Options: &etcdraftproto.Options{
   269  				TickInterval:      "500",
   270  				ElectionTick:      10,
   271  				HeartbeatTick:     1,
   272  				MaxInflightBlocks: 5,
   273  			},
   274  		}
   275  		metadata := protoutil.MarshalOrPanic(m)
   276  		mockOrderer := &mocks.OrdererConfig{}
   277  		mockOrderer.ConsensusMetadataReturns(metadata)
   278  		mockOrderer.BatchSizeReturns(
   279  			&orderer.BatchSize{
   280  				PreferredMaxBytes: 2 * 1024 * 1024,
   281  			},
   282  		)
   283  		mockOrderer.CapabilitiesReturns(&mocks.OrdererCapabilities{})
   284  		support.SharedConfigReturns(mockOrderer)
   285  
   286  		consenter := newConsenter(chainGetter)
   287  
   288  		chain, err := consenter.HandleChain(support, nil)
   289  		Expect(chain).To(BeNil())
   290  		Expect(err).To(MatchError("failed to parse TickInterval (500) to time duration"))
   291  	})
   292  })
   293  
   294  type consenter struct {
   295  	*etcdraft.Consenter
   296  	icr *mocks.InactiveChainRegistry
   297  }
   298  
   299  func newConsenter(chainGetter *mocks.ChainGetter) *consenter {
   300  	communicator := &clustermocks.Communicator{}
   301  	ca, err := tlsgen.NewCA()
   302  	Expect(err).NotTo(HaveOccurred())
   303  	communicator.On("Configure", mock.Anything, mock.Anything)
   304  	icr := &mocks.InactiveChainRegistry{}
   305  	icr.On("TrackChain", "foo", mock.Anything, mock.Anything)
   306  	certAsPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: []byte("cert bytes")})
   307  
   308  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   309  	Expect(err).NotTo(HaveOccurred())
   310  
   311  	c := &etcdraft.Consenter{
   312  		InactiveChainRegistry: icr,
   313  		Communication:         communicator,
   314  		Cert:                  certAsPEM,
   315  		Logger:                flogging.MustGetLogger("test"),
   316  		Chains:                chainGetter,
   317  		Dispatcher: &etcdraft.Dispatcher{
   318  			Logger:        flogging.MustGetLogger("test"),
   319  			ChainSelector: &mocks.ReceiverGetter{},
   320  		},
   321  		Dialer: &cluster.PredicateDialer{
   322  			Config: comm.ClientConfig{
   323  				SecOpts: comm.SecureOptions{
   324  					Certificate: ca.CertBytes(),
   325  				},
   326  			},
   327  		},
   328  		BCCSP: cryptoProvider,
   329  	}
   330  	return &consenter{
   331  		Consenter: c,
   332  		icr:       icr,
   333  	}
   334  }