github.com/kaituanwang/hyperledger@v2.0.1+incompatible/orderer/common/server/onboarding_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package server 8 9 import ( 10 "fmt" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "strings" 16 "sync" 17 "sync/atomic" 18 "testing" 19 "time" 20 21 "github.com/golang/protobuf/proto" 22 "github.com/hyperledger/fabric-protos-go/common" 23 "github.com/hyperledger/fabric-protos-go/orderer" 24 "github.com/hyperledger/fabric-protos-go/orderer/etcdraft" 25 "github.com/hyperledger/fabric/bccsp/sw" 26 "github.com/hyperledger/fabric/common/channelconfig" 27 "github.com/hyperledger/fabric/common/configtx" 28 deliver_mocks "github.com/hyperledger/fabric/common/deliver/mock" 29 "github.com/hyperledger/fabric/common/flogging" 30 ledger_mocks "github.com/hyperledger/fabric/common/ledger/blockledger/mocks" 31 "github.com/hyperledger/fabric/core/comm" 32 "github.com/hyperledger/fabric/core/config/configtest" 33 "github.com/hyperledger/fabric/internal/pkg/identity" 34 "github.com/hyperledger/fabric/orderer/common/cluster" 35 "github.com/hyperledger/fabric/orderer/common/cluster/mocks" 36 "github.com/hyperledger/fabric/orderer/common/localconfig" 37 server_mocks "github.com/hyperledger/fabric/orderer/common/server/mocks" 38 "github.com/hyperledger/fabric/protoutil" 39 . "github.com/onsi/gomega" 40 "github.com/onsi/gomega/gbytes" 41 "github.com/onsi/gomega/gexec" 42 "github.com/pkg/errors" 43 "github.com/stretchr/testify/assert" 44 "github.com/stretchr/testify/mock" 45 "go.uber.org/zap" 46 "go.uber.org/zap/zapcore" 47 ) 48 49 // the paths to configtxgen and cryptogen, which can be used by tests to create 50 // genesis blocks and certificates 51 var configtxgen, cryptogen, tempDir string 52 53 func TestMain(m *testing.M) { 54 var err error 55 configtxgen, err = gexec.Build("github.com/hyperledger/fabric/cmd/configtxgen") 56 if err != nil { 57 os.Exit(-1) 58 } 59 cryptogen, err = gexec.Build("github.com/hyperledger/fabric/cmd/cryptogen") 60 if err != nil { 61 os.Exit(-1) 62 } 63 defer gexec.CleanupBuildArtifacts() 64 65 tempDir, err = ioutil.TempDir("", "onboarding-test") 66 defer os.RemoveAll(tempDir) 67 68 copyYamlFiles("testdata", tempDir) 69 70 os.Exit(m.Run()) 71 } 72 73 func copyYamlFiles(src, dst string) { 74 for _, file := range []string{"configtx.yaml", "examplecom-config.yaml", "orderer.yaml"} { 75 fileBytes, err := ioutil.ReadFile(filepath.Join(src, file)) 76 if err != nil { 77 os.Exit(-1) 78 } 79 err = ioutil.WriteFile(filepath.Join(dst, file), fileBytes, 0644) 80 if err != nil { 81 os.Exit(-1) 82 } 83 } 84 } 85 86 func newServerNode(t *testing.T, key, cert []byte) *deliverServer { 87 srv, err := comm.NewGRPCServer("127.0.0.1:0", comm.ServerConfig{ 88 SecOpts: comm.SecureOptions{ 89 Key: key, 90 Certificate: cert, 91 UseTLS: true, 92 }, 93 }) 94 if err != nil { 95 panic(err) 96 } 97 ds := &deliverServer{ 98 t: t, 99 blockResponses: make(chan *orderer.DeliverResponse, 100), 100 srv: srv, 101 } 102 orderer.RegisterAtomicBroadcastServer(srv.Server(), ds) 103 go srv.Start() 104 return ds 105 } 106 107 type deliverServer struct { 108 isConnected int32 109 t *testing.T 110 srv *comm.GRPCServer 111 blockResponses chan *orderer.DeliverResponse 112 } 113 114 func (*deliverServer) Broadcast(orderer.AtomicBroadcast_BroadcastServer) error { 115 panic("implement me") 116 } 117 118 func (ds *deliverServer) Deliver(stream orderer.AtomicBroadcast_DeliverServer) error { 119 atomic.StoreInt32(&ds.isConnected, 1) 120 seekInfo, err := readSeekEnvelope(stream) 121 if err != nil { 122 panic(err) 123 } 124 if seekInfo.GetStart().GetSpecified() != nil { 125 return ds.deliverBlocks(stream) 126 } 127 if seekInfo.GetStart().GetNewest() != nil { 128 resp := <-ds.blockResponses 129 if resp == nil { 130 return nil 131 } 132 return stream.Send(resp) 133 } 134 panic(fmt.Sprintf("expected either specified or newest seek but got %v", seekInfo.GetStart())) 135 } 136 137 func readSeekEnvelope(stream orderer.AtomicBroadcast_DeliverServer) (*orderer.SeekInfo, error) { 138 env, err := stream.Recv() 139 if err != nil { 140 return nil, err 141 } 142 payload, err := protoutil.UnmarshalPayload(env.Payload) 143 if err != nil { 144 return nil, err 145 } 146 seekInfo := &orderer.SeekInfo{} 147 if err = proto.Unmarshal(payload.Data, seekInfo); err != nil { 148 return nil, err 149 } 150 return seekInfo, nil 151 } 152 153 func (ds *deliverServer) deliverBlocks(stream orderer.AtomicBroadcast_DeliverServer) error { 154 for { 155 blockChan := ds.blockResponses 156 response := <-blockChan 157 if response == nil { 158 return nil 159 } 160 if err := stream.Send(response); err != nil { 161 return err 162 } 163 } 164 } 165 166 func loadPEM(cryptoPath, suffix string, t *testing.T) []byte { 167 ordererTLSPath := filepath.Join(cryptoPath, "ordererOrganizations", "example.com", "orderers", "127.0.0.1.example.com", "tls") 168 b, err := ioutil.ReadFile(filepath.Join(ordererTLSPath, suffix)) 169 assert.NoError(t, err) 170 return b 171 } 172 173 func channelCreationBlock(systemChannel, applicationChannel string, prevBlock *common.Block) *common.Block { 174 block := &common.Block{ 175 Header: &common.BlockHeader{ 176 Number: prevBlock.Header.Number + 1, 177 PreviousHash: protoutil.BlockHeaderHash(prevBlock.Header), 178 }, 179 Metadata: &common.BlockMetadata{ 180 Metadata: [][]byte{{}, {}, {}, {}}, 181 }, 182 Data: &common.BlockData{ 183 Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{ 184 Payload: protoutil.MarshalOrPanic(&common.Payload{ 185 Header: &common.Header{ 186 ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{ 187 ChannelId: systemChannel, 188 Type: int32(common.HeaderType_ORDERER_TRANSACTION), 189 }), 190 }, 191 Data: protoutil.MarshalOrPanic(&common.Envelope{ 192 Payload: protoutil.MarshalOrPanic(&common.Payload{ 193 Header: &common.Header{ 194 ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{ 195 Type: int32(common.HeaderType_CONFIG), 196 ChannelId: applicationChannel, 197 }), 198 }, 199 }), 200 }), 201 }), 202 })}, 203 }, 204 } 205 206 block.Header.DataHash = protoutil.BlockDataHash(block.Data) 207 return block 208 } 209 210 func TestOnboardingChannelUnavailable(t *testing.T) { 211 // Scenario: During the probing phase of the onboarding, 212 // a channel is deemed relevant and we try to pull it during the 213 // second phase, but alas - precisely at that time - it becomes 214 // unavailable. 215 // The onboarding code is expected to skip the replication after 216 // the maximum attempt number is exhausted, and not to try to replicate 217 // the channel indefinitely. 218 cryptoPath := generateCryptoMaterials(t, cryptogen) 219 defer os.RemoveAll(cryptoPath) 220 221 caCert := loadPEM(cryptoPath, "ca.crt", t) 222 key := loadPEM(cryptoPath, "server.key", t) 223 cert := loadPEM(cryptoPath, "server.crt", t) 224 225 deliverServer := newServerNode(t, key, cert) 226 defer deliverServer.srv.Stop() 227 228 systemChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "system", "SampleSoloSystemChannel") 229 systemChannelBlockBytes, err := ioutil.ReadFile(systemChannelBlockPath) 230 assert.NoError(t, err) 231 232 applicationChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "testchannel", "SampleOrgChannel") 233 applicationChannelBlockBytes, err := ioutil.ReadFile(applicationChannelBlockPath) 234 assert.NoError(t, err) 235 236 testchannelGB := &common.Block{} 237 assert.NoError(t, proto.Unmarshal(applicationChannelBlockBytes, testchannelGB)) 238 testchannelGB.Header.Number = 0 239 240 systemChannelGenesisBlock := &common.Block{ 241 Header: &common.BlockHeader{ 242 Number: 0, 243 }, 244 Metadata: &common.BlockMetadata{ 245 Metadata: [][]byte{{}, {}, {}}, 246 }, 247 Data: &common.BlockData{Data: [][]byte{protoutil.MarshalOrPanic(&common.Envelope{ 248 Payload: protoutil.MarshalOrPanic(&common.Payload{ 249 Header: &common.Header{}, 250 }), 251 })}}, 252 } 253 systemChannelGenesisBlock.Header.DataHash = protoutil.BlockDataHash(systemChannelGenesisBlock.Data) 254 255 channelCreationBlock := channelCreationBlock("system", "testchannel", systemChannelGenesisBlock) 256 257 bootBlock := &common.Block{} 258 assert.NoError(t, proto.Unmarshal(systemChannelBlockBytes, bootBlock)) 259 bootBlock.Header.Number = 2 260 bootBlock.Header.PreviousHash = protoutil.BlockHeaderHash(channelCreationBlock.Header) 261 injectOrdererEndpoint(t, bootBlock, deliverServer.srv.Address()) 262 injectConsenterCertificate(t, testchannelGB, cert) 263 264 blocksCommittedToSystemLedger := make(chan uint64, 3) 265 blocksCommittedToApplicationLedger := make(chan uint64, 1) 266 267 systemLedger := &mocks.LedgerWriter{} 268 systemLedger.On("Height").Return(uint64(0)) 269 systemLedger.On("Append", mock.Anything).Return(nil).Run(func(arguments mock.Arguments) { 270 seq := arguments.Get(0).(*common.Block).Header.Number 271 blocksCommittedToSystemLedger <- seq 272 }) 273 274 appLedger := &mocks.LedgerWriter{} 275 appLedger.On("Height").Return(uint64(0)) 276 appLedger.On("Append", mock.Anything).Return(nil).Run(func(arguments mock.Arguments) { 277 seq := arguments.Get(0).(*common.Block).Header.Number 278 blocksCommittedToApplicationLedger <- seq 279 }) 280 281 lf := &mocks.LedgerFactory{} 282 lf.On("GetOrCreate", "system").Return(systemLedger, nil) 283 lf.On("GetOrCreate", "testchannel").Return(appLedger, nil) 284 lf.On("Close") 285 286 config := &localconfig.TopLevel{ 287 General: localconfig.General{ 288 Cluster: localconfig.Cluster{ 289 ReplicationPullTimeout: time.Hour, 290 DialTimeout: time.Hour, 291 RPCTimeout: time.Hour, 292 ReplicationRetryTimeout: time.Millisecond, 293 ReplicationBufferSize: 1, 294 ReplicationMaxRetries: 5, 295 }, 296 }, 297 } 298 299 secConfig := comm.SecureOptions{ 300 Certificate: cert, 301 Key: key, 302 UseTLS: true, 303 ServerRootCAs: [][]byte{caCert}, 304 } 305 306 verifier := &mocks.BlockVerifier{} 307 verifier.On("VerifyBlockSignature", mock.Anything, mock.Anything).Return(nil) 308 vr := &mocks.VerifierRetriever{} 309 vr.On("RetrieveVerifier", mock.Anything).Return(verifier) 310 311 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 312 assert.NoError(t, err) 313 314 r := &replicationInitiator{ 315 verifierRetriever: vr, 316 lf: lf, 317 logger: flogging.MustGetLogger("testOnboarding"), 318 conf: config, 319 secOpts: secConfig, 320 cryptoProvider: cryptoProvider, 321 } 322 323 type event struct { 324 expectedLog string 325 responseFunc func(chan *orderer.DeliverResponse) 326 } 327 328 var probe, pullSystemChannel, pullAppChannel, failedPulling bool 329 330 var loggerHooks []zap.Option 331 332 for _, e := range []event{ 333 { 334 expectedLog: "Probing whether I should pull channel testchannel", 335 responseFunc: func(blockResponses chan *orderer.DeliverResponse) { 336 probe = true 337 338 // At this point the client will re-connect, so close the stream. 339 blockResponses <- nil 340 // And send the genesis block of the application channel 'testchannel' 341 blockResponses <- &orderer.DeliverResponse{ 342 Type: &orderer.DeliverResponse_Block{ 343 Block: testchannelGB, 344 }, 345 } 346 blockResponses <- &orderer.DeliverResponse{ 347 Type: &orderer.DeliverResponse_Block{ 348 Block: testchannelGB, 349 }, 350 } 351 blockResponses <- &orderer.DeliverResponse{ 352 Type: &orderer.DeliverResponse_Block{ 353 Block: testchannelGB, 354 }, 355 } 356 blockResponses <- nil 357 blockResponses <- &orderer.DeliverResponse{ 358 Type: &orderer.DeliverResponse_Block{ 359 Block: testchannelGB, 360 }, 361 } 362 blockResponses <- &orderer.DeliverResponse{ 363 Type: &orderer.DeliverResponse_Block{ 364 Block: testchannelGB, 365 }, 366 } 367 blockResponses <- nil 368 }, 369 }, 370 { 371 expectedLog: "Pulling channel system", 372 responseFunc: func(blockResponses chan *orderer.DeliverResponse) { 373 pullSystemChannel = true 374 375 blockResponses <- &orderer.DeliverResponse{ 376 Type: &orderer.DeliverResponse_Block{ 377 Block: bootBlock, 378 }, 379 } 380 blockResponses <- &orderer.DeliverResponse{ 381 Type: &orderer.DeliverResponse_Block{ 382 Block: bootBlock, 383 }, 384 } 385 blockResponses <- &orderer.DeliverResponse{ 386 Type: &orderer.DeliverResponse_Block{ 387 Block: systemChannelGenesisBlock, 388 }, 389 } 390 blockResponses <- &orderer.DeliverResponse{ 391 Type: &orderer.DeliverResponse_Block{ 392 Block: channelCreationBlock, 393 }, 394 } 395 blockResponses <- &orderer.DeliverResponse{ 396 Type: &orderer.DeliverResponse_Block{ 397 Block: bootBlock, 398 }, 399 } 400 }, 401 }, 402 { 403 expectedLog: "Pulling channel testchannel", 404 responseFunc: func(blockResponses chan *orderer.DeliverResponse) { 405 pullAppChannel = true 406 407 for i := 0; i < config.General.Cluster.ReplicationMaxRetries+1; i++ { 408 // Send once the genesis block, to make the client think this is a valid OSN endpoint 409 deliverServer.blockResponses <- &orderer.DeliverResponse{ 410 Type: &orderer.DeliverResponse_Block{ 411 Block: testchannelGB, 412 }, 413 } 414 // Send EOF to make the client abort and retry again 415 deliverServer.blockResponses <- nil 416 } 417 }, 418 }, 419 { 420 expectedLog: "Failed pulling channel testchannel: retry attempts exhausted", 421 responseFunc: func(blockResponses chan *orderer.DeliverResponse) { 422 failedPulling = true 423 }, 424 }, 425 } { 426 event := e 427 loggerHooks = append(loggerHooks, zap.Hooks(func(entry zapcore.Entry) error { 428 if strings.Contains(entry.Message, event.expectedLog) { 429 event.responseFunc(deliverServer.blockResponses) 430 } 431 return nil 432 })) 433 } 434 435 // Program the logger to intercept the event 436 r.logger = r.logger.WithOptions(loggerHooks...) 437 438 // Send the latest system channel block (sequence 2) that is identical to the bootstrap block 439 deliverServer.blockResponses <- &orderer.DeliverResponse{ 440 Type: &orderer.DeliverResponse_Block{ 441 Block: bootBlock, 442 }, 443 } 444 // Send the bootstrap block of the system channel 445 deliverServer.blockResponses <- &orderer.DeliverResponse{ 446 Type: &orderer.DeliverResponse_Block{ 447 Block: systemChannelGenesisBlock, 448 }, 449 } 450 // Send a channel creation block (sequence 1) that denotes creation of 'testchannel' 451 deliverServer.blockResponses <- &orderer.DeliverResponse{ 452 Type: &orderer.DeliverResponse_Block{ 453 Block: channelCreationBlock, 454 }, 455 } 456 457 r.replicateIfNeeded(bootBlock) 458 459 // Ensure all events were invoked 460 assert.True(t, probe) 461 assert.True(t, pullSystemChannel) 462 assert.True(t, pullAppChannel) 463 assert.True(t, failedPulling) 464 465 // Ensure system channel was fully pulled 466 assert.Len(t, blocksCommittedToSystemLedger, 3) 467 // But the application channel only contains 1 block (the genesis block) 468 assert.Len(t, blocksCommittedToApplicationLedger, 1) 469 } 470 471 func TestReplicate(t *testing.T) { 472 clusterConfig := localconfig.Cluster{ 473 ReplicationPullTimeout: time.Hour, 474 DialTimeout: time.Hour, 475 RPCTimeout: time.Hour, 476 ReplicationRetryTimeout: time.Hour, 477 ReplicationBufferSize: 1, 478 } 479 480 var bootBlock common.Block 481 var bootBlockWithCorruptedPayload common.Block 482 483 flogging.ActivateSpec("testReplicateIfNeeded=debug") 484 485 cleanup := configtest.SetDevFabricConfigPath(t) 486 defer cleanup() 487 488 cryptoPath := generateCryptoMaterials(t, cryptogen) 489 defer os.RemoveAll(cryptoPath) 490 491 applicationChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "testchannel", "SampleOrgChannel") 492 applicationChannelBlockBytes, err := ioutil.ReadFile(applicationChannelBlockPath) 493 assert.NoError(t, err) 494 495 caCert := loadPEM(cryptoPath, "ca.crt", t) 496 key := loadPEM(cryptoPath, "server.key", t) 497 cert := loadPEM(cryptoPath, "server.crt", t) 498 499 prepareTestCase := func() *deliverServer { 500 deliverServer := newServerNode(t, key, cert) 501 502 assert.NoError(t, proto.Unmarshal(applicationChannelBlockBytes, &bootBlock)) 503 bootBlock.Header.Number = 10 504 injectOrdererEndpoint(t, &bootBlock, deliverServer.srv.Address()) 505 506 copyBlock := func(block *common.Block, seq uint64) common.Block { 507 res := common.Block{} 508 assert.NoError(t, proto.Unmarshal(protoutil.MarshalOrPanic(block), &res)) 509 res.Header.Number = seq 510 return res 511 } 512 513 bootBlockWithCorruptedPayload = copyBlock(&bootBlock, 100) 514 env := &common.Envelope{} 515 assert.NoError(t, proto.Unmarshal(bootBlockWithCorruptedPayload.Data.Data[0], env)) 516 payload := &common.Payload{} 517 assert.NoError(t, proto.Unmarshal(env.Payload, payload)) 518 payload.Data = []byte{1, 2, 3} 519 520 deliverServer.blockResponses <- &orderer.DeliverResponse{ 521 Type: &orderer.DeliverResponse_Block{Block: &bootBlock}, 522 } 523 524 blocks := make([]*common.Block, 11) 525 for seq := uint64(0); seq <= uint64(10); seq++ { 526 block := copyBlock(&bootBlock, seq) 527 if seq > 0 { 528 block.Header.PreviousHash = protoutil.BlockHeaderHash(blocks[seq-1].Header) 529 } 530 blocks[seq] = &block 531 deliverServer.blockResponses <- &orderer.DeliverResponse{ 532 Type: &orderer.DeliverResponse_Block{Block: &block}, 533 } 534 } 535 // We close the block responses to mark the server side to return from 536 // the method dispatch. 537 close(deliverServer.blockResponses) 538 539 // We need to ensure the hash chain is valid with respect to the bootstrap block. 540 // Validating the hash chain itself when we traverse channels will be taken care 541 // of in FAB-12926. 542 bootBlock.Header.PreviousHash = protoutil.BlockHeaderHash(blocks[9].Header) 543 return deliverServer 544 } 545 546 var hooksActivated bool 547 548 for _, testCase := range []struct { 549 name string 550 panicValue string 551 systemLedgerHeight uint64 552 bootBlock *common.Block 553 secOpts comm.SecureOptions 554 conf *localconfig.TopLevel 555 ledgerFactoryErr error 556 signer identity.SignerSerializer 557 zapHooks []func(zapcore.Entry) error 558 shouldConnect bool 559 replicateFunc func(*replicationInitiator, *common.Block) 560 verificationCount int 561 }{ 562 { 563 name: "Genesis block makes replication be skipped", 564 bootBlock: &common.Block{Header: &common.BlockHeader{Number: 0}}, 565 systemLedgerHeight: 10, 566 zapHooks: []func(entry zapcore.Entry) error{ 567 func(entry zapcore.Entry) error { 568 hooksActivated = true 569 assert.Equal(t, entry.Message, "Booted with a genesis block, replication isn't an option") 570 return nil 571 }, 572 }, 573 replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) { 574 ri.replicateIfNeeded(bootstrapBlock) 575 }, 576 }, 577 { 578 name: "Block puller initialization failure panics", 579 systemLedgerHeight: 10, 580 panicValue: "Failed creating puller config from bootstrap block: unable to decode TLS certificate PEM: ", 581 bootBlock: &bootBlockWithCorruptedPayload, 582 conf: &localconfig.TopLevel{}, 583 secOpts: comm.SecureOptions{}, 584 replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) { 585 ri.replicateIfNeeded(bootstrapBlock) 586 }, 587 }, 588 { 589 name: "Extraction of system channel name fails", 590 systemLedgerHeight: 10, 591 panicValue: "Failed extracting system channel name from bootstrap block: failed to retrieve channel id - block is empty", 592 bootBlock: &common.Block{Header: &common.BlockHeader{Number: 100}}, 593 conf: &localconfig.TopLevel{}, 594 secOpts: comm.SecureOptions{}, 595 replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) { 596 ri.replicateIfNeeded(bootstrapBlock) 597 }, 598 }, 599 { 600 name: "Is Replication needed fails", 601 systemLedgerHeight: 10, 602 ledgerFactoryErr: errors.New("I/O error"), 603 panicValue: "Failed determining whether replication is needed: I/O error", 604 bootBlock: &bootBlock, 605 conf: &localconfig.TopLevel{}, 606 secOpts: comm.SecureOptions{ 607 Certificate: cert, 608 Key: key, 609 }, 610 replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) { 611 ri.replicateIfNeeded(bootstrapBlock) 612 }, 613 }, 614 { 615 name: "Replication isn't needed", 616 systemLedgerHeight: 11, 617 bootBlock: &bootBlock, 618 conf: &localconfig.TopLevel{}, 619 secOpts: comm.SecureOptions{ 620 Certificate: cert, 621 Key: key, 622 }, 623 zapHooks: []func(entry zapcore.Entry) error{ 624 func(entry zapcore.Entry) error { 625 hooksActivated = true 626 assert.Equal(t, entry.Message, "Replication isn't needed") 627 return nil 628 }, 629 }, 630 replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) { 631 ri.replicateIfNeeded(bootstrapBlock) 632 }, 633 }, 634 { 635 name: "Replication is needed, but pulling fails", 636 panicValue: "Failed pulling system channel: " + 637 "failed obtaining the latest block for channel testchannel", 638 shouldConnect: true, 639 systemLedgerHeight: 10, 640 bootBlock: &bootBlock, 641 conf: &localconfig.TopLevel{ 642 General: localconfig.General{ 643 Cluster: clusterConfig, 644 }, 645 }, 646 secOpts: comm.SecureOptions{ 647 Certificate: cert, 648 Key: key, 649 UseTLS: true, 650 ServerRootCAs: [][]byte{caCert}, 651 }, 652 replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) { 653 ri.replicateIfNeeded(bootstrapBlock) 654 }, 655 }, 656 { 657 name: "Explicit replication is requested, but the channel shouldn't be pulled", 658 verificationCount: 10, 659 shouldConnect: true, 660 systemLedgerHeight: 10, 661 bootBlock: &bootBlock, 662 conf: &localconfig.TopLevel{ 663 General: localconfig.General{ 664 Cluster: clusterConfig, 665 }, 666 }, 667 secOpts: comm.SecureOptions{ 668 Certificate: cert, 669 Key: key, 670 UseTLS: true, 671 ServerRootCAs: [][]byte{caCert}, 672 }, 673 replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) { 674 ri.ReplicateChains(bootstrapBlock, []string{"foo"}) 675 }, 676 zapHooks: []func(entry zapcore.Entry) error{ 677 func(entry zapcore.Entry) error { 678 possibleLogs := []string{ 679 "Will now replicate chains [foo]", 680 "Channel testchannel shouldn't be pulled. Skipping it", 681 } 682 for _, possibleLog := range possibleLogs { 683 if entry.Message == possibleLog { 684 hooksActivated = true 685 } 686 } 687 return nil 688 }, 689 }, 690 }, 691 { 692 name: "Explicit replication is requested, but the channel cannot be pulled", 693 panicValue: "Failed pulling system channel: " + 694 "failed obtaining the latest block for channel testchannel", 695 shouldConnect: true, 696 systemLedgerHeight: 10, 697 bootBlock: &bootBlock, 698 conf: &localconfig.TopLevel{ 699 General: localconfig.General{ 700 Cluster: clusterConfig, 701 }, 702 }, 703 secOpts: comm.SecureOptions{ 704 Certificate: cert, 705 Key: key, 706 UseTLS: true, 707 ServerRootCAs: [][]byte{caCert}, 708 }, 709 replicateFunc: func(ri *replicationInitiator, bootstrapBlock *common.Block) { 710 ri.ReplicateChains(bootstrapBlock, []string{"testchannel"}) 711 }, 712 }, 713 } { 714 testCase := testCase 715 t.Run(testCase.name, func(t *testing.T) { 716 deliverServer := prepareTestCase() 717 defer deliverServer.srv.Stop() 718 719 lw := &mocks.LedgerWriter{} 720 lw.On("Height").Return(testCase.systemLedgerHeight).Once() 721 722 lf := &mocks.LedgerFactory{} 723 lf.On("GetOrCreate", mock.Anything).Return(lw, testCase.ledgerFactoryErr).Twice() 724 lf.On("Close") 725 726 verifier := &mocks.BlockVerifier{} 727 verifier.On("VerifyBlockSignature", mock.Anything, mock.Anything).Return(nil) 728 vr := &mocks.VerifierRetriever{} 729 vr.On("RetrieveVerifier", mock.Anything).Return(verifier) 730 731 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 732 assert.NoError(t, err) 733 734 r := &replicationInitiator{ 735 verifierRetriever: vr, 736 lf: lf, 737 logger: flogging.MustGetLogger("testReplicateIfNeeded"), 738 signer: testCase.signer, 739 740 conf: testCase.conf, 741 secOpts: testCase.secOpts, 742 cryptoProvider: cryptoProvider, 743 } 744 745 if testCase.panicValue != "" { 746 assert.PanicsWithValue(t, testCase.panicValue, func() { 747 testCase.replicateFunc(r, testCase.bootBlock) 748 }) 749 return 750 } 751 752 // Else, we are not expected to panic. 753 r.logger = r.logger.WithOptions(zap.Hooks(testCase.zapHooks...)) 754 755 // This is the method we're testing. 756 testCase.replicateFunc(r, testCase.bootBlock) 757 758 // Ensure we ran the hooks for a test case that doesn't panic 759 assert.True(t, hooksActivated) 760 // Restore the flag for the next iteration 761 defer func() { 762 hooksActivated = false 763 }() 764 765 assert.Equal(t, testCase.shouldConnect, atomic.LoadInt32(&deliverServer.isConnected) == int32(1)) 766 verifier.AssertNumberOfCalls(t, "VerifyBlockSignature", testCase.verificationCount) 767 }) 768 } 769 } 770 771 func TestInactiveChainReplicator(t *testing.T) { 772 for _, testCase := range []struct { 773 description string 774 chainsTracked []string 775 ReplicateChainsExpectedInput1 []string 776 ReplicateChainsExpectedInput1Reverse []string 777 ReplicateChainsExpectedInput2 []string 778 ReplicateChainsOutput1 []string 779 ReplicateChainsOutput2 []string 780 chainsExpectedToBeReplicated []string 781 expectedRegisteredChains map[string]struct{} 782 ReplicateChainsExpectedCallCount int 783 genesisBlock *common.Block 784 }{ 785 { 786 description: "no chains tracked", 787 expectedRegisteredChains: map[string]struct{}{}, 788 }, 789 { 790 description: "some chains tracked, but not all succeed replication", 791 chainsTracked: []string{"foo", "bar"}, 792 ReplicateChainsExpectedInput1: []string{"foo", "bar"}, 793 ReplicateChainsExpectedInput1Reverse: []string{"bar", "foo"}, 794 ReplicateChainsExpectedInput2: []string{"bar"}, 795 ReplicateChainsOutput1: []string{"foo"}, 796 ReplicateChainsOutput2: []string{}, 797 chainsExpectedToBeReplicated: []string{"foo"}, 798 ReplicateChainsExpectedCallCount: 2, 799 genesisBlock: &common.Block{}, 800 expectedRegisteredChains: map[string]struct{}{ 801 "foo": {}, 802 "bar": {}, 803 }, 804 }, 805 { 806 description: "some chains tracked, and all succeed replication but on 2nd pass", 807 chainsTracked: []string{"foo", "bar"}, 808 ReplicateChainsExpectedInput1: []string{"foo", "bar"}, 809 ReplicateChainsExpectedInput1Reverse: []string{"bar", "foo"}, 810 ReplicateChainsExpectedInput2: []string{"bar"}, 811 ReplicateChainsOutput1: []string{"foo"}, 812 ReplicateChainsOutput2: []string{"bar"}, 813 chainsExpectedToBeReplicated: []string{"foo", "bar"}, 814 ReplicateChainsExpectedCallCount: 2, 815 genesisBlock: &common.Block{}, 816 expectedRegisteredChains: map[string]struct{}{ 817 "foo": {}, 818 "bar": {}, 819 }, 820 }, 821 } { 822 t.Run(testCase.description, func(t *testing.T) { 823 registeredChains := make(map[string]struct{}) 824 registerChain := func(chain string) { 825 registeredChains[chain] = struct{}{} 826 } 827 scheduler := make(chan time.Time) 828 replicator := &server_mocks.ChainReplicator{} 829 icr := &inactiveChainReplicator{ 830 registerChain: registerChain, 831 logger: flogging.MustGetLogger("test"), 832 replicator: replicator, 833 chains2CreationCallbacks: make(map[string]chainCreation), 834 retrieveLastSysChannelConfigBlock: func() *common.Block { 835 return nil 836 }, 837 quitChan: make(chan struct{}), 838 scheduleChan: scheduler, 839 } 840 841 trackedChains := make(chan string, 10) 842 // Simulate starting of a chain by simply adding it to a channel 843 for _, trackedChain := range testCase.chainsTracked { 844 trackedChain := trackedChain 845 icr.TrackChain(trackedChain, testCase.genesisBlock, func() { 846 trackedChains <- trackedChain 847 }) 848 } 849 850 // First pass 851 input := testCase.ReplicateChainsExpectedInput1 852 output := testCase.ReplicateChainsOutput1 853 replicator.On("ReplicateChains", mock.Anything, input).Return(output).Once() 854 // the input might be called in reverse order 855 input = testCase.ReplicateChainsExpectedInput1Reverse 856 replicator.On("ReplicateChains", mock.Anything, input).Return(output).Once() 857 858 // Second pass 859 input = testCase.ReplicateChainsExpectedInput2 860 output = testCase.ReplicateChainsOutput2 861 replicator.On("ReplicateChains", mock.Anything, input).Return(output).Once() 862 863 var replicatorStopped sync.WaitGroup 864 replicatorStopped.Add(1) 865 go func() { 866 defer replicatorStopped.Done() 867 // trigger to replicate the first time 868 scheduler <- time.Time{} 869 // trigger to replicate a second time 870 scheduler <- time.Time{} 871 icr.stop() 872 }() 873 icr.run() 874 replicatorStopped.Wait() 875 close(trackedChains) 876 877 var replicatedChains []string 878 for chain := range trackedChains { 879 replicatedChains = append(replicatedChains, chain) 880 } 881 assert.Equal(t, testCase.chainsExpectedToBeReplicated, replicatedChains) 882 replicator.AssertNumberOfCalls(t, "ReplicateChains", testCase.ReplicateChainsExpectedCallCount) 883 assert.Equal(t, testCase.expectedRegisteredChains, registeredChains) 884 }) 885 } 886 } 887 888 func TestInactiveChainReplicatorChannels(t *testing.T) { 889 icr := &inactiveChainReplicator{ 890 logger: flogging.MustGetLogger("test"), 891 chains2CreationCallbacks: make(map[string]chainCreation), 892 } 893 icr.TrackChain("foo", &common.Block{}, func() {}) 894 assert.Contains(t, icr.Channels(), cluster.ChannelGenesisBlock{ChannelName: "foo", GenesisBlock: &common.Block{}}) 895 896 icr.TrackChain("bar", nil, func() {}) 897 assert.Contains(t, icr.Channels(), cluster.ChannelGenesisBlock{ChannelName: "bar", GenesisBlock: nil}) 898 899 icr.Close() 900 } 901 902 func injectConsenterCertificate(t *testing.T, block *common.Block, tlsCert []byte) { 903 env, err := protoutil.ExtractEnvelope(block, 0) 904 assert.NoError(t, err) 905 payload, err := protoutil.UnmarshalPayload(env.Payload) 906 assert.NoError(t, err) 907 confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data) 908 assert.NoError(t, err) 909 consensus := confEnv.Config.ChannelGroup.Groups[channelconfig.OrdererGroupKey].Values[channelconfig.ConsensusTypeKey] 910 consensus.Value = protoutil.MarshalOrPanic(&orderer.ConsensusType{ 911 Type: "etcdraft", 912 Metadata: protoutil.MarshalOrPanic(&etcdraft.ConfigMetadata{ 913 Consenters: []*etcdraft.Consenter{ 914 { 915 ServerTlsCert: tlsCert, 916 ClientTlsCert: tlsCert, 917 }, 918 }, 919 }), 920 }) 921 922 payload.Data = protoutil.MarshalOrPanic(confEnv) 923 env.Payload = protoutil.MarshalOrPanic(payload) 924 block.Data.Data[0] = protoutil.MarshalOrPanic(env) 925 block.Header.DataHash = protoutil.BlockDataHash(block.Data) 926 } 927 928 func injectOrdererEndpoint(t *testing.T, block *common.Block, endpoint string) { 929 ordererAddresses := channelconfig.OrdererAddressesValue([]string{endpoint}) 930 // Unwrap the layers until we reach the orderer addresses 931 env, err := protoutil.ExtractEnvelope(block, 0) 932 assert.NoError(t, err) 933 payload, err := protoutil.UnmarshalPayload(env.Payload) 934 assert.NoError(t, err) 935 confEnv, err := configtx.UnmarshalConfigEnvelope(payload.Data) 936 assert.NoError(t, err) 937 // Replace the orderer addresses 938 confEnv.Config.ChannelGroup.Values[ordererAddresses.Key()].Value = protoutil.MarshalOrPanic(ordererAddresses.Value()) 939 // And put it back into the block 940 payload.Data = protoutil.MarshalOrPanic(confEnv) 941 env.Payload = protoutil.MarshalOrPanic(payload) 942 block.Data.Data[0] = protoutil.MarshalOrPanic(env) 943 block.Header.DataHash = protoutil.BlockDataHash(block.Data) 944 } 945 946 func TestVerifierLoader(t *testing.T) { 947 cryptoPath := generateCryptoMaterials(t, cryptogen) 948 defer os.RemoveAll(cryptoPath) 949 950 systemChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "system", "SampleSoloSystemChannel") 951 systemChannelBlockBytes, err := ioutil.ReadFile(systemChannelBlockPath) 952 assert.NoError(t, err) 953 954 configBlock := &common.Block{} 955 assert.NoError(t, proto.Unmarshal(systemChannelBlockBytes, configBlock)) 956 957 verifier := &mocks.BlockVerifier{} 958 959 for _, testCase := range []struct { 960 description string 961 ledgerGetOrCreateErr error 962 ledgerHeight uint64 963 lastBlock *common.Block 964 lastConfigBlock *common.Block 965 verifierFromConfigReturns cluster.BlockVerifier 966 verifierFromConfigErr error 967 onFailureInvoked bool 968 expectedPanic string 969 expectedLoggedMessages map[string]struct{} 970 expectedResult verifiersByChannel 971 }{ 972 { 973 description: "obtaining ledger fails", 974 ledgerGetOrCreateErr: errors.New("IO error"), 975 expectedPanic: "Failed obtaining ledger for channel mychannel", 976 }, 977 { 978 description: "empty ledger", 979 expectedLoggedMessages: map[string]struct{}{ 980 "Channel mychannel has no blocks, skipping it": {}, 981 }, 982 expectedResult: make(verifiersByChannel), 983 }, 984 { 985 description: "block retrieval fails", 986 ledgerHeight: 100, 987 expectedPanic: "Failed retrieving block [99] for channel mychannel", 988 }, 989 { 990 description: "block retrieval succeeds but the block is bad", 991 ledgerHeight: 100, 992 lastBlock: &common.Block{}, 993 expectedPanic: "Failed retrieving config block [99] for channel mychannel", 994 }, 995 { 996 description: "config block retrieved is bad", 997 ledgerHeight: 100, 998 lastBlock: &common.Block{ 999 Metadata: &common.BlockMetadata{ 1000 Metadata: [][]byte{{}, protoutil.MarshalOrPanic(&common.Metadata{ 1001 Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: 21}), 1002 }), {}, {}}, 1003 }, 1004 }, 1005 lastConfigBlock: &common.Block{Header: &common.BlockHeader{Number: 21}}, 1006 expectedPanic: "Failed extracting configuration for channel mychannel from block [21]: empty block", 1007 onFailureInvoked: true, 1008 }, 1009 { 1010 description: "VerifierFromConfig fails", 1011 ledgerHeight: 100, 1012 lastBlock: &common.Block{ 1013 Metadata: &common.BlockMetadata{ 1014 Metadata: [][]byte{{}, protoutil.MarshalOrPanic(&common.Metadata{ 1015 Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: 21}), 1016 }), {}, {}}, 1017 }, 1018 }, 1019 lastConfigBlock: configBlock, 1020 verifierFromConfigErr: errors.New("failed initializing MSP"), 1021 expectedPanic: "Failed creating verifier for channel mychannel from block [99]: failed initializing MSP", 1022 onFailureInvoked: true, 1023 }, 1024 { 1025 description: "VerifierFromConfig succeeds", 1026 ledgerHeight: 100, 1027 lastBlock: &common.Block{ 1028 Metadata: &common.BlockMetadata{ 1029 Metadata: [][]byte{{}, protoutil.MarshalOrPanic(&common.Metadata{ 1030 Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: 21}), 1031 }), {}, {}}, 1032 }, 1033 }, 1034 lastConfigBlock: configBlock, 1035 expectedLoggedMessages: map[string]struct{}{ 1036 "Loaded verifier for channel mychannel from config block at index 99": {}, 1037 }, 1038 expectedResult: verifiersByChannel{ 1039 "mychannel": verifier, 1040 }, 1041 }, 1042 } { 1043 t.Run(testCase.description, func(t *testing.T) { 1044 iterator := &deliver_mocks.BlockIterator{} 1045 iterator.NextReturnsOnCall(0, testCase.lastBlock, common.Status_SUCCESS) 1046 iterator.NextReturnsOnCall(1, testCase.lastConfigBlock, common.Status_SUCCESS) 1047 1048 ledger := &ledger_mocks.ReadWriter{} 1049 ledger.On("Height").Return(testCase.ledgerHeight) 1050 ledger.On("Iterator", mock.Anything).Return(iterator, uint64(1)) 1051 1052 ledgerFactory := &server_mocks.Factory{} 1053 ledgerFactory.On("GetOrCreate", "mychannel").Return(ledger, testCase.ledgerGetOrCreateErr) 1054 ledgerFactory.On("ChannelIDs").Return([]string{"mychannel"}) 1055 1056 verifierFactory := &mocks.VerifierFactory{} 1057 verifierFactory.On("VerifierFromConfig", mock.Anything, "mychannel").Return(verifier, testCase.verifierFromConfigErr) 1058 1059 var onFailureInvoked bool 1060 onFailure := func(_ *common.Block) { 1061 onFailureInvoked = true 1062 } 1063 1064 verifierLoader := &verifierLoader{ 1065 onFailure: onFailure, 1066 ledgerFactory: ledgerFactory, 1067 verifierFactory: verifierFactory, 1068 logger: flogging.MustGetLogger("test"), 1069 } 1070 1071 verifierLoader.logger = verifierLoader.logger.WithOptions(zap.Hooks(func(entry zapcore.Entry) error { 1072 delete(testCase.expectedLoggedMessages, entry.Message) 1073 return nil 1074 })) 1075 1076 if testCase.expectedPanic != "" { 1077 f := func() { 1078 verifierLoader.loadVerifiers() 1079 } 1080 assert.PanicsWithValue(t, testCase.expectedPanic, f) 1081 } else { 1082 res := verifierLoader.loadVerifiers() 1083 assert.Equal(t, testCase.expectedResult, res) 1084 } 1085 1086 assert.Equal(t, testCase.onFailureInvoked, onFailureInvoked) 1087 assert.Empty(t, testCase.expectedLoggedMessages) 1088 }) 1089 } 1090 } 1091 1092 func TestValidateBootstrapBlock(t *testing.T) { 1093 cryptoPath := generateCryptoMaterials(t, cryptogen) 1094 defer os.RemoveAll(cryptoPath) 1095 1096 systemChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "system", "SampleSoloSystemChannel") 1097 systemChannelBlockBytes, err := ioutil.ReadFile(systemChannelBlockPath) 1098 assert.NoError(t, err) 1099 1100 applicationChannelBlockPath := generateBootstrapBlock(t, tempDir, configtxgen, "mychannel", "SampleOrgChannel") 1101 applicationChannelBlockBytes, err := ioutil.ReadFile(applicationChannelBlockPath) 1102 assert.NoError(t, err) 1103 1104 appBlock := &common.Block{} 1105 assert.NoError(t, proto.Unmarshal(applicationChannelBlockBytes, appBlock)) 1106 1107 systemBlock := &common.Block{} 1108 assert.NoError(t, proto.Unmarshal(systemChannelBlockBytes, systemBlock)) 1109 1110 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 1111 assert.NoError(t, err) 1112 1113 for _, testCase := range []struct { 1114 description string 1115 block *common.Block 1116 expectedError string 1117 }{ 1118 { 1119 description: "nil block", 1120 expectedError: "nil block", 1121 }, 1122 { 1123 description: "empty block", 1124 block: &common.Block{}, 1125 expectedError: "empty block data", 1126 }, 1127 { 1128 description: "bad envelope", 1129 block: &common.Block{ 1130 Data: &common.BlockData{ 1131 Data: [][]byte{{1, 2, 3}}, 1132 }, 1133 }, 1134 expectedError: "failed extracting envelope from block: " + 1135 "proto: common.Envelope: illegal tag 0 (wire type 1)", 1136 }, 1137 { 1138 description: "application channel block", 1139 block: appBlock, 1140 expectedError: "the block isn't a system channel block because it lacks ConsortiumsConfig", 1141 }, 1142 { 1143 description: "system channel block", 1144 block: systemBlock, 1145 }, 1146 } { 1147 t.Run(testCase.description, func(t *testing.T) { 1148 err := ValidateBootstrapBlock(testCase.block, cryptoProvider) 1149 if testCase.expectedError == "" { 1150 assert.NoError(t, err) 1151 return 1152 } 1153 1154 assert.EqualError(t, err, testCase.expectedError) 1155 }) 1156 } 1157 } 1158 1159 func generateBootstrapBlock(t *testing.T, tempDir, configtxgen, channel, profile string) string { 1160 gt := NewGomegaWithT(t) 1161 // create a genesis block for the specified channel and profile 1162 genesisBlockPath := filepath.Join(tempDir, channel+".block") 1163 cmd := exec.Command( 1164 configtxgen, 1165 "-channelID", channel, 1166 "-profile", profile, 1167 "-outputBlock", genesisBlockPath, 1168 "--configPath", tempDir, 1169 ) 1170 configtxgenProcess, err := gexec.Start(cmd, nil, nil) 1171 gt.Expect(err).NotTo(HaveOccurred()) 1172 gt.Eventually(configtxgenProcess, time.Minute).Should(gexec.Exit(0)) 1173 gt.Expect(configtxgenProcess.Err).To(gbytes.Say("Writing genesis block")) 1174 1175 return genesisBlockPath 1176 }