github.com/true-sqn/fabric@v2.1.1+incompatible/orderer/consensus/etcdraft/validator_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  	"io/ioutil"
    11  	"time"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	etcdraftproto "github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
    15  	raftprotos "github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
    16  	"github.com/hyperledger/fabric/bccsp"
    17  	"github.com/hyperledger/fabric/bccsp/sw"
    18  	"github.com/hyperledger/fabric/common/crypto/tlsgen"
    19  	"github.com/hyperledger/fabric/orderer/consensus/etcdraft"
    20  	consensusmocks "github.com/hyperledger/fabric/orderer/consensus/mocks"
    21  	. "github.com/onsi/ginkgo"
    22  	. "github.com/onsi/gomega"
    23  )
    24  
    25  var _ = Describe("Metadata Validation", func() {
    26  	var (
    27  		chain *etcdraft.Chain
    28  		tlsCA tlsgen.CA
    29  	)
    30  
    31  	BeforeEach(func() {
    32  		var (
    33  			channelID         string
    34  			consenterMetadata *raftprotos.ConfigMetadata
    35  			consenters        map[uint64]*raftprotos.Consenter
    36  			support           *consensusmocks.FakeConsenterSupport
    37  			dataDir           string
    38  			err               error
    39  			cryptoProvider    bccsp.BCCSP
    40  		)
    41  
    42  		channelID = "test-channel"
    43  
    44  		cryptoProvider, err = sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
    45  		Expect(err).NotTo(HaveOccurred())
    46  
    47  		dataDir, err = ioutil.TempDir("", "wal-")
    48  		Expect(err).NotTo(HaveOccurred())
    49  
    50  		tlsCA, err = tlsgen.NewCA()
    51  		Expect(err).NotTo(HaveOccurred())
    52  
    53  		support = &consensusmocks.FakeConsenterSupport{}
    54  		support.ChannelIDReturns(channelID)
    55  		consenterMetadata = createMetadata(1, tlsCA)
    56  		mockOrdererConfig := mockOrdererWithTLSRootCert(time.Hour, marshalOrPanic(consenterMetadata), tlsCA)
    57  		support.SharedConfigReturns(mockOrdererConfig)
    58  
    59  		meta := &raftprotos.BlockMetadata{
    60  			ConsenterIds:    make([]uint64, len(consenterMetadata.Consenters)),
    61  			NextConsenterId: 1,
    62  		}
    63  
    64  		for i := range meta.ConsenterIds {
    65  			meta.ConsenterIds[i] = meta.NextConsenterId
    66  			meta.NextConsenterId++
    67  		}
    68  
    69  		consenters = map[uint64]*raftprotos.Consenter{}
    70  		for i, c := range consenterMetadata.Consenters {
    71  			consenters[meta.ConsenterIds[i]] = c
    72  		}
    73  
    74  		c := newChain(10*time.Second, channelID, dataDir, 1, meta, consenters, cryptoProvider, support)
    75  		c.init()
    76  		chain = c.Chain
    77  	})
    78  
    79  	When("determining parameter well-formedness", func() {
    80  		It("succeeds when new consensus metadata is nil", func() {
    81  			Expect(chain.ValidateConsensusMetadata(nil, nil, false)).To(Succeed())
    82  		})
    83  
    84  		It("fails when new consensus metadata is not nil while old consensus metadata is nil", func() {
    85  			Expect(func() {
    86  				chain.ValidateConsensusMetadata(nil, []byte("test"), false)
    87  			}).To(Panic())
    88  		})
    89  
    90  		It("fails when old consensus metadata is not well-formed", func() {
    91  			Expect(func() {
    92  				chain.ValidateConsensusMetadata([]byte("test"), []byte("test"), false)
    93  			}).To(Panic())
    94  		})
    95  
    96  		It("fails when new consensus metadata is not well-formed", func() {
    97  			oldBytes, _ := proto.Marshal(&etcdraftproto.ConfigMetadata{})
    98  			Expect(chain.ValidateConsensusMetadata(oldBytes, []byte("test"), false)).NotTo(Succeed())
    99  		})
   100  	})
   101  
   102  	Context("valid old consensus metadata", func() {
   103  		var (
   104  			oldBytes    []byte
   105  			oldMetadata *etcdraftproto.ConfigMetadata
   106  			newMetadata *etcdraftproto.ConfigMetadata
   107  			newChannel  bool
   108  		)
   109  
   110  		BeforeEach(func() {
   111  			oldMetadata = &etcdraftproto.ConfigMetadata{
   112  				Options: &etcdraftproto.Options{
   113  					TickInterval:         "500ms",
   114  					ElectionTick:         10,
   115  					HeartbeatTick:        1,
   116  					MaxInflightBlocks:    5,
   117  					SnapshotIntervalSize: 20 * 1024 * 1024, // 20 MB
   118  				},
   119  				Consenters: []*etcdraftproto.Consenter{
   120  					{
   121  						Host:          "host1",
   122  						Port:          10001,
   123  						ClientTlsCert: clientTLSCert(tlsCA),
   124  						ServerTlsCert: serverTLSCert(tlsCA),
   125  					},
   126  					{
   127  						Host:          "host2",
   128  						Port:          10002,
   129  						ClientTlsCert: clientTLSCert(tlsCA),
   130  						ServerTlsCert: serverTLSCert(tlsCA),
   131  					},
   132  					{
   133  						Host:          "host3",
   134  						Port:          10003,
   135  						ClientTlsCert: clientTLSCert(tlsCA),
   136  						ServerTlsCert: serverTLSCert(tlsCA),
   137  					},
   138  				},
   139  			}
   140  			newMetadata = oldMetadata
   141  			oldBytes, _ = proto.Marshal(oldMetadata)
   142  			newChannel = false
   143  		})
   144  
   145  		It("fails when new consensus metadata has invalid options", func() {
   146  			// NOTE: we are not checking all failures here since tests for CheckConfigMetadata does that
   147  			newMetadata.Options.TickInterval = ""
   148  			newBytes, _ := proto.Marshal(newMetadata)
   149  			Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).NotTo(Succeed())
   150  		})
   151  
   152  		Context("new channel creation", func() {
   153  			BeforeEach(func() {
   154  				newChannel = true
   155  			})
   156  
   157  			It("fails when the new consenters are an empty set", func() {
   158  				newMetadata.Consenters = []*etcdraftproto.Consenter{}
   159  				newBytes, _ := proto.Marshal(newMetadata)
   160  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).NotTo(Succeed())
   161  			})
   162  
   163  			It("succeeds when the new consenters are the same as the existing consenters", func() {
   164  				newBytes, _ := proto.Marshal(newMetadata)
   165  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).To(Succeed())
   166  			})
   167  
   168  			It("succeeds when the new consenters are a subset of the existing consenters", func() {
   169  				newMetadata.Consenters = newMetadata.Consenters[:2]
   170  				newBytes, _ := proto.Marshal(newMetadata)
   171  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).To(Succeed())
   172  			})
   173  
   174  			It("fails when the new consenters are not a subset of the existing consenters", func() {
   175  				newMetadata.Consenters[2].ClientTlsCert = clientTLSCert(tlsCA)
   176  				newBytes, _ := proto.Marshal(newMetadata)
   177  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).NotTo(Succeed())
   178  			})
   179  		})
   180  
   181  		Context("config update on a channel", func() {
   182  			BeforeEach(func() {
   183  				newChannel = false
   184  				chain.ActiveNodes.Store([]uint64{1, 2, 3})
   185  			})
   186  
   187  			It("fails when the new consenters are an empty set", func() {
   188  				// NOTE: This also takes care of the case when we remove node from a singleton consenter set
   189  				newMetadata.Consenters = []*etcdraftproto.Consenter{}
   190  				newBytes, _ := proto.Marshal(newMetadata)
   191  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).NotTo(Succeed())
   192  			})
   193  
   194  			It("succeeds when the new consenters are the same as the existing consenters", func() {
   195  				newBytes, _ := proto.Marshal(newMetadata)
   196  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).To(Succeed())
   197  			})
   198  
   199  			It("succeeds on addition of a single consenter", func() {
   200  				newMetadata.Consenters = append(newMetadata.Consenters, &etcdraftproto.Consenter{
   201  					Host:          "host4",
   202  					Port:          10004,
   203  					ClientTlsCert: clientTLSCert(tlsCA),
   204  					ServerTlsCert: serverTLSCert(tlsCA),
   205  				})
   206  				newBytes, _ := proto.Marshal(newMetadata)
   207  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).To(Succeed())
   208  			})
   209  
   210  			It("fails on addition of more than one consenter", func() {
   211  				newMetadata.Consenters = append(newMetadata.Consenters,
   212  					&etcdraftproto.Consenter{
   213  						Host:          "host4",
   214  						Port:          10004,
   215  						ClientTlsCert: clientTLSCert(tlsCA),
   216  						ServerTlsCert: serverTLSCert(tlsCA),
   217  					},
   218  					&etcdraftproto.Consenter{
   219  						Host:          "host5",
   220  						Port:          10005,
   221  						ClientTlsCert: clientTLSCert(tlsCA),
   222  						ServerTlsCert: serverTLSCert(tlsCA),
   223  					},
   224  				)
   225  				newBytes, _ := proto.Marshal(newMetadata)
   226  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).NotTo(Succeed())
   227  			})
   228  
   229  			It("succeeds on removal of a single consenter", func() {
   230  				newMetadata.Consenters = newMetadata.Consenters[:2]
   231  				newBytes, _ := proto.Marshal(newMetadata)
   232  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).To(Succeed())
   233  			})
   234  
   235  			It("fails on removal of more than one consenter", func() {
   236  				newMetadata.Consenters = newMetadata.Consenters[:1]
   237  				newBytes, _ := proto.Marshal(newMetadata)
   238  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).NotTo(Succeed())
   239  			})
   240  
   241  			It("succeeds on rotating certs in case of both addition and removal of a node each to reuse the raft NodeId", func() {
   242  				newMetadata.Consenters = append(newMetadata.Consenters[:2], &etcdraftproto.Consenter{
   243  					Host:          "host4",
   244  					Port:          10004,
   245  					ClientTlsCert: clientTLSCert(tlsCA),
   246  					ServerTlsCert: serverTLSCert(tlsCA),
   247  				})
   248  				newBytes, _ := proto.Marshal(newMetadata)
   249  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).To(Succeed())
   250  			})
   251  
   252  			It("succeeds on removal of inactive node in 2/3 cluster", func() {
   253  				chain.ActiveNodes.Store([]uint64{1, 2})
   254  				newMetadata.Consenters = newMetadata.Consenters[:2]
   255  				newBytes, _ := proto.Marshal(newMetadata)
   256  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).To(Succeed())
   257  			})
   258  
   259  			It("fails on removal of active node in 2/3 cluster", func() {
   260  				chain.ActiveNodes.Store([]uint64{1, 2})
   261  				newMetadata.Consenters = newMetadata.Consenters[1:]
   262  				newBytes, _ := proto.Marshal(newMetadata)
   263  				Expect(chain.ValidateConsensusMetadata(oldBytes, newBytes, newChannel)).To(
   264  					MatchError("2 out of 3 nodes are alive, configuration will result in quorum loss"))
   265  			})
   266  		})
   267  	})
   268  })