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