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