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