github.com/defanghe/fabric@v2.1.1+incompatible/orderer/common/cluster/util_test.go (about) 1 /* 2 Copyright IBM Corp. 2017 All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package cluster_test 8 9 import ( 10 "crypto/x509" 11 "encoding/pem" 12 "errors" 13 "fmt" 14 "io/ioutil" 15 "strings" 16 "sync" 17 "testing" 18 "time" 19 20 "github.com/golang/protobuf/proto" 21 "github.com/hyperledger/fabric-protos-go/common" 22 "github.com/hyperledger/fabric-protos-go/msp" 23 "github.com/hyperledger/fabric/bccsp/sw" 24 "github.com/hyperledger/fabric/common/capabilities" 25 "github.com/hyperledger/fabric/common/channelconfig" 26 "github.com/hyperledger/fabric/common/configtx" 27 "github.com/hyperledger/fabric/common/configtx/test" 28 "github.com/hyperledger/fabric/common/crypto/tlsgen" 29 "github.com/hyperledger/fabric/common/flogging" 30 "github.com/hyperledger/fabric/common/policies" 31 "github.com/hyperledger/fabric/core/config/configtest" 32 "github.com/hyperledger/fabric/internal/configtxgen/encoder" 33 "github.com/hyperledger/fabric/internal/configtxgen/genesisconfig" 34 "github.com/hyperledger/fabric/internal/pkg/comm" 35 "github.com/hyperledger/fabric/orderer/common/cluster" 36 "github.com/hyperledger/fabric/orderer/common/cluster/mocks" 37 "github.com/hyperledger/fabric/protoutil" 38 "github.com/stretchr/testify/assert" 39 "github.com/stretchr/testify/mock" 40 "go.uber.org/zap" 41 "go.uber.org/zap/zapcore" 42 ) 43 44 //go:generate counterfeiter -o mocks/policy.go --fake-name Policy . policy 45 46 type policy interface { 47 policies.Policy 48 } 49 50 //go:generate counterfeiter -o mocks/policy_manager.go --fake-name PolicyManager . policyManager 51 52 type policyManager interface { 53 policies.Manager 54 } 55 56 func TestParallelStubActivation(t *testing.T) { 57 // Scenario: Activate the stub from different goroutines in parallel. 58 stub := &cluster.Stub{} 59 var wg sync.WaitGroup 60 n := 100 61 wg.Add(n) 62 instance := &cluster.RemoteContext{} 63 var activationCount int 64 maybeCreateInstance := func() (*cluster.RemoteContext, error) { 65 activationCount++ 66 return instance, nil 67 } 68 69 for i := 0; i < n; i++ { 70 go func() { 71 defer wg.Done() 72 stub.Activate(maybeCreateInstance) 73 }() 74 } 75 wg.Wait() 76 activatedStub := stub.RemoteContext 77 // Ensure the instance is the reference we stored 78 // and not any other reference, i.e - it wasn't 79 // copied by value. 80 assert.True(t, activatedStub == instance) 81 // Ensure the method was invoked only once. 82 assert.Equal(t, activationCount, 1) 83 } 84 85 func TestDialerCustomKeepAliveOptions(t *testing.T) { 86 ca, err := tlsgen.NewCA() 87 assert.NoError(t, err) 88 89 clientKeyPair, err := ca.NewClientCertKeyPair() 90 clientConfig := comm.ClientConfig{ 91 KaOpts: comm.KeepaliveOptions{ 92 ClientTimeout: time.Second * 12345, 93 }, 94 Timeout: time.Millisecond * 100, 95 SecOpts: comm.SecureOptions{ 96 RequireClientCert: true, 97 Key: clientKeyPair.Key, 98 Certificate: clientKeyPair.Cert, 99 ServerRootCAs: [][]byte{ca.CertBytes()}, 100 UseTLS: true, 101 ClientRootCAs: [][]byte{ca.CertBytes()}, 102 }, 103 } 104 105 dialer := &cluster.PredicateDialer{Config: clientConfig} 106 timeout := dialer.Config.KaOpts.ClientTimeout 107 assert.Equal(t, time.Second*12345, timeout) 108 } 109 110 func TestPredicateDialerUpdateRootCAs(t *testing.T) { 111 node1 := newTestNode(t) 112 defer node1.stop() 113 114 anotherTLSCA, err := tlsgen.NewCA() 115 assert.NoError(t, err) 116 117 dialer := &cluster.PredicateDialer{ 118 Config: node1.clientConfig.Clone(), 119 } 120 dialer.Config.SecOpts.ServerRootCAs = [][]byte{anotherTLSCA.CertBytes()} 121 dialer.Config.Timeout = time.Second 122 dialer.Config.AsyncConnect = false 123 124 _, err = dialer.Dial(node1.srv.Address(), nil) 125 assert.Error(t, err) 126 127 // Update root TLS CAs asynchronously to make sure we don't have a data race. 128 go func() { 129 dialer.UpdateRootCAs(node1.clientConfig.SecOpts.ServerRootCAs) 130 }() 131 132 // Eventually we should succeed connecting. 133 for i := 0; i < 10; i++ { 134 conn, err := dialer.Dial(node1.srv.Address(), nil) 135 if err == nil { 136 conn.Close() 137 return 138 } 139 } 140 141 assert.Fail(t, "could not connect after 10 attempts despite changing TLS CAs") 142 } 143 144 func TestDialerBadConfig(t *testing.T) { 145 emptyCertificate := []byte("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----") 146 dialer := &cluster.PredicateDialer{ 147 Config: comm.ClientConfig{ 148 SecOpts: comm.SecureOptions{ 149 UseTLS: true, 150 ServerRootCAs: [][]byte{emptyCertificate}, 151 }, 152 }, 153 } 154 _, err := dialer.Dial("127.0.0.1:8080", func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { 155 return nil 156 }) 157 assert.EqualError(t, err, "error adding root certificate: asn1: syntax error: sequence truncated") 158 } 159 160 func TestDERtoPEM(t *testing.T) { 161 ca, err := tlsgen.NewCA() 162 assert.NoError(t, err) 163 keyPair, err := ca.NewServerCertKeyPair("localhost") 164 assert.NoError(t, err) 165 assert.Equal(t, cluster.DERtoPEM(keyPair.TLSCert.Raw), string(keyPair.Cert)) 166 } 167 168 func TestStandardDialer(t *testing.T) { 169 emptyCertificate := []byte("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----") 170 certPool := [][]byte{emptyCertificate} 171 config := comm.ClientConfig{SecOpts: comm.SecureOptions{UseTLS: true, ServerRootCAs: certPool}} 172 standardDialer := &cluster.StandardDialer{ 173 Config: config, 174 } 175 _, err := standardDialer.Dial(cluster.EndpointCriteria{Endpoint: "127.0.0.1:8080", TLSRootCAs: certPool}) 176 assert.EqualError(t, 177 err, 178 "failed creating gRPC client: error adding root certificate: asn1: syntax error: sequence truncated", 179 ) 180 } 181 182 func TestVerifyBlockSignature(t *testing.T) { 183 verifier := &mocks.BlockVerifier{} 184 var nilConfigEnvelope *common.ConfigEnvelope 185 verifier.On("VerifyBlockSignature", mock.Anything, nilConfigEnvelope).Return(nil) 186 187 block := createBlockChain(3, 3)[0] 188 189 // The block should have a valid structure 190 err := cluster.VerifyBlockSignature(block, verifier, nil) 191 assert.NoError(t, err) 192 193 for _, testCase := range []struct { 194 name string 195 mutateBlock func(*common.Block) *common.Block 196 errorContains string 197 }{ 198 { 199 name: "nil metadata", 200 errorContains: "no metadata in block", 201 mutateBlock: func(block *common.Block) *common.Block { 202 block.Metadata = nil 203 return block 204 }, 205 }, 206 { 207 name: "zero metadata slice", 208 errorContains: "no metadata in block", 209 mutateBlock: func(block *common.Block) *common.Block { 210 block.Metadata.Metadata = nil 211 return block 212 }, 213 }, 214 { 215 name: "nil metadata", 216 errorContains: "failed unmarshaling medatata for signatures", 217 mutateBlock: func(block *common.Block) *common.Block { 218 block.Metadata.Metadata[0] = []byte{1, 2, 3} 219 return block 220 }, 221 }, 222 { 223 name: "bad signature header", 224 errorContains: "failed unmarshaling signature header", 225 mutateBlock: func(block *common.Block) *common.Block { 226 metadata := protoutil.GetMetadataFromBlockOrPanic(block, common.BlockMetadataIndex_SIGNATURES) 227 metadata.Signatures[0].SignatureHeader = []byte{1, 2, 3} 228 block.Metadata.Metadata[common.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(metadata) 229 return block 230 }, 231 }, 232 } { 233 testCase := testCase 234 t.Run(testCase.name, func(t *testing.T) { 235 // Create a copy of the block 236 blockCopy := &common.Block{} 237 err := proto.Unmarshal(protoutil.MarshalOrPanic(block), blockCopy) 238 assert.NoError(t, err) 239 // Mutate the block to sabotage it 240 blockCopy = testCase.mutateBlock(blockCopy) 241 err = cluster.VerifyBlockSignature(blockCopy, verifier, nil) 242 assert.Contains(t, err.Error(), testCase.errorContains) 243 }) 244 } 245 } 246 247 func TestVerifyBlockHash(t *testing.T) { 248 var start uint64 = 3 249 var end uint64 = 23 250 251 verify := func(blockchain []*common.Block) error { 252 for i := 0; i < len(blockchain); i++ { 253 err := cluster.VerifyBlockHash(i, blockchain) 254 if err != nil { 255 return err 256 } 257 } 258 return nil 259 } 260 261 // Verify that createBlockChain() creates a valid blockchain 262 assert.NoError(t, verify(createBlockChain(start, end))) 263 264 twoBlocks := createBlockChain(2, 3) 265 twoBlocks[0].Header = nil 266 assert.EqualError(t, cluster.VerifyBlockHash(1, twoBlocks), "previous block header is nil") 267 268 // Index out of bounds 269 blockchain := createBlockChain(start, end) 270 err := cluster.VerifyBlockHash(100, blockchain) 271 assert.EqualError(t, err, "index 100 out of bounds (total 21 blocks)") 272 273 for _, testCase := range []struct { 274 name string 275 mutateBlockSequence func([]*common.Block) []*common.Block 276 errorContains string 277 }{ 278 { 279 name: "non consecutive sequences", 280 errorContains: "sequences 12 and 666 were received consecutively", 281 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 282 blockSequence[len(blockSequence)/2].Header.Number = 666 283 assignHashes(blockSequence) 284 return blockSequence 285 }, 286 }, 287 { 288 name: "data hash mismatch", 289 errorContains: "computed hash of block (13) (dcb2ec1c5e482e4914cb953ff8eedd12774b244b12912afbe6001ba5de9ff800)" + 290 " doesn't match claimed hash (07)", 291 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 292 blockSequence[len(blockSequence)/2].Header.DataHash = []byte{7} 293 return blockSequence 294 }, 295 }, 296 { 297 name: "prev hash mismatch", 298 errorContains: "block [12]'s hash " + 299 "(866351705f1c2f13e10d52ead9d0ca3b80689ede8cc8bf70a6d60c67578323f4) " + 300 "mismatches block [13]'s prev block hash (07)", 301 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 302 blockSequence[len(blockSequence)/2].Header.PreviousHash = []byte{7} 303 return blockSequence 304 }, 305 }, 306 { 307 name: "nil block header", 308 errorContains: "missing block header", 309 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 310 blockSequence[0].Header = nil 311 return blockSequence 312 }, 313 }, 314 } { 315 testCase := testCase 316 t.Run(testCase.name, func(t *testing.T) { 317 blockchain := createBlockChain(start, end) 318 blockchain = testCase.mutateBlockSequence(blockchain) 319 err := verify(blockchain) 320 assert.EqualError(t, err, testCase.errorContains) 321 }) 322 } 323 } 324 325 func TestVerifyBlocks(t *testing.T) { 326 var sigSet1 []*protoutil.SignedData 327 var sigSet2 []*protoutil.SignedData 328 329 configEnvelope1 := &common.ConfigEnvelope{ 330 Config: &common.Config{ 331 Sequence: 1, 332 }, 333 } 334 configEnvelope2 := &common.ConfigEnvelope{ 335 Config: &common.Config{ 336 Sequence: 2, 337 }, 338 } 339 configTransaction := func(envelope *common.ConfigEnvelope) *common.Envelope { 340 return &common.Envelope{ 341 Payload: protoutil.MarshalOrPanic(&common.Payload{ 342 Data: protoutil.MarshalOrPanic(envelope), 343 Header: &common.Header{ 344 ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{ 345 Type: int32(common.HeaderType_CONFIG), 346 }), 347 }, 348 }), 349 } 350 } 351 352 for _, testCase := range []struct { 353 name string 354 configureVerifier func(*mocks.BlockVerifier) 355 mutateBlockSequence func([]*common.Block) []*common.Block 356 expectedError string 357 verifierExpectedCalls int 358 }{ 359 { 360 name: "empty sequence", 361 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 362 return nil 363 }, 364 expectedError: "buffer is empty", 365 }, 366 { 367 name: "prev hash mismatch", 368 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 369 blockSequence[len(blockSequence)/2].Header.PreviousHash = []byte{7} 370 return blockSequence 371 }, 372 expectedError: "block [74]'s hash " + 373 "(5cb4bd1b6a73f81afafd96387bb7ff4473c2425929d0862586f5fbfa12d762dd) " + 374 "mismatches block [75]'s prev block hash (07)", 375 }, 376 { 377 name: "bad signature", 378 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 379 return blockSequence 380 }, 381 configureVerifier: func(verifier *mocks.BlockVerifier) { 382 var nilEnvelope *common.ConfigEnvelope 383 verifier.On("VerifyBlockSignature", mock.Anything, nilEnvelope).Return(errors.New("bad signature")) 384 }, 385 expectedError: "bad signature", 386 verifierExpectedCalls: 1, 387 }, 388 { 389 name: "block that its type cannot be classified", 390 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 391 blockSequence[len(blockSequence)/2].Data = &common.BlockData{ 392 Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{})}, 393 } 394 blockSequence[len(blockSequence)/2].Header.DataHash = protoutil.BlockDataHash(blockSequence[len(blockSequence)/2].Data) 395 assignHashes(blockSequence) 396 return blockSequence 397 }, 398 expectedError: "nil header in payload", 399 }, 400 { 401 name: "config blocks in the sequence need to be verified and one of them is improperly signed", 402 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 403 var err error 404 // Put a config transaction in block n / 4 405 blockSequence[len(blockSequence)/4].Data = &common.BlockData{ 406 Data: [][]byte{protoutil.MarshalOrPanic(configTransaction(configEnvelope1))}, 407 } 408 blockSequence[len(blockSequence)/4].Header.DataHash = protoutil.BlockDataHash(blockSequence[len(blockSequence)/4].Data) 409 410 // Put a config transaction in block n / 2 411 blockSequence[len(blockSequence)/2].Data = &common.BlockData{ 412 Data: [][]byte{protoutil.MarshalOrPanic(configTransaction(configEnvelope2))}, 413 } 414 blockSequence[len(blockSequence)/2].Header.DataHash = protoutil.BlockDataHash(blockSequence[len(blockSequence)/2].Data) 415 416 assignHashes(blockSequence) 417 418 sigSet1, err = cluster.SignatureSetFromBlock(blockSequence[len(blockSequence)/4]) 419 assert.NoError(t, err) 420 sigSet2, err = cluster.SignatureSetFromBlock(blockSequence[len(blockSequence)/2]) 421 assert.NoError(t, err) 422 423 return blockSequence 424 }, 425 configureVerifier: func(verifier *mocks.BlockVerifier) { 426 var nilEnvelope *common.ConfigEnvelope 427 // The first config block, validates correctly. 428 verifier.On("VerifyBlockSignature", sigSet1, nilEnvelope).Return(nil).Once() 429 // However, the second config block - validates incorrectly. 430 confEnv1 := &common.ConfigEnvelope{} 431 proto.Unmarshal(protoutil.MarshalOrPanic(configEnvelope1), confEnv1) 432 verifier.On("VerifyBlockSignature", sigSet2, confEnv1).Return(errors.New("bad signature")).Once() 433 }, 434 expectedError: "bad signature", 435 verifierExpectedCalls: 2, 436 }, 437 { 438 name: "config block in the sequence needs to be verified, and it is properly signed", 439 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 440 var err error 441 // Put a config transaction in block n / 4 442 blockSequence[len(blockSequence)/4].Data = &common.BlockData{ 443 Data: [][]byte{protoutil.MarshalOrPanic(configTransaction(configEnvelope1))}, 444 } 445 blockSequence[len(blockSequence)/4].Header.DataHash = protoutil.BlockDataHash(blockSequence[len(blockSequence)/4].Data) 446 447 assignHashes(blockSequence) 448 449 sigSet1, err = cluster.SignatureSetFromBlock(blockSequence[len(blockSequence)/4]) 450 assert.NoError(t, err) 451 452 sigSet2, err = cluster.SignatureSetFromBlock(blockSequence[len(blockSequence)-1]) 453 assert.NoError(t, err) 454 455 return blockSequence 456 }, 457 configureVerifier: func(verifier *mocks.BlockVerifier) { 458 var nilEnvelope *common.ConfigEnvelope 459 confEnv1 := &common.ConfigEnvelope{} 460 proto.Unmarshal(protoutil.MarshalOrPanic(configEnvelope1), confEnv1) 461 verifier.On("VerifyBlockSignature", sigSet1, nilEnvelope).Return(nil).Once() 462 verifier.On("VerifyBlockSignature", sigSet2, confEnv1).Return(nil).Once() 463 }, 464 // We have a single config block in the 'middle' of the chain, so we have 2 verifications total: 465 // The last block, and the config block. 466 verifierExpectedCalls: 2, 467 }, 468 { 469 name: "last two blocks are config blocks, last block only verified once", 470 mutateBlockSequence: func(blockSequence []*common.Block) []*common.Block { 471 var err error 472 // Put a config transaction in block n-2 and in n-1 473 blockSequence[len(blockSequence)-2].Data = &common.BlockData{ 474 Data: [][]byte{protoutil.MarshalOrPanic(configTransaction(configEnvelope1))}, 475 } 476 blockSequence[len(blockSequence)-2].Header.DataHash = protoutil.BlockDataHash(blockSequence[len(blockSequence)-2].Data) 477 478 blockSequence[len(blockSequence)-1].Data = &common.BlockData{ 479 Data: [][]byte{protoutil.MarshalOrPanic(configTransaction(configEnvelope2))}, 480 } 481 blockSequence[len(blockSequence)-1].Header.DataHash = protoutil.BlockDataHash(blockSequence[len(blockSequence)-1].Data) 482 483 assignHashes(blockSequence) 484 485 sigSet1, err = cluster.SignatureSetFromBlock(blockSequence[len(blockSequence)-2]) 486 assert.NoError(t, err) 487 488 sigSet2, err = cluster.SignatureSetFromBlock(blockSequence[len(blockSequence)-1]) 489 assert.NoError(t, err) 490 491 return blockSequence 492 }, 493 configureVerifier: func(verifier *mocks.BlockVerifier) { 494 var nilEnvelope *common.ConfigEnvelope 495 confEnv1 := &common.ConfigEnvelope{} 496 proto.Unmarshal(protoutil.MarshalOrPanic(configEnvelope1), confEnv1) 497 verifier.On("VerifyBlockSignature", sigSet1, nilEnvelope).Return(nil).Once() 498 // We ensure that the signature set of the last block is verified using the config envelope of the block 499 // before it. 500 verifier.On("VerifyBlockSignature", sigSet2, confEnv1).Return(nil).Once() 501 // Note that we do not record a call to verify the last block, with the config envelope extracted from the block itself. 502 }, 503 // We have 2 config blocks, yet we only verify twice- the first config block, and the next config block, but no more, 504 // since the last block is a config block. 505 verifierExpectedCalls: 2, 506 }, 507 } { 508 testCase := testCase 509 t.Run(testCase.name, func(t *testing.T) { 510 blockchain := createBlockChain(50, 100) 511 blockchain = testCase.mutateBlockSequence(blockchain) 512 verifier := &mocks.BlockVerifier{} 513 if testCase.configureVerifier != nil { 514 testCase.configureVerifier(verifier) 515 } 516 err := cluster.VerifyBlocks(blockchain, verifier) 517 if testCase.expectedError != "" { 518 assert.EqualError(t, err, testCase.expectedError) 519 } else { 520 assert.NoError(t, err) 521 } 522 }) 523 } 524 } 525 526 func assignHashes(blockchain []*common.Block) { 527 for i := 1; i < len(blockchain); i++ { 528 blockchain[i].Header.PreviousHash = protoutil.BlockHeaderHash(blockchain[i-1].Header) 529 } 530 } 531 532 func createBlockChain(start, end uint64) []*common.Block { 533 newBlock := func(seq uint64) *common.Block { 534 sHdr := &common.SignatureHeader{ 535 Creator: []byte{1, 2, 3}, 536 Nonce: []byte{9, 5, 42, 66}, 537 } 538 block := protoutil.NewBlock(seq, nil) 539 blockSignature := &common.MetadataSignature{ 540 SignatureHeader: protoutil.MarshalOrPanic(sHdr), 541 } 542 block.Metadata.Metadata[common.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&common.Metadata{ 543 Value: nil, 544 Signatures: []*common.MetadataSignature{ 545 blockSignature, 546 }, 547 }) 548 549 txn := protoutil.MarshalOrPanic(&common.Envelope{ 550 Payload: protoutil.MarshalOrPanic(&common.Payload{ 551 Header: &common.Header{}, 552 }), 553 }) 554 block.Data.Data = append(block.Data.Data, txn) 555 return block 556 } 557 var blockchain []*common.Block 558 for seq := uint64(start); seq <= uint64(end); seq++ { 559 block := newBlock(seq) 560 block.Data.Data = append(block.Data.Data, make([]byte, 100)) 561 block.Header.DataHash = protoutil.BlockDataHash(block.Data) 562 blockchain = append(blockchain, block) 563 } 564 assignHashes(blockchain) 565 return blockchain 566 } 567 568 func TestEndpointconfigFromConfigBlockGreenPath(t *testing.T) { 569 t.Run("global endpoints", func(t *testing.T) { 570 block, err := test.MakeGenesisBlock("mychannel") 571 assert.NoError(t, err) 572 573 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 574 assert.NoError(t, err) 575 // For a block that doesn't have per org endpoints, 576 // we take the global endpoints 577 injectGlobalOrdererEndpoint(t, block, "globalEndpoint") 578 endpointConfig, err := cluster.EndpointconfigFromConfigBlock(block, cryptoProvider) 579 assert.NoError(t, err) 580 assert.Len(t, endpointConfig, 1) 581 assert.Equal(t, "globalEndpoint", endpointConfig[0].Endpoint) 582 583 bl, _ := pem.Decode(endpointConfig[0].TLSRootCAs[0]) 584 cert, err := x509.ParseCertificate(bl.Bytes) 585 assert.NoError(t, err) 586 587 assert.True(t, cert.IsCA) 588 }) 589 590 t.Run("per org endpoints", func(t *testing.T) { 591 block, err := test.MakeGenesisBlock("mychannel") 592 assert.NoError(t, err) 593 594 // Make a second config. 595 gConf := genesisconfig.Load(genesisconfig.SampleSingleMSPSoloProfile, configtest.GetDevConfigDir()) 596 gConf.Orderer.Capabilities = map[string]bool{ 597 capabilities.OrdererV2_0: true, 598 } 599 channelGroup, err := encoder.NewChannelGroup(gConf) 600 assert.NoError(t, err) 601 602 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 603 assert.NoError(t, err) 604 bundle, err := channelconfig.NewBundle("mychannel", &common.Config{ChannelGroup: channelGroup}, cryptoProvider) 605 assert.NoError(t, err) 606 607 msps, err := bundle.MSPManager().GetMSPs() 608 assert.NoError(t, err) 609 caBytes := msps["SampleOrg"].GetTLSRootCerts()[0] 610 611 assert.NoError(t, err) 612 injectAdditionalTLSCAEndpointPair(t, block, "anotherEndpoint", caBytes, "fooOrg") 613 endpointConfig, err := cluster.EndpointconfigFromConfigBlock(block, cryptoProvider) 614 assert.NoError(t, err) 615 // And ensure that the endpoints that are taken, are the per org ones. 616 assert.Len(t, endpointConfig, 2) 617 for _, endpoint := range endpointConfig { 618 // If this is the original organization (and not the clone), 619 // the TLS CA is 'caBytes' read from the second block. 620 if endpoint.Endpoint == "anotherEndpoint" { 621 assert.Len(t, endpoint.TLSRootCAs, 1) 622 assert.Equal(t, caBytes, endpoint.TLSRootCAs[0]) 623 continue 624 } 625 // Else, our endpoints are from the original org, and the TLS CA is something else. 626 assert.NotEqual(t, caBytes, endpoint.TLSRootCAs[0]) 627 // The endpoints we expect to see are something else. 628 assert.Equal(t, 0, strings.Index(endpoint.Endpoint, "127.0.0.1:")) 629 bl, _ := pem.Decode(endpoint.TLSRootCAs[0]) 630 cert, err := x509.ParseCertificate(bl.Bytes) 631 assert.NoError(t, err) 632 assert.True(t, cert.IsCA) 633 } 634 }) 635 } 636 637 func TestEndpointconfigFromConfigBlockFailures(t *testing.T) { 638 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 639 assert.NoError(t, err) 640 641 t.Run("nil block", func(t *testing.T) { 642 certs, err := cluster.EndpointconfigFromConfigBlock(nil, cryptoProvider) 643 assert.Nil(t, certs) 644 assert.EqualError(t, err, "nil block") 645 }) 646 647 t.Run("nil block data", func(t *testing.T) { 648 certs, err := cluster.EndpointconfigFromConfigBlock(&common.Block{}, cryptoProvider) 649 assert.Nil(t, certs) 650 assert.EqualError(t, err, "block data is nil") 651 }) 652 653 t.Run("no envelope", func(t *testing.T) { 654 certs, err := cluster.EndpointconfigFromConfigBlock(&common.Block{ 655 Data: &common.BlockData{}, 656 }, cryptoProvider) 657 assert.Nil(t, certs) 658 assert.EqualError(t, err, "envelope index out of bounds") 659 }) 660 661 t.Run("bad envelope", func(t *testing.T) { 662 certs, err := cluster.EndpointconfigFromConfigBlock(&common.Block{ 663 Data: &common.BlockData{ 664 Data: [][]byte{{}}, 665 }, 666 }, cryptoProvider) 667 assert.Nil(t, certs) 668 assert.EqualError(t, err, "failed extracting bundle from envelope: envelope header cannot be nil") 669 }) 670 } 671 672 func TestConfigFromBlockBadInput(t *testing.T) { 673 for _, testCase := range []struct { 674 name string 675 block *common.Block 676 expectedError string 677 }{ 678 { 679 name: "nil block", 680 expectedError: "empty block", 681 block: nil, 682 }, 683 { 684 name: "nil block data", 685 expectedError: "empty block", 686 block: &common.Block{}, 687 }, 688 { 689 name: "no data in block", 690 expectedError: "empty block", 691 block: &common.Block{Data: &common.BlockData{}}, 692 }, 693 { 694 name: "invalid payload", 695 expectedError: "error unmarshaling Envelope: proto: common.Envelope: illegal tag 0 (wire type 1)", 696 block: &common.Block{Data: &common.BlockData{Data: [][]byte{{1, 2, 3}}}}, 697 }, 698 { 699 name: "bad genesis block", 700 expectedError: "invalid config envelope: proto: common.ConfigEnvelope: illegal tag 0 (wire type 1)", 701 block: &common.Block{ 702 Header: &common.BlockHeader{}, Data: &common.BlockData{Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{ 703 Payload: protoutil.MarshalOrPanic(&common.Payload{ 704 Data: []byte{1, 2, 3}, 705 }), 706 })}}}, 707 }, 708 { 709 name: "invalid envelope in block", 710 expectedError: "error unmarshaling Envelope: proto: common.Envelope: illegal tag 0 (wire type 1)", 711 block: &common.Block{Data: &common.BlockData{Data: [][]byte{{1, 2, 3}}}}, 712 }, 713 { 714 name: "invalid payload in block envelope", 715 expectedError: "error unmarshaling Payload: proto: common.Payload: illegal tag 0 (wire type 1)", 716 block: &common.Block{Data: &common.BlockData{Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{ 717 Payload: []byte{1, 2, 3}, 718 })}}}, 719 }, 720 { 721 name: "invalid channel header", 722 expectedError: "error unmarshaling ChannelHeader: proto: common.ChannelHeader: illegal tag 0 (wire type 1)", 723 block: &common.Block{ 724 Header: &common.BlockHeader{Number: 1}, 725 Data: &common.BlockData{Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{ 726 Payload: protoutil.MarshalOrPanic(&common.Payload{ 727 Header: &common.Header{ 728 ChannelHeader: []byte{1, 2, 3}, 729 }, 730 }), 731 })}}}, 732 }, 733 { 734 name: "invalid config block", 735 expectedError: "invalid config envelope: proto: common.ConfigEnvelope: illegal tag 0 (wire type 1)", 736 block: &common.Block{ 737 Header: &common.BlockHeader{}, 738 Data: &common.BlockData{Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{ 739 Payload: protoutil.MarshalOrPanic(&common.Payload{ 740 Data: []byte{1, 2, 3}, 741 Header: &common.Header{ 742 ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{ 743 Type: int32(common.HeaderType_CONFIG), 744 }), 745 }, 746 }), 747 })}}}, 748 }, 749 } { 750 t.Run(testCase.name, func(t *testing.T) { 751 conf, err := cluster.ConfigFromBlock(testCase.block) 752 assert.Nil(t, conf) 753 assert.EqualError(t, err, testCase.expectedError) 754 }) 755 } 756 } 757 758 func TestBlockValidationPolicyVerifier(t *testing.T) { 759 config := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 760 group, err := encoder.NewChannelGroup(config) 761 assert.NoError(t, err) 762 assert.NotNil(t, group) 763 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 764 assert.NoError(t, err) 765 766 validConfigEnvelope := &common.ConfigEnvelope{ 767 Config: &common.Config{ 768 ChannelGroup: group, 769 }, 770 } 771 772 for _, testCase := range []struct { 773 description string 774 expectedError string 775 envelope *common.ConfigEnvelope 776 policyMap map[string]policies.Policy 777 policy policies.Policy 778 }{ 779 /** 780 { 781 description: "policy not found", 782 expectedError: "policy /Channel/Orderer/BlockValidation wasn't found", 783 }, 784 */ 785 { 786 description: "policy evaluation fails", 787 expectedError: "invalid signature", 788 policy: &mocks.Policy{ 789 EvaluateSignedDataStub: func([]*protoutil.SignedData) error { 790 return errors.New("invalid signature") 791 }, 792 }, 793 }, 794 { 795 description: "bad config envelope", 796 expectedError: "config must contain a channel group", 797 policy: &mocks.Policy{}, 798 envelope: &common.ConfigEnvelope{Config: &common.Config{}}, 799 }, 800 { 801 description: "good config envelope overrides custom policy manager", 802 policy: &mocks.Policy{ 803 EvaluateSignedDataStub: func([]*protoutil.SignedData) error { 804 return errors.New("invalid signature") 805 }, 806 }, 807 envelope: validConfigEnvelope, 808 }, 809 } { 810 t.Run(testCase.description, func(t *testing.T) { 811 mockPolicyManager := &mocks.PolicyManager{} 812 if testCase.policy != nil { 813 mockPolicyManager.GetPolicyReturns(testCase.policy, true) 814 } else { 815 mockPolicyManager.GetPolicyReturns(nil, false) 816 } 817 mockPolicyManager.GetPolicyReturns(testCase.policy, true) 818 verifier := &cluster.BlockValidationPolicyVerifier{ 819 Logger: flogging.MustGetLogger("test"), 820 Channel: "mychannel", 821 PolicyMgr: mockPolicyManager, 822 BCCSP: cryptoProvider, 823 } 824 825 err := verifier.VerifyBlockSignature(nil, testCase.envelope) 826 if testCase.expectedError != "" { 827 assert.EqualError(t, err, testCase.expectedError) 828 } else { 829 assert.NoError(t, err) 830 } 831 }) 832 } 833 } 834 835 func TestBlockVerifierAssembler(t *testing.T) { 836 config := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 837 group, err := encoder.NewChannelGroup(config) 838 assert.NoError(t, err) 839 assert.NotNil(t, group) 840 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 841 assert.NoError(t, err) 842 843 t.Run("Good config envelope", func(t *testing.T) { 844 bva := &cluster.BlockVerifierAssembler{BCCSP: cryptoProvider} 845 verifier, err := bva.VerifierFromConfig(&common.ConfigEnvelope{ 846 Config: &common.Config{ 847 ChannelGroup: group, 848 }, 849 }, "mychannel") 850 assert.NoError(t, err) 851 852 assert.NoError(t, verifier.VerifyBlockSignature(nil, nil)) 853 }) 854 855 t.Run("Bad config envelope", func(t *testing.T) { 856 bva := &cluster.BlockVerifierAssembler{BCCSP: cryptoProvider} 857 _, err := bva.VerifierFromConfig(&common.ConfigEnvelope{}, "mychannel") 858 assert.EqualError(t, err, "failed extracting bundle from envelope: channelconfig Config cannot be nil") 859 }) 860 } 861 862 func TestLastConfigBlock(t *testing.T) { 863 blockRetriever := &mocks.BlockRetriever{} 864 blockRetriever.On("Block", uint64(42)).Return(&common.Block{}) 865 blockRetriever.On("Block", uint64(666)).Return(nil) 866 867 for _, testCase := range []struct { 868 name string 869 block *common.Block 870 blockRetriever cluster.BlockRetriever 871 expectedError string 872 }{ 873 { 874 name: "nil block", 875 expectedError: "nil block", 876 blockRetriever: blockRetriever, 877 }, 878 { 879 name: "nil support", 880 expectedError: "nil blockRetriever", 881 block: &common.Block{}, 882 }, 883 { 884 name: "nil metadata", 885 expectedError: "failed to retrieve metadata: no metadata in block", 886 blockRetriever: blockRetriever, 887 block: &common.Block{}, 888 }, 889 { 890 name: "no block with index", 891 block: &common.Block{ 892 Metadata: &common.BlockMetadata{ 893 Metadata: [][]byte{{}, protoutil.MarshalOrPanic(&common.Metadata{ 894 Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: 666}), 895 })}, 896 }, 897 }, 898 expectedError: "unable to retrieve last config block [666]", 899 blockRetriever: blockRetriever, 900 }, 901 { 902 name: "valid last config block", 903 block: &common.Block{ 904 Metadata: &common.BlockMetadata{ 905 Metadata: [][]byte{{}, protoutil.MarshalOrPanic(&common.Metadata{ 906 Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: 42}), 907 })}, 908 }, 909 }, 910 blockRetriever: blockRetriever, 911 }, 912 } { 913 testCase := testCase 914 t.Run(testCase.name, func(t *testing.T) { 915 block, err := cluster.LastConfigBlock(testCase.block, testCase.blockRetriever) 916 if testCase.expectedError == "" { 917 assert.NoError(t, err) 918 assert.NotNil(t, block) 919 return 920 } 921 assert.EqualError(t, err, testCase.expectedError) 922 assert.Nil(t, block) 923 }) 924 } 925 } 926 927 func TestVerificationRegistryRegisterVerifier(t *testing.T) { 928 blockBytes, err := ioutil.ReadFile("testdata/mychannel.block") 929 assert.NoError(t, err) 930 931 block := &common.Block{} 932 assert.NoError(t, proto.Unmarshal(blockBytes, block)) 933 934 verifier := &mocks.BlockVerifier{} 935 936 verifierFactory := &mocks.VerifierFactory{} 937 verifierFactory.On("VerifierFromConfig", 938 mock.Anything, "mychannel").Return(verifier, nil) 939 940 registry := &cluster.VerificationRegistry{ 941 Logger: flogging.MustGetLogger("test"), 942 VerifiersByChannel: make(map[string]cluster.BlockVerifier), 943 VerifierFactory: verifierFactory, 944 } 945 946 var loadCount int 947 registry.LoadVerifier = func(chain string) cluster.BlockVerifier { 948 assert.Equal(t, "mychannel", chain) 949 loadCount++ 950 return verifier 951 } 952 953 v := registry.RetrieveVerifier("mychannel") 954 assert.Nil(t, v) 955 956 registry.RegisterVerifier("mychannel") 957 v = registry.RetrieveVerifier("mychannel") 958 assert.Equal(t, verifier, v) 959 assert.Equal(t, 1, loadCount) 960 961 // If the verifier exists, this is a no-op 962 registry.RegisterVerifier("mychannel") 963 assert.Equal(t, 1, loadCount) 964 } 965 966 func TestVerificationRegistry(t *testing.T) { 967 blockBytes, err := ioutil.ReadFile("testdata/mychannel.block") 968 assert.NoError(t, err) 969 970 block := &common.Block{} 971 assert.NoError(t, proto.Unmarshal(blockBytes, block)) 972 973 flogging.ActivateSpec("test=DEBUG") 974 defer flogging.Reset() 975 976 verifier := &mocks.BlockVerifier{} 977 978 for _, testCase := range []struct { 979 description string 980 verifiersByChannel map[string]cluster.BlockVerifier 981 blockCommitted *common.Block 982 channelCommitted string 983 channelRetrieved string 984 expectedVerifier cluster.BlockVerifier 985 verifierFromConfig cluster.BlockVerifier 986 verifierFromConfigErr error 987 loggedMessages map[string]struct{} 988 }{ 989 { 990 description: "bad block", 991 blockCommitted: &common.Block{}, 992 channelRetrieved: "foo", 993 channelCommitted: "foo", 994 loggedMessages: map[string]struct{}{ 995 "Failed parsing block of channel foo: empty block, content: " + 996 "{\n\t\"data\": null,\n\t\"header\": null,\n\t\"metadata\": null\n}\n": {}, 997 "No verifier for channel foo exists": {}, 998 }, 999 expectedVerifier: nil, 1000 }, 1001 { 1002 description: "not a config block", 1003 blockCommitted: createBlockChain(5, 5)[0], 1004 channelRetrieved: "foo", 1005 channelCommitted: "foo", 1006 loggedMessages: map[string]struct{}{ 1007 "No verifier for channel foo exists": {}, 1008 "Committed block [5] for channel foo that is not a config block": {}, 1009 }, 1010 expectedVerifier: nil, 1011 }, 1012 { 1013 description: "valid block but verifier from config fails", 1014 blockCommitted: block, 1015 verifierFromConfigErr: errors.New("invalid MSP config"), 1016 channelRetrieved: "bar", 1017 channelCommitted: "bar", 1018 loggedMessages: map[string]struct{}{ 1019 "Failed creating a verifier from a " + 1020 "config block for channel bar: invalid MSP config, " + 1021 "content: " + cluster.BlockToString(block): {}, 1022 "No verifier for channel bar exists": {}, 1023 }, 1024 expectedVerifier: nil, 1025 }, 1026 { 1027 description: "valid block and verifier from config succeeds but wrong channel retrieved", 1028 blockCommitted: block, 1029 verifierFromConfig: verifier, 1030 channelRetrieved: "foo", 1031 channelCommitted: "bar", 1032 loggedMessages: map[string]struct{}{ 1033 "No verifier for channel foo exists": {}, 1034 "Committed config block [0] for channel bar": {}, 1035 }, 1036 expectedVerifier: nil, 1037 verifiersByChannel: make(map[string]cluster.BlockVerifier), 1038 }, 1039 { 1040 description: "valid block and verifier from config succeeds", 1041 blockCommitted: block, 1042 verifierFromConfig: verifier, 1043 channelRetrieved: "bar", 1044 channelCommitted: "bar", 1045 loggedMessages: map[string]struct{}{ 1046 "Committed config block [0] for channel bar": {}, 1047 }, 1048 expectedVerifier: verifier, 1049 verifiersByChannel: make(map[string]cluster.BlockVerifier), 1050 }, 1051 } { 1052 t.Run(testCase.description, func(t *testing.T) { 1053 verifierFactory := &mocks.VerifierFactory{} 1054 verifierFactory.On("VerifierFromConfig", 1055 mock.Anything, testCase.channelCommitted).Return(testCase.verifierFromConfig, testCase.verifierFromConfigErr) 1056 1057 registry := &cluster.VerificationRegistry{ 1058 Logger: flogging.MustGetLogger("test"), 1059 VerifiersByChannel: testCase.verifiersByChannel, 1060 VerifierFactory: verifierFactory, 1061 } 1062 1063 loggedEntriesByMethods := make(map[string]struct{}) 1064 // Configure the logger to collect the message logged 1065 registry.Logger = registry.Logger.WithOptions(zap.Hooks(func(entry zapcore.Entry) error { 1066 loggedEntriesByMethods[entry.Message] = struct{}{} 1067 return nil 1068 })) 1069 1070 registry.BlockCommitted(testCase.blockCommitted, testCase.channelCommitted) 1071 verifier := registry.RetrieveVerifier(testCase.channelRetrieved) 1072 1073 assert.Equal(t, testCase.loggedMessages, loggedEntriesByMethods) 1074 assert.Equal(t, testCase.expectedVerifier, verifier) 1075 }) 1076 } 1077 } 1078 1079 func TestLedgerInterceptor(t *testing.T) { 1080 block := &common.Block{} 1081 1082 ledger := &mocks.LedgerWriter{} 1083 ledger.On("Append", block).Return(nil).Once() 1084 1085 var intercepted bool 1086 1087 var interceptedLedger cluster.LedgerWriter = &cluster.LedgerInterceptor{ 1088 Channel: "mychannel", 1089 LedgerWriter: ledger, 1090 InterceptBlockCommit: func(b *common.Block, channel string) { 1091 assert.Equal(t, block, b) 1092 assert.Equal(t, "mychannel", channel) 1093 intercepted = true 1094 }, 1095 } 1096 1097 err := interceptedLedger.Append(block) 1098 assert.NoError(t, err) 1099 assert.True(t, intercepted) 1100 ledger.AssertCalled(t, "Append", block) 1101 } 1102 1103 func injectAdditionalTLSCAEndpointPair(t *testing.T, block *common.Block, endpoint string, tlsCA []byte, orgName string) { 1104 // Unwrap the layers until we reach the orderer addresses 1105 env, err := protoutil.ExtractEnvelope(block, 0) 1106 assert.NoError(t, err) 1107 payload, err := protoutil.UnmarshalPayload(env.Payload) 1108 assert.NoError(t, err) 1109 confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data) 1110 assert.NoError(t, err) 1111 ordererGrp := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Groups 1112 // Get the first orderer org config 1113 var firstOrdererConfig *common.ConfigGroup 1114 for _, grp := range ordererGrp { 1115 firstOrdererConfig = grp 1116 break 1117 } 1118 // Duplicate it. 1119 secondOrdererConfig := proto.Clone(firstOrdererConfig).(*common.ConfigGroup) 1120 ordererGrp[orgName] = secondOrdererConfig 1121 // Reach the FabricMSPConfig buried in it. 1122 mspConfig := &msp.MSPConfig{} 1123 err = proto.Unmarshal(secondOrdererConfig.Values[channelconfig.MSPKey].Value, mspConfig) 1124 assert.NoError(t, err) 1125 1126 fabricConfig := &msp.FabricMSPConfig{} 1127 err = proto.Unmarshal(mspConfig.Config, fabricConfig) 1128 assert.NoError(t, err) 1129 1130 // Plant the given TLS CA in it. 1131 fabricConfig.TlsRootCerts = [][]byte{tlsCA} 1132 // No intermediate root CAs, to make the test simpler. 1133 fabricConfig.TlsIntermediateCerts = nil 1134 // Rename it. 1135 fabricConfig.Name = orgName 1136 1137 // Pack the MSP config back into the config 1138 secondOrdererConfig.Values[channelconfig.MSPKey].Value = protoutil.MarshalOrPanic(&msp.MSPConfig{ 1139 Config: protoutil.MarshalOrPanic(fabricConfig), 1140 Type: mspConfig.Type, 1141 }) 1142 1143 // Inject the endpoint 1144 ordererOrgProtos := &common.OrdererAddresses{ 1145 Addresses: []string{endpoint}, 1146 } 1147 secondOrdererConfig.Values[channelconfig.EndpointsKey].Value = protoutil.MarshalOrPanic(ordererOrgProtos) 1148 1149 // Fold everything back into the block 1150 payload.Data = protoutil.MarshalOrPanic(confEnv) 1151 env.Payload = protoutil.MarshalOrPanic(payload) 1152 block.Data.Data[0] = protoutil.MarshalOrPanic(env) 1153 } 1154 1155 func TestEndpointCriteriaString(t *testing.T) { 1156 // The top cert is the issuer of the bottom cert 1157 certs := `-----BEGIN CERTIFICATE----- 1158 MIIBozCCAUigAwIBAgIQMXmzUnikiAZDr4VsrBL+rzAKBggqhkjOPQQDAjAxMS8w 1159 LQYDVQQFEyY2NTc2NDA3Njc5ODcwOTA3OTEwNDM5NzkxMTAwNzA0Mzk3Njg3OTAe 1160 Fw0xOTExMTEyMDM5MDRaFw0yOTExMDkyMDM5MDRaMDExLzAtBgNVBAUTJjY1NzY0 1161 MDc2Nzk4NzA5MDc5MTA0Mzk3OTExMDA3MDQzOTc2ODc5MFkwEwYHKoZIzj0CAQYI 1162 KoZIzj0DAQcDQgAEzBBkRvWgasCKf1pejwpOu+1Fv9FffOZMHnna/7lfMrAqOs8d 1163 HMDVU7mSexu7YNTpAwm4vkdHXi35H8zlVABTxaNCMEAwDgYDVR0PAQH/BAQDAgGm 1164 MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/ 1165 MAoGCCqGSM49BAMCA0kAMEYCIQCXqXoYLAJN9diIdGxPlRQJgJLju4brWXZfyt3s 1166 E9TjFwIhAOuUJjcOchdP6UA9WLnVWciEo1Omf59NgfHL1gUPb/t6 1167 -----END CERTIFICATE----- 1168 -----BEGIN CERTIFICATE----- 1169 MIIBpDCCAUqgAwIBAgIRAIyvtL0z1xQ+NecXeH1HmmAwCgYIKoZIzj0EAwIwMTEv 1170 MC0GA1UEBRMmNjU3NjQwNzY3OTg3MDkwNzkxMDQzOTc5MTEwMDcwNDM5NzY4Nzkw 1171 HhcNMTkxMTExMjAzOTA0WhcNMTkxMTEyMjAzOTA0WjAyMTAwLgYDVQQFEycxODcw 1172 MDQyMzcxODQwMjY5Mzk2ODUxNzk1NzM3MzIyMTc2OTA3MjAwWTATBgcqhkjOPQIB 1173 BggqhkjOPQMBBwNCAARZBFDBOfC7T9RbsX+PgyE6sM7ocuwn6krIGjc00ICivFgQ 1174 qdHMU7hiswiYwSvwh9MDHlprCRW3ycSgEYQgKU5to0IwQDAOBgNVHQ8BAf8EBAMC 1175 BaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA8GA1UdEQQIMAaHBH8A 1176 AAEwCgYIKoZIzj0EAwIDSAAwRQIhAK6G7qr/ClszCFP25gsflA31+7eoss5vi3o4 1177 qz8bY+s6AiBvO0aOfE8M4ibjmRE4vSXo0+gkOIJKqZcmiRdnJSr8Xw== 1178 -----END CERTIFICATE-----` 1179 1180 epc := cluster.EndpointCriteria{ 1181 Endpoint: "orderer.example.com:7050", 1182 TLSRootCAs: [][]byte{[]byte(certs)}, 1183 } 1184 1185 actual := fmt.Sprint(epc) 1186 expected := `{"CAs":[{"Expired":false,"Issuer":"self","Subject":"SERIALNUMBER=65764076798709079104397911007043976879"},{"Expired":true,"Issuer":"SERIALNUMBER=65764076798709079104397911007043976879","Subject":"SERIALNUMBER=187004237184026939685179573732217690720"}],"Endpoint":"orderer.example.com:7050"}` 1187 assert.Equal(t, expected, actual) 1188 }