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