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