github.com/defanghe/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 })