github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/common/multichannel/registrar_test.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package multichannel 8 9 import ( 10 "bytes" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "path" 15 "path/filepath" 16 "testing" 17 "time" 18 19 "github.com/golang/protobuf/proto" 20 "github.com/hechain20/hechain/bccsp" 21 "github.com/hechain20/hechain/bccsp/sw" 22 "github.com/hechain20/hechain/common/channelconfig" 23 "github.com/hechain20/hechain/common/crypto/tlsgen" 24 "github.com/hechain20/hechain/common/ledger/blockledger" 25 "github.com/hechain20/hechain/common/ledger/blockledger/fileledger" 26 "github.com/hechain20/hechain/common/metrics/disabled" 27 "github.com/hechain20/hechain/common/policies" 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/blockcutter" 34 "github.com/hechain20/hechain/orderer/common/cluster" 35 "github.com/hechain20/hechain/orderer/common/localconfig" 36 "github.com/hechain20/hechain/orderer/common/multichannel/mocks" 37 "github.com/hechain20/hechain/orderer/common/types" 38 "github.com/hechain20/hechain/orderer/consensus" 39 "github.com/hechain20/hechain/orderer/consensus/etcdraft" 40 "github.com/hechain20/hechain/protoutil" 41 cb "github.com/hyperledger/fabric-protos-go/common" 42 ab "github.com/hyperledger/fabric-protos-go/orderer" 43 "github.com/pkg/errors" 44 "github.com/stretchr/testify/assert" 45 "github.com/stretchr/testify/require" 46 ) 47 48 //go:generate counterfeiter -o mocks/resources.go --fake-name Resources . resources 49 50 type resources interface { 51 channelconfig.Resources 52 } 53 54 //go:generate counterfeiter -o mocks/orderer_config.go --fake-name OrdererConfig . ordererConfig 55 56 type ordererConfig interface { 57 channelconfig.Orderer 58 } 59 60 //go:generate counterfeiter -o mocks/orderer_capabilities.go --fake-name OrdererCapabilities . ordererCapabilities 61 62 type ordererCapabilities interface { 63 channelconfig.OrdererCapabilities 64 } 65 66 //go:generate counterfeiter -o mocks/channel_config.go --fake-name ChannelConfig . channelConfig 67 68 type channelConfig interface { 69 channelconfig.Channel 70 } 71 72 //go:generate counterfeiter -o mocks/channel_capabilities.go --fake-name ChannelCapabilities . channelCapabilities 73 74 type channelCapabilities interface { 75 channelconfig.ChannelCapabilities 76 } 77 78 //go:generate counterfeiter -o mocks/signer_serializer.go --fake-name SignerSerializer . signerSerializer 79 80 type signerSerializer interface { 81 identity.SignerSerializer 82 } 83 84 //go:generate counterfeiter -o mocks/consenter.go --fake-name Consenter . consenter 85 type consenter interface { 86 consensus.Consenter 87 consensus.ClusterConsenter 88 } 89 90 func mockCrypto() *mocks.SignerSerializer { 91 return &mocks.SignerSerializer{} 92 } 93 94 func newFactory(dir string) blockledger.Factory { 95 rlf, err := fileledger.New(dir, &disabled.Provider{}) 96 if err != nil { 97 panic(err) 98 } 99 100 return rlf 101 } 102 103 func newLedgerAndFactory(dir string, chainID string, genesisBlockSys *cb.Block) (blockledger.Factory, blockledger.ReadWriter) { 104 rlf := newFactory(dir) 105 rl := newLedger(rlf, chainID, genesisBlockSys) 106 return rlf, rl 107 } 108 109 func newLedger(rlf blockledger.Factory, chainID string, genesisBlockSys *cb.Block) blockledger.ReadWriter { 110 rl, err := rlf.GetOrCreate(chainID) 111 if err != nil { 112 panic(err) 113 } 114 115 if genesisBlockSys != nil { 116 err = rl.Append(genesisBlockSys) 117 if err != nil { 118 panic(err) 119 } 120 } 121 return rl 122 } 123 124 func testMessageOrderAndRetrieval(maxMessageCount uint32, chainID string, chainSupport *ChainSupport, lr blockledger.ReadWriter, t *testing.T) { 125 messages := make([]*cb.Envelope, maxMessageCount) 126 for i := uint32(0); i < maxMessageCount; i++ { 127 messages[i] = makeNormalTx(chainID, int(i)) 128 } 129 for _, message := range messages { 130 chainSupport.Order(message, 0) 131 } 132 it, _ := lr.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 133 defer it.Close() 134 block, status := it.Next() 135 require.Equal(t, cb.Status_SUCCESS, status, "Could not retrieve block") 136 for i := uint32(0); i < maxMessageCount; i++ { 137 require.True(t, proto.Equal(messages[i], protoutil.ExtractEnvelopeOrPanic(block, int(i))), "Block contents wrong at index %d", i) 138 } 139 } 140 141 func TestConfigTx(t *testing.T) { 142 // system channel 143 confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 144 genesisBlockSys := encoder.New(confSys).GenesisBlock() 145 146 // Tests for a normal channel which contains 3 config transactions and other 147 // normal transactions to make sure the right one returned 148 t.Run("GetConfigTx - ok", func(t *testing.T) { 149 tmpdir, err := ioutil.TempDir("", "registrar_test-") 150 require.NoError(t, err) 151 defer os.RemoveAll(tmpdir) 152 153 _, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 154 for i := 0; i < 5; i++ { 155 rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx("testchannelid", i)})) 156 } 157 rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{makeConfigTx("testchannelid", 5)})) 158 ctx := makeConfigTx("testchannelid", 6) 159 rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{ctx})) 160 161 // block with LAST_CONFIG metadata in SIGNATURES field 162 block := blockledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx("testchannelid", 7)}) 163 blockSignatureValue := protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{ 164 LastConfig: &cb.LastConfig{Index: 7}, 165 }) 166 block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{Value: blockSignatureValue}) 167 rl.Append(block) 168 169 pctx := configTx(rl) 170 require.True(t, proto.Equal(pctx, ctx), "Did not select most recent config transaction") 171 }) 172 } 173 174 func TestNewRegistrar(t *testing.T) { 175 // system channel 176 confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 177 genesisBlockSys := encoder.New(confSys).GenesisBlock() 178 179 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 180 require.NoError(t, err) 181 182 // This test checks to make sure the orderer can come up if it cannot find any chains 183 t.Run("No chains", func(t *testing.T) { 184 tmpdir, err := ioutil.TempDir("", "registrar_test-") 185 require.NoError(t, err) 186 defer os.RemoveAll(tmpdir) 187 188 lf, err := fileledger.New(tmpdir, &disabled.Provider{}) 189 require.NoError(t, err) 190 191 consenters := map[string]consensus.Consenter{"etcdraft": &mocks.Consenter{}} 192 193 var manager *Registrar 194 require.NotPanics(t, func() { 195 manager = NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 196 manager.Initialize(consenters) 197 }, "Should not panic when starting without a system channel") 198 require.NotNil(t, manager) 199 list := manager.ChannelList() 200 require.Equal(t, types.ChannelList{}, list) 201 info, err := manager.ChannelInfo("my-channel") 202 require.EqualError(t, err, types.ErrChannelNotExist.Error()) 203 require.Equal(t, types.ChannelInfo{}, info) 204 }) 205 206 // This test checks to make sure that the orderer refuses to come up if there are multiple system channels 207 t.Run("Multiple system chains - failure", func(t *testing.T) { 208 tmpdir, err := ioutil.TempDir("", "registrar_test-") 209 require.NoError(t, err) 210 defer os.RemoveAll(tmpdir) 211 212 lf, err := fileledger.New(tmpdir, &disabled.Provider{}) 213 require.NoError(t, err) 214 215 for _, id := range []string{"foo", "bar"} { 216 rl, err := lf.GetOrCreate(id) 217 require.NoError(t, err) 218 219 err = rl.Append(encoder.New(confSys).GenesisBlockForChannel(id)) 220 require.NoError(t, err) 221 } 222 223 consenter := &mocks.Consenter{} 224 consenter.HandleChainCalls(handleChain) 225 consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter} 226 227 require.Panics(t, func() { 228 NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil).Initialize(consenters) 229 }, "Two system channels should have caused panic") 230 }) 231 232 // This test essentially brings the entire system up and is ultimately what main.go will replicate 233 t.Run("Correct flow with system channel", func(t *testing.T) { 234 tmpdir, err := ioutil.TempDir("", "registrar_test-") 235 require.NoError(t, err) 236 defer os.RemoveAll(tmpdir) 237 238 lf, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 239 240 consenter := &mocks.Consenter{} 241 consenter.HandleChainCalls(handleChain) 242 consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter} 243 244 manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 245 manager.Initialize(consenters) 246 247 chainSupport := manager.GetChain("Fake") 248 require.Nilf(t, chainSupport, "Should not have found a chain that was not created") 249 250 chainSupport = manager.GetChain("testchannelid") 251 require.NotNilf(t, chainSupport, "Should have gotten chain which was initialized by ledger") 252 253 list := manager.ChannelList() 254 require.NotNil(t, list.SystemChannel) 255 256 require.Equal( 257 t, 258 types.ChannelList{ 259 SystemChannel: &types.ChannelInfoShort{Name: "testchannelid", URL: ""}, 260 Channels: nil, 261 }, 262 list, 263 ) 264 265 info, err := manager.ChannelInfo("testchannelid") 266 require.NoError(t, err) 267 require.Equal(t, 268 types.ChannelInfo{Name: "testchannelid", URL: "", ConsensusRelation: "other", Status: "active", Height: 1}, 269 info, 270 ) 271 272 testMessageOrderAndRetrieval(confSys.Orderer.BatchSize.MaxMessageCount, "testchannelid", chainSupport, rl, t) 273 }) 274 } 275 276 func TestRegistrar_Initialize(t *testing.T) { 277 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 278 require.NoError(t, err) 279 280 tlsCA, err := tlsgen.NewCA() 281 require.NoError(t, err) 282 283 dialer := &cluster.PredicateDialer{ 284 Config: comm.ClientConfig{ 285 SecOpts: comm.SecureOptions{ 286 Certificate: tlsCA.CertBytes(), 287 }, 288 }, 289 } 290 291 config := localconfig.TopLevel{ 292 General: localconfig.General{ 293 BootstrapMethod: "none", 294 GenesisFile: "", 295 Cluster: localconfig.Cluster{ 296 ReplicationBufferSize: 1, 297 ReplicationPullTimeout: time.Microsecond, 298 ReplicationRetryTimeout: time.Microsecond, 299 ReplicationMaxRetries: 2, 300 }, 301 }, 302 ChannelParticipation: localconfig.ChannelParticipation{ 303 Enabled: true, 304 }, 305 } 306 307 confAppRaft := genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir()) 308 confAppRaft.Consortiums = nil 309 confAppRaft.Consortium = "" 310 certDir, err := ioutil.TempDir("", "registrar_test-") 311 require.NoError(t, err) 312 defer os.RemoveAll(certDir) 313 generateCertificates(t, confAppRaft, tlsCA, certDir) 314 bootstrapper, err := encoder.NewBootstrapper(confAppRaft) 315 require.NoError(t, err, "cannot create bootstrapper") 316 genesisBlockAppRaft := bootstrapper.GenesisBlockForChannel("my-raft-channel") 317 require.NotNil(t, genesisBlockAppRaft) 318 319 confSysRaft := genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir()) 320 confSysRaft.Orderer.EtcdRaft.Consenters = confAppRaft.Orderer.EtcdRaft.Consenters 321 bootstrapper, err = encoder.NewBootstrapper(confSysRaft) 322 require.NoError(t, err, "cannot create bootstrapper") 323 genesisBlockSysRaft := bootstrapper.GenesisBlockForChannel("my-sys-channel") 324 require.NotNil(t, genesisBlockSysRaft) 325 326 consenter := &mocks.Consenter{} 327 consenter.HandleChainCalls(handleChainCluster) 328 consenters := map[string]consensus.Consenter{confAppRaft.Orderer.OrdererType: consenter} 329 330 // This test essentially brings the entire system up and is ultimately what main.go will replicate 331 t.Run("Correct flow with system channel - etcdraft.Chain", func(t *testing.T) { 332 tmpdir, err := ioutil.TempDir("", "registrar_test-") 333 require.NoError(t, err) 334 defer os.RemoveAll(tmpdir) 335 336 lf, _ := newLedgerAndFactory(tmpdir, "my-sys-channel", genesisBlockSysRaft) 337 338 manager := NewRegistrar(config, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 339 manager.Initialize(consenters) 340 341 chainSupport := manager.GetChain("Fake") 342 require.Nilf(t, chainSupport, "Should not have found a chain that was not created") 343 344 chainSupport = manager.GetChain("my-sys-channel") 345 require.NotNilf(t, chainSupport, "Should have gotten chain which was initialized by ledger") 346 347 list := manager.ChannelList() 348 require.NotNil(t, list.SystemChannel) 349 350 require.Equal(t, 351 types.ChannelList{ 352 SystemChannel: &types.ChannelInfoShort{Name: "my-sys-channel", URL: ""}, 353 Channels: nil, 354 }, 355 list, 356 ) 357 358 info, err := manager.ChannelInfo("my-sys-channel") 359 require.NoError(t, err) 360 require.Equal(t, 361 types.ChannelInfo{Name: "my-sys-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1}, 362 info, 363 ) 364 }) 365 366 t.Run("Correct flow without system channel - etcdraft.Chain", func(t *testing.T) { 367 // TODO 368 tmpdir, err := ioutil.TempDir("", "registrar_test-") 369 require.NoError(t, err) 370 defer os.RemoveAll(tmpdir) 371 372 lf, _ := newLedgerAndFactory(tmpdir, "my-raft-channel", genesisBlockAppRaft) 373 374 consenter.IsChannelMemberReturns(true, nil) 375 376 manager := NewRegistrar(config, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 377 manager.Initialize(consenters) 378 379 chainSupport := manager.GetChain("not-there") 380 require.Nilf(t, chainSupport, "Should not have found a chain that was not created") 381 382 chainSupport = manager.GetChain("my-raft-channel") 383 require.NotNilf(t, chainSupport, "Should have gotten chain which was initialized by ledger") 384 385 list := manager.ChannelList() 386 require.Nil(t, list.SystemChannel) 387 388 require.Equal( 389 t, 390 types.ChannelList{ 391 SystemChannel: nil, 392 Channels: []types.ChannelInfoShort{{Name: "my-raft-channel", URL: ""}}, 393 }, 394 list, 395 ) 396 397 info, err := manager.ChannelInfo("my-raft-channel") 398 require.NoError(t, err) 399 require.Equal(t, 400 types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1}, 401 info, 402 ) 403 }) 404 405 t.Run("Correct flow without system channel - follower.Chain", func(t *testing.T) { 406 // TODO 407 tmpdir, err := ioutil.TempDir("", "registrar_test-") 408 require.NoError(t, err) 409 defer os.RemoveAll(tmpdir) 410 411 lf, _ := newLedgerAndFactory(tmpdir, "my-raft-channel", genesisBlockAppRaft) 412 413 consenter.IsChannelMemberReturns(false, nil) 414 415 manager := NewRegistrar(config, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 416 manager.Initialize(consenters) 417 418 fChain := manager.GetFollower("not-there") 419 require.Nil(t, fChain, "Should not have found a follower that was not created") 420 421 list := manager.ChannelList() 422 require.Nil(t, list.SystemChannel) 423 424 require.Equal( 425 t, 426 types.ChannelList{ 427 SystemChannel: nil, 428 Channels: []types.ChannelInfoShort{{Name: "my-raft-channel", URL: ""}}, 429 }, 430 list, 431 ) 432 433 info, err := manager.ChannelInfo("my-raft-channel") 434 require.NoError(t, err) 435 require.Equal(t, 436 types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "active", Height: 1}, 437 info, 438 ) 439 440 fChain = manager.GetFollower("my-raft-channel") 441 require.NotNil(t, fChain, "Should have gotten follower which was initialized by ledger") 442 fChain.Halt() 443 }) 444 445 t.Run("Correct flow without system channel - follower.Chain with join block", func(t *testing.T) { 446 // TODO 447 tmpdir, err := ioutil.TempDir("", "registrar_test-") 448 require.NoError(t, err) 449 defer os.RemoveAll(tmpdir) 450 451 config.FileLedger = localconfig.FileLedger{Location: tmpdir} 452 453 lf, _ := newLedgerAndFactory(tmpdir, "my-raft-channel", genesisBlockAppRaft) 454 455 consenter.IsChannelMemberReturns(false, nil) 456 457 manager := NewRegistrar(config, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 458 459 // Emulate saved join-block from before the restart 460 joinBlockAppRaft := protoutil.UnmarshalBlockOrPanic(protoutil.MarshalOrPanic(genesisBlockAppRaft)) 461 joinBlockAppRaft.Header.Number = 10 462 manager.joinBlockFileRepo.Save("my-raft-channel", protoutil.MarshalOrPanic(joinBlockAppRaft)) 463 464 manager.Initialize(consenters) 465 466 fChain := manager.GetFollower("not-there") 467 require.Nil(t, fChain, "Should not have found a follower that was not created") 468 469 list := manager.ChannelList() 470 require.Nil(t, list.SystemChannel) 471 472 require.Equal(t, 473 types.ChannelList{ 474 SystemChannel: nil, 475 Channels: []types.ChannelInfoShort{{Name: "my-raft-channel", URL: ""}}, 476 }, 477 list, 478 ) 479 480 info, err := manager.ChannelInfo("my-raft-channel") 481 require.NoError(t, err) 482 require.Equal(t, 483 types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "onboarding", Height: 1}, 484 info, 485 ) 486 487 fChain = manager.GetFollower("my-raft-channel") 488 require.NotNil(t, fChain, "Should have gotten follower which was initialized by ledger") 489 fChain.Halt() 490 }) 491 } 492 493 func TestNewRegistrarWithFileRepo(t *testing.T) { 494 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 495 require.NoError(t, err) 496 497 consenter := &mocks.Consenter{} 498 consenter.HandleChainCalls(handleChain) 499 consenter.IsChannelMemberReturns(true, nil) 500 consenters := map[string]consensus.Consenter{"etcdraft": consenter} 501 502 t.Run("Correct flow with valid file repo dir, one existing channel, two joinblocks", func(t *testing.T) { 503 tmpdir, err := ioutil.TempDir("", "registrar_test-") 504 require.NoError(t, err) 505 defer os.RemoveAll(tmpdir) 506 507 tlsCA, err := tlsgen.NewCA() 508 require.NoError(t, err) 509 510 confAppRaft := genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir()) 511 confAppRaft.Consortiums = nil 512 confAppRaft.Consortium = "" 513 514 generateCertificates(t, confAppRaft, tlsCA, tmpdir) 515 516 bootstrapper, err := encoder.NewBootstrapper(confAppRaft) 517 require.NoError(t, err, "cannot create bootstrapper") 518 519 genesisBlockAppRaft := bootstrapper.GenesisBlockForChannel("my-cft-channel") 520 require.NotNil(t, genesisBlockAppRaft) 521 genesisBlockAppRaft2 := bootstrapper.GenesisBlockForChannel("my-other-cft-channel") 522 require.NotNil(t, genesisBlockAppRaft2) 523 524 // create joinblock file repo directory with two existing joinblocks 525 createJoinBlockFileRepoDirWithBlocks( 526 t, 527 tmpdir, 528 &joinBlock{ 529 channel: "my-cft-channel", 530 block: genesisBlockAppRaft, 531 }, 532 &joinBlock{ 533 channel: "my-other-cft-channel", 534 block: genesisBlockAppRaft2, 535 }, 536 ) 537 538 // create one existing channel 539 genesisBlockAppExisting := bootstrapper.GenesisBlockForChannel("my-existing-channel") 540 require.NotNil(t, genesisBlockAppExisting) 541 lf, _ := newLedgerAndFactory(tmpdir, "my-existing-channel", genesisBlockAppExisting) 542 require.NoError(t, err) 543 544 // create the ledger for one of the channels with a joinblock in the file repo 545 _, err = lf.GetOrCreate("my-other-cft-channel") 546 require.NoError(t, err) 547 548 config := localconfig.TopLevel{ 549 ChannelParticipation: localconfig.ChannelParticipation{Enabled: true}, 550 FileLedger: localconfig.FileLedger{Location: tmpdir}, 551 } 552 var manager *Registrar 553 require.NotPanics(t, func() { 554 manager = NewRegistrar(config, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 555 manager.Initialize(consenters) 556 }, "Should not panic when file repo dir exists and is read writable") 557 require.NotNil(t, manager) 558 require.NotNil(t, manager.joinBlockFileRepo) 559 require.DirExists(t, filepath.Join(tmpdir, "pendingops")) 560 561 list := manager.ChannelList() 562 require.Nil(t, list.SystemChannel) 563 require.ElementsMatch( 564 t, 565 []types.ChannelInfoShort{ 566 {Name: "my-cft-channel", URL: ""}, 567 {Name: "my-other-cft-channel", URL: ""}, 568 {Name: "my-existing-channel", URL: ""}, 569 }, 570 list.Channels, 571 ) 572 }) 573 } 574 575 type joinBlock struct { 576 channel string 577 block *cb.Block 578 } 579 580 func createJoinBlockFileRepoDirWithBlocks(t *testing.T, tmpdir string, joinBlocks ...*joinBlock) { 581 joinBlockRepoPath := filepath.Join(tmpdir, "pendingops", "join") 582 err := os.MkdirAll(joinBlockRepoPath, 0o755) 583 require.NoError(t, err) 584 for _, jb := range joinBlocks { 585 blockBytes, err := proto.Marshal(jb.block) 586 require.NoError(t, err) 587 err = ioutil.WriteFile(filepath.Join(joinBlockRepoPath, fmt.Sprintf("%s.join", jb.channel)), blockBytes, 0o600) 588 require.NoError(t, err) 589 } 590 } 591 592 func TestCreateChain(t *testing.T) { 593 // system channel 594 confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 595 genesisBlockSys := encoder.New(confSys).GenesisBlock() 596 597 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 598 require.NoError(t, err) 599 600 t.Run("Create chain", func(t *testing.T) { 601 tmpdir, err := ioutil.TempDir("", "registrar_test-") 602 require.NoError(t, err) 603 defer os.RemoveAll(tmpdir) 604 605 lf, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 606 607 consenter := &mocks.Consenter{} 608 consenter.HandleChainCalls(handleChainCluster) 609 consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter} 610 611 manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 612 manager.Initialize(consenters) 613 614 ledger, err := lf.GetOrCreate("mychannel") 615 require.NoError(t, err) 616 617 genesisBlock := encoder.New(confSys).GenesisBlockForChannel("mychannel") 618 ledger.Append(genesisBlock) 619 620 // Before creating the chain, it doesn't exist 621 require.Nil(t, manager.GetChain("mychannel")) 622 // After creating the chain, it exists 623 manager.CreateChain("mychannel") 624 chain := manager.GetChain("mychannel") 625 require.NotNil(t, chain) 626 627 list := manager.ChannelList() 628 require.Equal( 629 t, 630 types.ChannelList{ 631 SystemChannel: &types.ChannelInfoShort{Name: "testchannelid", URL: ""}, 632 Channels: []types.ChannelInfoShort{{Name: "mychannel", URL: ""}}, 633 }, 634 list, 635 ) 636 637 info, err := manager.ChannelInfo("testchannelid") 638 require.NoError(t, err) 639 require.Equal(t, 640 types.ChannelInfo{Name: "testchannelid", URL: "", ConsensusRelation: types.ConsensusRelationConsenter, Status: types.StatusActive, Height: 1}, 641 info, 642 ) 643 644 info, err = manager.ChannelInfo("mychannel") 645 require.NoError(t, err) 646 require.Equal(t, 647 types.ChannelInfo{Name: "mychannel", URL: "", ConsensusRelation: types.ConsensusRelationConsenter, Status: types.StatusActive, Height: 1}, 648 info, 649 ) 650 651 // A subsequent creation, replaces the chain. 652 manager.CreateChain("mychannel") 653 chain2 := manager.GetChain("mychannel") 654 require.NotNil(t, chain2) 655 // They are not the same 656 require.NotEqual(t, chain, chain2) 657 // The old chain is halted 658 _, ok := <-chain.Chain.(*mockChainCluster).queue 659 require.False(t, ok) 660 661 // The new chain is not halted: Close the channel to prove that. 662 close(chain2.Chain.(*mockChainCluster).queue) 663 }) 664 665 t.Run("chain of type etcdraft.Chain is already created", func(t *testing.T) { 666 tmpdir, err := ioutil.TempDir("", "registrar_test-") 667 require.NoError(t, err) 668 defer os.RemoveAll(tmpdir) 669 670 lf, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 671 672 consenter := &mocks.Consenter{} 673 consenter.HandleChainCalls(handleChain) 674 consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter} 675 676 manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 677 manager.Initialize(consenters) 678 679 testChainSupport := &ChainSupport{Chain: &etcdraft.Chain{}} 680 manager.chains["test"] = testChainSupport 681 682 orglessChannelConf := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile, configtest.GetDevConfigDir()) 683 envConfigUpdate, err := encoder.MakeChannelCreationTransaction("test", mockCrypto(), orglessChannelConf) 684 require.NoError(t, err, "Constructing chain creation tx") 685 686 manager.newChain(envConfigUpdate) 687 688 testChainSupport2 := manager.GetChain("test") 689 require.NotNil(t, testChainSupport2) 690 691 assert.Same(t, testChainSupport, testChainSupport2) 692 }) 693 694 // This test brings up the entire system, with the mock consenter, including the broadcasters etc. and creates a new chain 695 t.Run("New chain", func(t *testing.T) { 696 expectedLastConfigSeq := uint64(1) 697 newChainID := "test-new-chain" 698 699 tmpdir, err := ioutil.TempDir("", "registrar_test-") 700 require.NoError(t, err) 701 defer os.RemoveAll(tmpdir) 702 703 lf, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 704 705 consenter := &mocks.Consenter{} 706 consenter.HandleChainCalls(handleChain) 707 consenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter} 708 709 manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 710 manager.Initialize(consenters) 711 orglessChannelConf := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile, configtest.GetDevConfigDir()) 712 orglessChannelConf.Application.Organizations = nil 713 envConfigUpdate, err := encoder.MakeChannelCreationTransaction(newChainID, mockCrypto(), orglessChannelConf) 714 require.NoError(t, err, "Constructing chain creation tx") 715 716 res, err := manager.NewChannelConfig(envConfigUpdate) 717 require.NoError(t, err, "Constructing initial channel config") 718 719 configEnv, err := res.ConfigtxValidator().ProposeConfigUpdate(envConfigUpdate) 720 require.NoError(t, err, "Proposing initial update") 721 require.Equal(t, expectedLastConfigSeq, configEnv.GetConfig().Sequence, "Sequence of config envelope for new channel should always be set to %d", expectedLastConfigSeq) 722 723 ingressTx, err := protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, newChainID, mockCrypto(), configEnv, msgVersion, epoch) 724 require.NoError(t, err, "Creating ingresstx") 725 726 wrapped := wrapConfigTx(ingressTx) 727 728 chainSupport := manager.GetChain(manager.SystemChannelID()) 729 require.NotNilf(t, chainSupport, "Could not find system channel") 730 731 chainSupport.Configure(wrapped, 0) 732 func() { 733 it, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 734 defer it.Close() 735 block, status := it.Next() 736 if status != cb.Status_SUCCESS { 737 t.Fatalf("Could not retrieve block") 738 } 739 if len(block.Data.Data) != 1 { 740 t.Fatalf("Should have had only one message in the orderer transaction block") 741 } 742 743 require.True(t, proto.Equal(wrapped, protoutil.UnmarshalEnvelopeOrPanic(block.Data.Data[0])), "Orderer config block contains wrong transaction") 744 }() 745 746 chainSupport = manager.GetChain(newChainID) 747 if chainSupport == nil { 748 t.Fatalf("Should have gotten new chain which was created") 749 } 750 751 messages := make([]*cb.Envelope, confSys.Orderer.BatchSize.MaxMessageCount) 752 for i := 0; i < int(confSys.Orderer.BatchSize.MaxMessageCount); i++ { 753 messages[i] = makeNormalTx(newChainID, i) 754 } 755 756 for _, message := range messages { 757 chainSupport.Order(message, 0) 758 } 759 760 it, _ := chainSupport.Reader().Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 0}}}) 761 defer it.Close() 762 block, status := it.Next() 763 if status != cb.Status_SUCCESS { 764 t.Fatalf("Could not retrieve new chain genesis block") 765 } 766 if len(block.Data.Data) != 1 { 767 t.Fatalf("Should have had only one message in the new genesis block") 768 } 769 770 require.True(t, proto.Equal(ingressTx, protoutil.UnmarshalEnvelopeOrPanic(block.Data.Data[0])), "Genesis block contains wrong transaction") 771 772 block, status = it.Next() 773 if status != cb.Status_SUCCESS { 774 t.Fatalf("Could not retrieve block on new chain") 775 } 776 for i := 0; i < int(confSys.Orderer.BatchSize.MaxMessageCount); i++ { 777 if !proto.Equal(protoutil.ExtractEnvelopeOrPanic(block, i), messages[i]) { 778 t.Errorf("Block contents wrong at index %d in new chain", i) 779 } 780 } 781 782 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 783 require.NoError(t, err) 784 rcs, err := newChainSupport(manager, chainSupport.ledgerResources, consenters, mockCrypto(), blockcutter.NewMetrics(&disabled.Provider{}), cryptoProvider) 785 require.NoError(t, err) 786 require.Equal(t, expectedLastConfigSeq, rcs.lastConfigSeq, "On restart, incorrect lastConfigSeq") 787 }) 788 } 789 790 func TestResourcesCheck(t *testing.T) { 791 mockOrderer := &mocks.OrdererConfig{} 792 mockOrdererCaps := &mocks.OrdererCapabilities{} 793 mockOrderer.CapabilitiesReturns(mockOrdererCaps) 794 mockChannel := &mocks.ChannelConfig{} 795 mockChannelCaps := &mocks.ChannelCapabilities{} 796 mockChannel.CapabilitiesReturns(mockChannelCaps) 797 798 mockResources := &mocks.Resources{} 799 mockResources.PolicyManagerReturns(&policies.ManagerImpl{}) 800 801 t.Run("GoodResources", func(t *testing.T) { 802 mockResources.OrdererConfigReturns(mockOrderer, true) 803 mockResources.ChannelConfigReturns(mockChannel) 804 805 err := checkResources(mockResources) 806 require.NoError(t, err) 807 }) 808 809 t.Run("MissingOrdererConfigPanic", func(t *testing.T) { 810 mockResources.OrdererConfigReturns(nil, false) 811 812 err := checkResources(mockResources) 813 require.Error(t, err) 814 require.Regexp(t, "config does not contain orderer config", err.Error()) 815 }) 816 817 t.Run("MissingOrdererCapability", func(t *testing.T) { 818 mockResources.OrdererConfigReturns(mockOrderer, true) 819 mockOrdererCaps.SupportedReturns(errors.New("An error")) 820 821 err := checkResources(mockResources) 822 require.Error(t, err) 823 require.Regexp(t, "config requires unsupported orderer capabilities:", err.Error()) 824 825 // reset 826 mockOrdererCaps.SupportedReturns(nil) 827 }) 828 829 t.Run("MissingChannelCapability", func(t *testing.T) { 830 mockChannelCaps.SupportedReturns(errors.New("An error")) 831 832 err := checkResources(mockResources) 833 require.Error(t, err) 834 require.Regexp(t, "config requires unsupported channel capabilities:", err.Error()) 835 }) 836 837 t.Run("MissingOrdererConfigPanic", func(t *testing.T) { 838 mockResources.OrdererConfigReturns(nil, false) 839 840 require.Panics(t, func() { 841 checkResourcesOrPanic(mockResources) 842 }) 843 }) 844 } 845 846 // The registrar's BroadcastChannelSupport implementation should reject message types which should not be processed directly. 847 func TestBroadcastChannelSupport(t *testing.T) { 848 // system channel 849 confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 850 genesisBlockSys := encoder.New(confSys).GenesisBlock() 851 852 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 853 require.NoError(t, err) 854 855 t.Run("Rejection", func(t *testing.T) { 856 tmpdir, err := ioutil.TempDir("", "registrar_test-") 857 require.NoError(t, err) 858 defer os.RemoveAll(tmpdir) 859 860 ledgerFactory, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 861 consenter := &mocks.Consenter{} 862 consenter.HandleChainCalls(handleChain) 863 mockConsenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter} 864 registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 865 registrar.Initialize(mockConsenters) 866 randomValue := 1 867 configTx := makeConfigTx("testchannelid", randomValue) 868 _, _, _, err = registrar.BroadcastChannelSupport(configTx) 869 require.Error(t, err, "Messages of type HeaderType_CONFIG should return an error.") 870 }) 871 872 t.Run("No system channel", func(t *testing.T) { 873 tmpdir, err := ioutil.TempDir("", "registrar_test-") 874 require.NoError(t, err) 875 defer os.RemoveAll(tmpdir) 876 877 ledgerFactory, _ := newLedgerAndFactory(tmpdir, "", nil) 878 consenter := &mocks.Consenter{} 879 consenter.HandleChainCalls(handleChain) 880 mockConsenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: consenter, "etcdraft": &mocks.Consenter{}} 881 registrar := NewRegistrar(localconfig.TopLevel{ 882 General: localconfig.General{ 883 BootstrapMethod: "none", 884 GenesisFile: "", 885 }, 886 }, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 887 registrar.Initialize(mockConsenters) 888 configTx := makeConfigTxFull("testchannelid", 1) 889 _, _, _, err = registrar.BroadcastChannelSupport(configTx) 890 require.Error(t, err) 891 require.Equal(t, "channel creation request not allowed because the orderer system channel is not defined", err.Error()) 892 }) 893 } 894 895 func TestRegistrar_JoinChannel(t *testing.T) { 896 var ( 897 tmpdir string 898 tlsCA tlsgen.CA 899 confAppRaft *genesisconfig.Profile 900 genesisBlockAppRaft *cb.Block 901 confSysRaft *genesisconfig.Profile 902 genesisBlockSysRaft *cb.Block 903 cryptoProvider bccsp.BCCSP 904 config localconfig.TopLevel 905 dialer *cluster.PredicateDialer 906 ledgerFactory blockledger.Factory 907 consenter *mocks.Consenter 908 mockConsenters map[string]consensus.Consenter 909 ) 910 911 setup := func(t *testing.T) { 912 var err error 913 tmpdir, err = ioutil.TempDir("", "registrar_test-") 914 require.NoError(t, err) 915 916 tlsCA, err = tlsgen.NewCA() 917 require.NoError(t, err) 918 919 confAppRaft = genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir()) 920 confAppRaft.Consortiums = nil 921 confAppRaft.Consortium = "" 922 generateCertificates(t, confAppRaft, tlsCA, tmpdir) 923 bootstrapper, err := encoder.NewBootstrapper(confAppRaft) 924 require.NoError(t, err, "cannot create bootstrapper") 925 genesisBlockAppRaft = bootstrapper.GenesisBlockForChannel("my-raft-channel") 926 require.NotNil(t, genesisBlockAppRaft) 927 928 confSysRaft = genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir()) 929 generateCertificates(t, confSysRaft, tlsCA, tmpdir) 930 bootstrapper, err = encoder.NewBootstrapper(confSysRaft) 931 require.NoError(t, err, "cannot create bootstrapper") 932 genesisBlockSysRaft = bootstrapper.GenesisBlockForChannel("sys-raft-channel") 933 require.NotNil(t, genesisBlockSysRaft) 934 935 cryptoProvider, err = sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 936 require.NoError(t, err) 937 938 config = localconfig.TopLevel{ 939 General: localconfig.General{ 940 BootstrapMethod: "none", 941 Cluster: localconfig.Cluster{ 942 ReplicationBufferSize: 1, 943 ReplicationPullTimeout: time.Microsecond, 944 ReplicationRetryTimeout: time.Microsecond, 945 ReplicationMaxRetries: 2, 946 }, 947 }, 948 ChannelParticipation: localconfig.ChannelParticipation{ 949 Enabled: true, 950 }, 951 FileLedger: localconfig.FileLedger{ 952 Location: tmpdir, 953 }, 954 } 955 956 dialer = &cluster.PredicateDialer{ 957 Config: comm.ClientConfig{ 958 SecOpts: comm.SecureOptions{ 959 Certificate: tlsCA.CertBytes(), 960 }, 961 }, 962 } 963 964 ledgerFactory = newFactory(tmpdir) 965 consenter = &mocks.Consenter{} 966 consenter.HandleChainCalls(handleChainCluster) 967 mockConsenters = map[string]consensus.Consenter{confAppRaft.Orderer.OrdererType: consenter} 968 } 969 970 cleanup := func() { 971 ledgerFactory.Close() 972 os.RemoveAll(tmpdir) 973 } 974 975 t.Run("Reject join when removal is occurring", func(t *testing.T) { 976 setup(t) 977 defer cleanup() 978 979 newLedger(ledgerFactory, "sys-raft-channel", genesisBlockSysRaft) 980 981 registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 982 registrar.Initialize(mockConsenters) 983 984 registrar.pendingRemoval["some-app-channel"] = consensus.StaticStatusReporter{ConsensusRelation: types.ConsensusRelationFollower, Status: types.StatusInactive} 985 986 info, err := registrar.JoinChannel("some-app-channel", &cb.Block{}, true) 987 require.Equal(t, err, types.ErrChannelPendingRemoval) 988 require.Equal(t, types.ChannelInfo{}, info) 989 joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "some-app-channel.join") 990 _, err = os.Stat(joinBlockPath) 991 require.True(t, os.IsNotExist(err)) 992 }) 993 994 t.Run("Reject join when removal previously failed", func(t *testing.T) { 995 setup(t) 996 defer cleanup() 997 998 newLedger(ledgerFactory, "sys-raft-channel", genesisBlockSysRaft) 999 1000 registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 1001 registrar.Initialize(mockConsenters) 1002 1003 registrar.pendingRemoval["some-app-channel"] = consensus.StaticStatusReporter{ConsensusRelation: types.ConsensusRelationFollower, Status: types.StatusFailed} 1004 1005 info, err := registrar.JoinChannel("some-app-channel", &cb.Block{}, true) 1006 require.Equal(t, types.ErrChannelRemovalFailure, err) 1007 require.Equal(t, types.ChannelInfo{}, info) 1008 }) 1009 1010 t.Run("Reject join when system channel exists", func(t *testing.T) { 1011 setup(t) 1012 defer cleanup() 1013 1014 newLedger(ledgerFactory, "sys-raft-channel", genesisBlockSysRaft) 1015 1016 registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 1017 registrar.Initialize(mockConsenters) 1018 1019 info, err := registrar.JoinChannel("some-app-channel", &cb.Block{}, true) 1020 require.EqualError(t, err, "system channel exists") 1021 require.Equal(t, types.ChannelInfo{}, info) 1022 joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "some-app-channel.join") 1023 _, err = os.Stat(joinBlockPath) 1024 require.True(t, os.IsNotExist(err)) 1025 }) 1026 1027 t.Run("Reject join when channel exists", func(t *testing.T) { 1028 setup(t) 1029 defer cleanup() 1030 1031 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 1032 registrar.Initialize(mockConsenters) 1033 1034 ledger, err := ledgerFactory.GetOrCreate("my-raft-channel") 1035 require.NoError(t, err) 1036 ledger.Append(genesisBlockAppRaft) 1037 1038 // Before creating the chain, it doesn't exist 1039 require.Nil(t, registrar.GetChain("my-raft-channel")) 1040 // After creating the chain, it exists 1041 registrar.CreateChain("my-raft-channel") 1042 require.NotNil(t, registrar.GetChain("my-raft-channel")) 1043 1044 info, err := registrar.JoinChannel("my-raft-channel", &cb.Block{}, true) 1045 require.EqualError(t, err, "channel already exists") 1046 require.Equal(t, types.ChannelInfo{}, info) 1047 joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "my-channel.join") 1048 _, err = os.Stat(joinBlockPath) 1049 require.True(t, os.IsNotExist(err)) 1050 }) 1051 1052 t.Run("Reject system channel join when app channels exist", func(t *testing.T) { 1053 setup(t) 1054 defer cleanup() 1055 1056 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 1057 registrar.Initialize(mockConsenters) 1058 1059 ledger, err := ledgerFactory.GetOrCreate("my-raft-channel") 1060 require.NoError(t, err) 1061 ledger.Append(genesisBlockAppRaft) 1062 1063 // Before creating the chain, it doesn't exist 1064 require.Nil(t, registrar.GetChain("my-raft-channel")) 1065 // After creating the chain, it exists 1066 registrar.CreateChain("my-raft-channel") 1067 require.NotNil(t, registrar.GetChain("my-raft-channel")) 1068 1069 info, err := registrar.JoinChannel("sys-channel", &cb.Block{}, false) 1070 require.EqualError(t, err, "application channels already exist") 1071 require.Equal(t, types.ChannelInfo{}, info) 1072 joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "sys-channel.join") 1073 _, err = os.Stat(joinBlockPath) 1074 require.True(t, os.IsNotExist(err)) 1075 }) 1076 1077 t.Run("Join app channel as member without on-boarding", func(t *testing.T) { 1078 setup(t) 1079 defer cleanup() 1080 1081 consenter.IsChannelMemberReturns(true, nil) 1082 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 1083 fakeFields := newFakeMetricsFields() 1084 registrar.channelParticipationMetrics = newFakeMetrics(fakeFields) 1085 1086 t.Run("failure - consenter channel membership error", func(t *testing.T) { 1087 badConsenter := &mocks.Consenter{} 1088 badConsenter.IsChannelMemberReturns(false, errors.New("apple")) 1089 mockConsenters := map[string]consensus.Consenter{confAppRaft.Orderer.OrdererType: badConsenter} 1090 registrar.Initialize(mockConsenters) 1091 1092 // Before joining the channel, it doesn't exist 1093 require.Nil(t, registrar.GetChain("my-raft-channel")) 1094 require.Empty(t, ledgerFactory.ChannelIDs()) 1095 1096 _, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1097 require.EqualError(t, err, "failed to determine cluster membership from join-block: apple") 1098 1099 // After join failure, check that everything has been cleaned up 1100 require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 0 }, time.Minute, time.Second) 1101 require.Empty(t, ledgerFactory.ChannelIDs()) 1102 joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "my-raft-channel.join") 1103 _, err = os.Stat(joinBlockPath) 1104 require.True(t, os.IsNotExist(err)) 1105 require.Nil(t, registrar.GetChain("my-raft-channel")) 1106 require.Empty(t, ledgerFactory.ChannelIDs()) 1107 }) 1108 1109 t.Run("failure - consenter error", func(t *testing.T) { 1110 badConsenter := &mocks.Consenter{} 1111 badConsenter.IsChannelMemberReturns(true, nil) 1112 badConsenter.HandleChainReturns(nil, errors.New("banana")) 1113 mockConsenters := map[string]consensus.Consenter{confAppRaft.Orderer.OrdererType: badConsenter} 1114 registrar.Initialize(mockConsenters) 1115 1116 // Before joining the channel, it doesn't exist 1117 require.Nil(t, registrar.GetChain("my-raft-channel")) 1118 require.Empty(t, ledgerFactory.ChannelIDs()) 1119 1120 _, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1121 require.EqualError(t, err, "failed to create chain support: error creating consenter for channel: my-raft-channel: banana") 1122 1123 // After join failure, check that everything has been cleaned up 1124 require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 0 }, time.Minute, time.Second) 1125 require.Empty(t, ledgerFactory.ChannelIDs()) 1126 joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "my-raft-channel.join") 1127 _, err = os.Stat(joinBlockPath) 1128 require.True(t, os.IsNotExist(err)) 1129 require.Nil(t, registrar.GetChain("my-raft-channel")) 1130 require.Empty(t, ledgerFactory.ChannelIDs()) 1131 }) 1132 1133 t.Run("success", func(t *testing.T) { 1134 registrar.Initialize(mockConsenters) 1135 // Before joining the channel, it doesn't exist 1136 require.Nil(t, registrar.GetChain("my-raft-channel")) 1137 info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1138 require.NoError(t, err) 1139 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 0x1}, info) 1140 // After creating the channel, it exists 1141 require.NotNil(t, registrar.GetChain("my-raft-channel")) 1142 1143 // ChannelInfo() and ChannelList() are working fine 1144 info, err = registrar.ChannelInfo("my-raft-channel") 1145 require.NoError(t, err) 1146 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 0x1}, info) 1147 channelList := registrar.ChannelList() 1148 require.Equal(t, 1, len(channelList.Channels)) 1149 require.Equal(t, "my-raft-channel", channelList.Channels[0].Name) 1150 require.Nil(t, channelList.SystemChannel) 1151 joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "my-raft-channel.join") 1152 _, err = os.Stat(joinBlockPath) 1153 require.True(t, os.IsNotExist(err)) 1154 checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 1, 1, 1) 1155 }) 1156 }) 1157 1158 t.Run("Join app channel as member with on-boarding", func(t *testing.T) { 1159 setup(t) 1160 defer cleanup() 1161 1162 genesisBlockAppRaft.Header.Number = 10 1163 consenter.IsChannelMemberReturns(true, nil) 1164 1165 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1166 registrar.Initialize(mockConsenters) 1167 1168 // Before join the chain, it doesn't exist 1169 require.Nil(t, registrar.GetChain("my-raft-channel")) 1170 1171 info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1172 require.NoError(t, err) 1173 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "onboarding", Height: 0x0}, info) 1174 // After creating the follower.Chain, it not in the chains map. 1175 require.Nil(t, registrar.GetChain("my-raft-channel")) 1176 1177 // ChannelInfo() and ChannelList() are working fine 1178 info, err = registrar.ChannelInfo("my-raft-channel") 1179 require.NoError(t, err) 1180 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "onboarding", Height: 0x0}, info) 1181 channelList := registrar.ChannelList() 1182 require.Equal(t, 1, len(channelList.Channels)) 1183 require.Equal(t, "my-raft-channel", channelList.Channels[0].Name) 1184 require.Nil(t, channelList.SystemChannel) 1185 1186 fChain := registrar.GetFollower("my-raft-channel") 1187 require.NotNil(t, fChain) 1188 fChain.Halt() 1189 }) 1190 1191 t.Run("Join app channel as follower, with on-boarding", func(t *testing.T) { 1192 setup(t) 1193 defer cleanup() 1194 1195 genesisBlockAppRaft.Header.Number = 10 1196 consenter.IsChannelMemberReturns(false, nil) 1197 1198 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1199 fakeFields := newFakeMetricsFields() 1200 registrar.channelParticipationMetrics = newFakeMetrics(fakeFields) 1201 registrar.Initialize(mockConsenters) 1202 1203 // Before join the chain, it doesn't exist 1204 require.Nil(t, registrar.GetChain("my-raft-channel")) 1205 1206 info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1207 require.NoError(t, err) 1208 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "onboarding", Height: 0x0}, info) 1209 // After creating the follower.Chain, it not in the chains map. 1210 require.Nil(t, registrar.GetChain("my-raft-channel")) 1211 // ChannelInfo() and ChannelList() are working fine 1212 info, err = registrar.ChannelInfo("my-raft-channel") 1213 require.NoError(t, err) 1214 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "onboarding", Height: 0x0}, info) 1215 channelList := registrar.ChannelList() 1216 require.Equal(t, 1, len(channelList.Channels)) 1217 require.Equal(t, "my-raft-channel", channelList.Channels[0].Name) 1218 require.Nil(t, channelList.SystemChannel) 1219 1220 fChain := registrar.GetFollower("my-raft-channel") 1221 require.NotNil(t, fChain) 1222 fChain.Halt() 1223 1224 checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 2, 2, 1) 1225 }) 1226 1227 t.Run("Join app channel as follower then switch to member", func(t *testing.T) { 1228 setup(t) 1229 defer cleanup() 1230 1231 consenter.IsChannelMemberReturns(false, nil) 1232 1233 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1234 fakeFields := newFakeMetricsFields() 1235 registrar.channelParticipationMetrics = newFakeMetrics(fakeFields) 1236 registrar.Initialize(mockConsenters) 1237 1238 // Before join the chain, it doesn't exist 1239 require.Nil(t, registrar.GetChain("my-raft-channel")) 1240 require.Nil(t, registrar.GetFollower("my-raft-channel")) 1241 1242 genesisBlockAppRaft.Header.Number = 1 1243 info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1244 require.NoError(t, err) 1245 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "onboarding", Height: 0x0}, info) 1246 1247 // After creating the follower.Chain, it not in the chains map, it is in the followers map. 1248 require.Nil(t, registrar.GetChain("my-raft-channel")) 1249 fChain := registrar.GetFollower("my-raft-channel") 1250 require.NotNil(t, fChain) 1251 fChain.Halt() 1252 1253 // join-block exists before switching from follower to member 1254 joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "my-raft-channel.join") 1255 _, err = os.Stat(joinBlockPath) 1256 require.NoError(t, err) 1257 checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 2, 2, 1) 1258 1259 // Let's assume the follower appended a block 1260 genesisBlockAppRaft.Header.Number = 0 1261 newLedger(ledgerFactory, "my-raft-channel", genesisBlockAppRaft) 1262 1263 // Now Switch => a chain is created and the follower removed 1264 require.NotPanics(t, func() { registrar.SwitchFollowerToChain("my-raft-channel") }) 1265 // Now the chain is in the chains map, the follower is gone 1266 require.NotNil(t, registrar.GetChain("my-raft-channel")) 1267 require.Nil(t, registrar.GetFollower("my-raft-channel")) 1268 // ChannelInfo() and ChannelList() are still working fine 1269 info, err = registrar.ChannelInfo("my-raft-channel") 1270 require.NoError(t, err) 1271 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 0x1}, info) 1272 channelList := registrar.ChannelList() 1273 require.Equal(t, 1, len(channelList.Channels)) 1274 require.Equal(t, "my-raft-channel", channelList.Channels[0].Name) 1275 require.Nil(t, channelList.SystemChannel) 1276 1277 // join-block removed after switching from follower to member 1278 _, err = os.Stat(joinBlockPath) 1279 require.True(t, os.IsNotExist(err)) 1280 1281 checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 1, 1, 2) 1282 }) 1283 1284 t.Run("Join app channel as member, then switch to follower", func(t *testing.T) { 1285 setup(t) 1286 defer cleanup() 1287 1288 consenter.IsChannelMemberReturns(true, nil) 1289 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1290 fakeFields := newFakeMetricsFields() 1291 registrar.channelParticipationMetrics = newFakeMetrics(fakeFields) 1292 registrar.Initialize(mockConsenters) 1293 1294 // Before join the chain, it doesn't exist 1295 require.Nil(t, registrar.GetChain("my-raft-channel")) 1296 1297 info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1298 require.NoError(t, err) 1299 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 0x1}, info) 1300 // After creating the chain, it exists 1301 cs := registrar.GetChain("my-raft-channel") 1302 require.NotNil(t, cs) 1303 1304 // ChannelInfo() and ChannelList() are working fine 1305 info, err = registrar.ChannelInfo("my-raft-channel") 1306 require.NoError(t, err) 1307 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 0x1}, info) 1308 channelList := registrar.ChannelList() 1309 require.Equal(t, 1, len(channelList.Channels)) 1310 require.Equal(t, "my-raft-channel", channelList.Channels[0].Name) 1311 require.Nil(t, channelList.SystemChannel) 1312 checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 1, 1, 1) 1313 1314 // Let's assume the chain appended another config block 1315 genesisBlockAppRaft.Header.PreviousHash = protoutil.BlockHeaderHash(genesisBlockAppRaft.Header) 1316 genesisBlockAppRaft.Header.Number = 1 1317 require.NoError(t, cs.Append(genesisBlockAppRaft)) 1318 consenter.IsChannelMemberReturns(false, nil) 1319 require.Equal(t, uint64(2), cs.Height()) 1320 1321 // Now halt and switch, as if the orderer was evicted 1322 cs.Halt() 1323 require.NotPanics(t, func() { registrar.SwitchChainToFollower("my-raft-channel") }) 1324 // Now the follower is in the followers map, the chain is gone 1325 fChain := registrar.GetFollower("my-raft-channel") 1326 require.NotNil(t, fChain) 1327 require.Nil(t, registrar.GetChain("my-raft-channel")) 1328 // ChannelInfo() and ChannelList() are still working fine 1329 info, err = registrar.ChannelInfo("my-raft-channel") 1330 require.NoError(t, err) 1331 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "follower", Status: "active", Height: 0x2}, info) 1332 channelList = registrar.ChannelList() 1333 require.Equal(t, 1, len(channelList.Channels)) 1334 require.Equal(t, "my-raft-channel", channelList.Channels[0].Name) 1335 require.Nil(t, channelList.SystemChannel) 1336 fChain.Halt() 1337 require.False(t, fChain.IsRunning()) 1338 checkMetrics(t, fakeFields, []string{"channel", "my-raft-channel"}, 2, 1, 3) 1339 }) 1340 1341 t.Run("Join system channel without on-boarding", func(t *testing.T) { 1342 setup(t) 1343 defer cleanup() 1344 1345 consenter.IsChannelMemberReturns(true, nil) 1346 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 1347 registrar.Initialize(mockConsenters) 1348 1349 // Before join the chain, it doesn't exist 1350 require.Nil(t, registrar.GetChain("sys-raft-channel")) 1351 1352 info, err := registrar.JoinChannel("sys-raft-channel", genesisBlockSysRaft, false) 1353 require.NoError(t, err) 1354 require.Equal(t, types.ChannelInfo{Name: "sys-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "inactive", Height: 0x1}, info) 1355 // After creating the chain, it exists 1356 cs := registrar.GetChain("sys-raft-channel") 1357 require.NotNil(t, cs) 1358 1359 // join-block exists 1360 joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "sys-raft-channel.join") 1361 _, err = os.Stat(joinBlockPath) 1362 require.NoError(t, err) 1363 1364 // ChannelInfo() and ChannelList() are working fine 1365 info, err = registrar.ChannelInfo("sys-raft-channel") 1366 require.NoError(t, err) 1367 require.Equal(t, types.ChannelInfo{Name: "sys-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "inactive", Height: 0x1}, info) 1368 channelList := registrar.ChannelList() 1369 require.Equal(t, 0, len(channelList.Channels)) 1370 require.NotNil(t, channelList.SystemChannel) 1371 require.Equal(t, "sys-raft-channel", channelList.SystemChannel.Name) 1372 ledgerRW, err := ledgerFactory.GetOrCreate("sys-raft-channel") 1373 require.NoError(t, err) 1374 require.Equal(t, uint64(1), ledgerRW.Height(), "block was appended") 1375 }) 1376 1377 t.Run("Join system channel with on-boarding", func(t *testing.T) { 1378 setup(t) 1379 defer cleanup() 1380 1381 consenter.IsChannelMemberReturns(true, nil) 1382 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 1383 registrar.Initialize(mockConsenters) 1384 1385 // Before join the chain, it doesn't exist 1386 require.Nil(t, registrar.GetChain("sys-raft-channel")) 1387 1388 genesisBlockSysRaft.Header.Number = 7 1389 info, err := registrar.JoinChannel("sys-raft-channel", genesisBlockSysRaft, false) 1390 require.NoError(t, err) 1391 require.Equal(t, types.ChannelInfo{Name: "sys-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "inactive", Height: 0x0}, info) 1392 // After creating the chain, it exists 1393 cs := registrar.GetChain("sys-raft-channel") 1394 require.NotNil(t, cs) 1395 1396 // join-block exists 1397 joinBlockPath := filepath.Join(tmpdir, "pendingops", "join", "sys-raft-channel.join") 1398 _, err = os.Stat(joinBlockPath) 1399 require.NoError(t, err) 1400 1401 // ChannelInfo() and ChannelList() are working fine 1402 info, err = registrar.ChannelInfo("sys-raft-channel") 1403 require.NoError(t, err) 1404 require.Equal(t, types.ChannelInfo{Name: "sys-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "inactive", Height: 0x0}, info) 1405 channelList := registrar.ChannelList() 1406 require.Equal(t, 0, len(channelList.Channels)) 1407 require.NotNil(t, channelList.SystemChannel) 1408 require.Equal(t, "sys-raft-channel", channelList.SystemChannel.Name) 1409 ledgerRW, err := ledgerFactory.GetOrCreate("sys-raft-channel") 1410 require.NoError(t, err) 1411 require.Equal(t, uint64(0), ledgerRW.Height(), "block was not appended") 1412 }) 1413 } 1414 1415 func checkMetrics(t *testing.T, fakeFields *fakeMetricsFields, expectedLabels []string, expectedRelation, expectedStatus, expectedCallCount int) { 1416 require.Equal(t, expectedCallCount, fakeFields.fakeConsensusRelation.SetCallCount()) 1417 require.Equal(t, float64(expectedRelation), fakeFields.fakeConsensusRelation.SetArgsForCall(expectedCallCount-1)) 1418 require.Equal(t, expectedCallCount, fakeFields.fakeConsensusRelation.WithCallCount()) 1419 require.Equal(t, expectedLabels, fakeFields.fakeConsensusRelation.WithArgsForCall(expectedCallCount-1)) 1420 require.Equal(t, expectedCallCount, fakeFields.fakeStatus.SetCallCount()) 1421 require.Equal(t, float64(expectedStatus), fakeFields.fakeStatus.SetArgsForCall(expectedCallCount-1)) 1422 require.Equal(t, expectedCallCount, fakeFields.fakeStatus.WithCallCount()) 1423 require.Equal(t, expectedLabels, fakeFields.fakeStatus.WithArgsForCall(expectedCallCount-1)) 1424 } 1425 1426 func TestRegistrar_RemoveChannel(t *testing.T) { 1427 var ( 1428 tmpdir string 1429 tlsCA tlsgen.CA 1430 confAppRaft *genesisconfig.Profile 1431 genesisBlockAppRaft *cb.Block 1432 confSysRaft *genesisconfig.Profile 1433 genesisBlockSysRaft *cb.Block 1434 1435 cryptoProvider bccsp.BCCSP 1436 config localconfig.TopLevel 1437 dialer *cluster.PredicateDialer 1438 appBootstrapper *encoder.Bootstrapper 1439 ledgerFactory blockledger.Factory 1440 consenter *mocks.Consenter 1441 mockConsenters map[string]consensus.Consenter 1442 ) 1443 1444 setup := func(t *testing.T) { 1445 var err error 1446 tmpdir, err = ioutil.TempDir("", "remove-channel") 1447 require.NoError(t, err) 1448 1449 tlsCA, err = tlsgen.NewCA() 1450 require.NoError(t, err) 1451 1452 confAppRaft = genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir()) 1453 confAppRaft.Consortiums = nil 1454 confAppRaft.Consortium = "" 1455 generateCertificates(t, confAppRaft, tlsCA, tmpdir) 1456 appBootstrapper, err = encoder.NewBootstrapper(confAppRaft) 1457 require.NoError(t, err, "cannot create bootstrapper") 1458 genesisBlockAppRaft = appBootstrapper.GenesisBlockForChannel("my-raft-channel") 1459 require.NotNil(t, genesisBlockAppRaft) 1460 1461 confSysRaft = genesisconfig.Load(genesisconfig.SampleDevModeEtcdRaftProfile, configtest.GetDevConfigDir()) 1462 generateCertificates(t, confSysRaft, tlsCA, tmpdir) 1463 sysBootstrapper, err := encoder.NewBootstrapper(confSysRaft) 1464 require.NoError(t, err, "cannot create bootstrapper") 1465 genesisBlockSysRaft = sysBootstrapper.GenesisBlockForChannel("raft-sys-channel") 1466 require.NotNil(t, genesisBlockSysRaft) 1467 1468 cryptoProvider, err = sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 1469 require.NoError(t, err) 1470 1471 config = localconfig.TopLevel{ 1472 ChannelParticipation: localconfig.ChannelParticipation{ 1473 Enabled: true, 1474 }, 1475 General: localconfig.General{ 1476 BootstrapMethod: "none", 1477 Cluster: localconfig.Cluster{ 1478 ReplicationBufferSize: 1, 1479 ReplicationPullTimeout: time.Microsecond, 1480 ReplicationRetryTimeout: time.Microsecond, 1481 ReplicationMaxRetries: 2, 1482 }, 1483 }, 1484 FileLedger: localconfig.FileLedger{ 1485 Location: tmpdir, 1486 }, 1487 } 1488 dialer = &cluster.PredicateDialer{ 1489 Config: comm.ClientConfig{ 1490 SecOpts: comm.SecureOptions{ 1491 Certificate: tlsCA.CertBytes(), 1492 }, 1493 }, 1494 } 1495 1496 ledgerFactory = newFactory(tmpdir) 1497 consenter = &mocks.Consenter{} 1498 consenter.HandleChainCalls(handleChainCluster) 1499 mockConsenters = map[string]consensus.Consenter{"etcdraft": consenter} 1500 } 1501 1502 cleanup := func() { 1503 ledgerFactory.Close() 1504 os.RemoveAll(tmpdir) 1505 } 1506 1507 t.Run("kafka system channel exists", func(t *testing.T) { 1508 setup(t) 1509 defer cleanup() 1510 1511 confSysKafka := genesisconfig.Load(genesisconfig.SampleInsecureKafkaProfile, configtest.GetDevConfigDir()) 1512 genesisBlockSysKafka := encoder.New(confSysKafka).GenesisBlockForChannel("kafka-sys-channel") 1513 newLedger(ledgerFactory, "kafka-sys-channel", genesisBlockSysKafka) 1514 1515 kafkaConsenter := &mocks.Consenter{} 1516 kafkaConsenter.HandleChainCalls(handleChain) 1517 mockConsenters["kafka"] = kafkaConsenter 1518 1519 registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 1520 registrar.Initialize(mockConsenters) 1521 1522 t.Run("reject removal of app channel", func(t *testing.T) { 1523 err := registrar.RemoveChannel("some-app-channel") 1524 require.EqualError(t, err, "system channel exists") 1525 }) 1526 1527 t.Run("reject removal of kafka system channel", func(t *testing.T) { 1528 err := registrar.RemoveChannel("kafka-sys-channel") 1529 require.EqualError(t, err, "cannot remove kafka system channel: kafka-sys-channel") 1530 }) 1531 }) 1532 1533 t.Run("without a system channel failures", func(t *testing.T) { 1534 setup(t) 1535 defer cleanup() 1536 1537 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, nil) 1538 registrar.Initialize(mockConsenters) 1539 1540 t.Run("when channel id does not exist", func(t *testing.T) { 1541 err := registrar.RemoveChannel("some-raft-channel") 1542 require.EqualError(t, err, "channel does not exist") 1543 }) 1544 1545 t.Run("when channel id is blank", func(t *testing.T) { 1546 err := registrar.RemoveChannel("") 1547 require.EqualError(t, err, "channel does not exist") 1548 }) 1549 }) 1550 1551 t.Run("remove app channel", func(t *testing.T) { 1552 setup(t) 1553 defer cleanup() 1554 1555 t.Run("consenter", func(t *testing.T) { 1556 consenter.IsChannelMemberReturns(true, nil) 1557 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1558 registrar.Initialize(mockConsenters) 1559 1560 require.Nil(t, registrar.GetChain("my-raft-channel")) 1561 require.NotContains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1562 info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1563 require.NoError(t, err) 1564 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1}, info) 1565 require.NotNil(t, registrar.GetChain("my-raft-channel")) 1566 require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1567 1568 err = registrar.RemoveChannel("my-raft-channel") 1569 require.NoError(t, err) 1570 1571 // After removing the channel, it no longer exists in the registrar or the ledger 1572 require.Nil(t, registrar.GetChain("my-raft-channel")) 1573 require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 0 }, time.Minute, time.Second) 1574 require.NotContains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1575 }) 1576 1577 t.Run("follower", func(t *testing.T) { 1578 consenter.IsChannelMemberReturns(false, nil) 1579 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1580 registrar.Initialize(mockConsenters) 1581 1582 genesisBlockAppRaftFollower := appBootstrapper.GenesisBlockForChannel("my-follower-raft-channel") 1583 require.NotNil(t, genesisBlockAppRaftFollower) 1584 1585 require.Nil(t, registrar.GetChain("my-follower-raft-channel")) 1586 require.NotContains(t, ledgerFactory.ChannelIDs(), "my-follower-raft-channel") 1587 info, err := registrar.JoinChannel("my-follower-raft-channel", genesisBlockAppRaftFollower, true) 1588 require.NoError(t, err) 1589 require.Equal(t, types.ChannelInfo{Name: "my-follower-raft-channel", URL: "", ConsensusRelation: "follower", Status: "onboarding", Height: 0}, info) 1590 require.NotNil(t, registrar.GetFollower("my-follower-raft-channel")) 1591 require.Contains(t, ledgerFactory.ChannelIDs(), "my-follower-raft-channel") 1592 1593 err = registrar.RemoveChannel("my-follower-raft-channel") 1594 require.NoError(t, err) 1595 1596 // After removing the channel, it no longer exists in the registrar or the ledger 1597 require.Nil(t, registrar.GetFollower("my-follower-raft-channel")) 1598 require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 0 }, time.Minute, time.Second) 1599 require.NotContains(t, ledgerFactory.ChannelIDs(), "my-follower-raft-channel") 1600 1601 channelInfo, err := registrar.ChannelInfo("my-follower-raft-channel") 1602 require.Equal(t, err, types.ErrChannelNotExist) 1603 require.Equal(t, channelInfo, types.ChannelInfo{}) 1604 }) 1605 }) 1606 1607 t.Run("remove system channel", func(t *testing.T) { 1608 setup(t) 1609 defer cleanup() 1610 1611 newLedger(ledgerFactory, "raft-sys-channel", genesisBlockSysRaft) 1612 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1613 registrar.Initialize(mockConsenters) 1614 require.NotNil(t, registrar.GetChain("raft-sys-channel")) 1615 require.Contains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel") 1616 1617 createLedgerAndChain(t, registrar, ledgerFactory, genesisBlockAppRaft, "my-raft-channel") 1618 require.NotNil(t, registrar.GetChain("my-raft-channel")) 1619 require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1620 genesisBlockAppRaftNonMember := appBootstrapper.GenesisBlockForChannel("channel-im-not-a-member-of") 1621 require.NotNil(t, genesisBlockAppRaftNonMember) 1622 createLedgerAndChain(t, registrar, ledgerFactory, genesisBlockAppRaftNonMember, "channel-im-not-a-member-of") 1623 1624 require.NotNil(t, registrar.GetChain("channel-im-not-a-member-of")) 1625 require.Contains(t, ledgerFactory.ChannelIDs(), "channel-im-not-a-member-of") 1626 consenter.IsChannelMemberStub = func(b *cb.Block) (bool, error) { 1627 if bytes.Equal(b.Header.DataHash, genesisBlockAppRaft.Header.DataHash) { 1628 return true, nil 1629 } 1630 return false, nil 1631 } 1632 1633 err := registrar.RemoveChannel("raft-sys-channel") 1634 require.NoError(t, err) 1635 1636 // After removing the system channel, it no longer exists in the registrar or the ledger 1637 require.Empty(t, registrar.SystemChannelID()) 1638 require.Nil(t, registrar.SystemChannel()) 1639 require.Nil(t, registrar.GetChain("raft-sys-channel")) 1640 require.NotContains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel") 1641 1642 // application channel members still exist 1643 require.NotNil(t, registrar.GetChain("my-raft-channel")) 1644 require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1645 // channels the orderer wasn't a member no longer exist 1646 require.Nil(t, registrar.GetChain("channel-im-not-a-member-of")) 1647 require.NotContains(t, ledgerFactory.ChannelIDs(), "channel-im-not-a-member-of") 1648 }) 1649 1650 t.Run("remove system channel ledger fails", func(t *testing.T) { 1651 setup(t) 1652 defer cleanup() 1653 1654 newLedger(ledgerFactory, "raft-sys-channel", genesisBlockSysRaft) 1655 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1656 registrar.Initialize(mockConsenters) 1657 require.NotNil(t, registrar.GetChain("raft-sys-channel")) 1658 require.Contains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel") 1659 1660 os.RemoveAll(filepath.Join(tmpdir, "pendingops", "remove")) 1661 1662 err := registrar.RemoveChannel("raft-sys-channel") 1663 require.Error(t, err) 1664 require.Contains(t, err.Error(), "failed removing ledger for system channel raft-sys-channel") 1665 1666 require.NotNil(t, registrar.SystemChannelID()) 1667 require.Contains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel") 1668 }) 1669 1670 t.Run("remove non-systemchannel members fails", func(t *testing.T) { 1671 setup(t) 1672 defer cleanup() 1673 1674 newLedger(ledgerFactory, "raft-sys-channel", genesisBlockSysRaft) 1675 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1676 registrar.Initialize(mockConsenters) 1677 require.NotNil(t, registrar.GetChain("raft-sys-channel")) 1678 require.Contains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel") 1679 1680 genesisBlockAppRaftNonMember := appBootstrapper.GenesisBlockForChannel("channel-im-not-a-member-of") 1681 require.NotNil(t, genesisBlockAppRaftNonMember) 1682 createLedgerAndChain(t, registrar, ledgerFactory, genesisBlockAppRaftNonMember, "channel-im-not-a-member-of") 1683 1684 require.NotNil(t, registrar.GetChain("channel-im-not-a-member-of")) 1685 require.Contains(t, ledgerFactory.ChannelIDs(), "channel-im-not-a-member-of") 1686 consenter.IsChannelMemberStub = func(b *cb.Block) (bool, error) { 1687 os.RemoveAll(filepath.Join(tmpdir, "pendingops", "remove")) 1688 if bytes.Equal(b.Header.DataHash, genesisBlockAppRaft.Header.DataHash) { 1689 return true, nil 1690 } 1691 return false, nil 1692 } 1693 1694 err := registrar.RemoveChannel("raft-sys-channel") 1695 require.Error(t, err) 1696 require.Contains(t, err.Error(), "failed removing ledger for channel(s): channel-im-not-a-member-of") 1697 1698 require.Empty(t, registrar.SystemChannelID()) 1699 require.Nil(t, registrar.SystemChannel()) 1700 require.Nil(t, registrar.GetChain("raft-sys-channel")) 1701 require.NotContains(t, ledgerFactory.ChannelIDs(), "raft-sys-channel") 1702 1703 require.NotNil(t, registrar.GetChain("channel-im-not-a-member-of")) 1704 require.Contains(t, ledgerFactory.ChannelIDs(), "channel-im-not-a-member-of") 1705 }) 1706 1707 t.Run("app channel is already being removed", func(t *testing.T) { 1708 setup(t) 1709 defer cleanup() 1710 1711 consenter.IsChannelMemberReturns(true, nil) 1712 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1713 registrar.Initialize(mockConsenters) 1714 1715 require.Nil(t, registrar.GetChain("my-raft-channel")) 1716 require.NotContains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1717 info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1718 require.NoError(t, err) 1719 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1}, info) 1720 require.NotNil(t, registrar.GetChain("my-raft-channel")) 1721 require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1722 1723 registrar.pendingRemoval["my-raft-channel"] = consensus.StaticStatusReporter{ConsensusRelation: types.ConsensusRelationFollower, Status: types.StatusInactive} 1724 1725 err = registrar.RemoveChannel("my-raft-channel") 1726 require.Error(t, err) 1727 require.Equal(t, types.ErrChannelPendingRemoval, err) 1728 1729 require.Contains(t, registrar.ChannelList().Channels, types.ChannelInfoShort{Name: "my-raft-channel"}) 1730 }) 1731 1732 t.Run("app channel removal previously failed", func(t *testing.T) { 1733 setup(t) 1734 defer cleanup() 1735 1736 consenter.IsChannelMemberReturns(true, nil) 1737 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1738 registrar.Initialize(mockConsenters) 1739 1740 require.Nil(t, registrar.GetChain("my-raft-channel")) 1741 require.NotContains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1742 info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1743 require.NoError(t, err) 1744 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1}, info) 1745 require.NotNil(t, registrar.GetChain("my-raft-channel")) 1746 require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1747 1748 registrar.pendingRemoval["my-raft-channel"] = consensus.StaticStatusReporter{ConsensusRelation: types.ConsensusRelationFollower, Status: types.StatusFailed} 1749 1750 err = registrar.RemoveChannel("my-raft-channel") 1751 require.NoError(t, err) 1752 1753 require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 0 }, time.Minute, time.Second) 1754 require.NotContains(t, registrar.ChannelList().Channels, types.ChannelInfoShort{Name: "my-raft-channel"}) 1755 }) 1756 1757 t.Run("remove channel fails", func(t *testing.T) { 1758 setup(t) 1759 defer cleanup() 1760 1761 consenter.IsChannelMemberReturns(true, nil) 1762 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider, dialer) 1763 registrar.Initialize(mockConsenters) 1764 1765 require.Nil(t, registrar.GetChain("my-raft-channel")) 1766 require.NotContains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1767 info, err := registrar.JoinChannel("my-raft-channel", genesisBlockAppRaft, true) 1768 require.NoError(t, err) 1769 require.Equal(t, types.ChannelInfo{Name: "my-raft-channel", URL: "", ConsensusRelation: "consenter", Status: "active", Height: 1}, info) 1770 require.NotNil(t, registrar.GetChain("my-raft-channel")) 1771 require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1772 1773 // This will force the failure of the underlying factory.Remove() because the pendingops/remove will not exist 1774 os.RemoveAll(filepath.Join(tmpdir, "pendingops", "remove")) 1775 1776 err = registrar.RemoveChannel("my-raft-channel") 1777 require.NoError(t, err) 1778 1779 require.Nil(t, registrar.GetChain("my-raft-channel")) 1780 require.Eventually(t, func() bool { return len(ledgerFactory.ChannelIDs()) == 1 }, time.Minute, time.Second) 1781 require.Contains(t, ledgerFactory.ChannelIDs(), "my-raft-channel") 1782 1783 // Confirm removal failure by checking channel status 1784 channelInfo, err := registrar.ChannelInfo("my-raft-channel") 1785 require.NoError(t, err) 1786 require.Equal(t, channelInfo, types.ChannelInfo{ 1787 Name: "my-raft-channel", 1788 ConsensusRelation: types.ConsensusRelationConsenter, 1789 Status: types.StatusFailed, 1790 }) 1791 }) 1792 } 1793 1794 func generateCertificates(t *testing.T, confAppRaft *genesisconfig.Profile, tlsCA tlsgen.CA, certDir string) { 1795 for i, c := range confAppRaft.Orderer.EtcdRaft.Consenters { 1796 srvC, err := tlsCA.NewServerCertKeyPair(c.Host) 1797 require.NoError(t, err) 1798 srvP := path.Join(certDir, fmt.Sprintf("server%d.crt", i)) 1799 err = ioutil.WriteFile(srvP, srvC.Cert, 0o644) 1800 require.NoError(t, err) 1801 1802 clnC, err := tlsCA.NewClientCertKeyPair() 1803 require.NoError(t, err) 1804 clnP := path.Join(certDir, fmt.Sprintf("client%d.crt", i)) 1805 err = ioutil.WriteFile(clnP, clnC.Cert, 0o644) 1806 require.NoError(t, err) 1807 1808 c.ServerTlsCert = []byte(srvP) 1809 c.ClientTlsCert = []byte(clnP) 1810 } 1811 } 1812 1813 func createLedgerAndChain(t *testing.T, r *Registrar, lf blockledger.Factory, b *cb.Block, channel string) { 1814 require.Nil(t, r.GetChain(channel)) 1815 require.NotContains(t, lf.ChannelIDs(), channel) 1816 ledger, err := lf.GetOrCreate(channel) 1817 require.NoError(t, err) 1818 ledger.Append(b) 1819 require.Contains(t, lf.ChannelIDs(), channel) 1820 r.CreateChain(channel) 1821 require.NotNil(t, r.GetChain(channel)) 1822 } 1823 1824 func TestRegistrar_ConfigBlockOrPanic(t *testing.T) { 1825 t.Run("Panics when ledger is empty", func(t *testing.T) { 1826 tmpdir, err := ioutil.TempDir("", "file-ledger") 1827 require.NoError(t, err) 1828 defer os.RemoveAll(tmpdir) 1829 1830 _, l := newLedgerAndFactory(tmpdir, "testchannelid", nil) 1831 1832 require.PanicsWithValue(t, "Failed to retrieve block", func() { 1833 ConfigBlockOrPanic(l) 1834 }) 1835 }) 1836 1837 t.Run("Panics when config block not complete", func(t *testing.T) { 1838 block := protoutil.NewBlock(0, nil) 1839 block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = []byte("bad metadata") 1840 1841 tmpdir, err := ioutil.TempDir("", "file-ledger") 1842 require.NoError(t, err) 1843 defer os.RemoveAll(tmpdir) 1844 1845 _, l := newLedgerAndFactory(tmpdir, "testchannelid", block) 1846 1847 require.PanicsWithValue(t, "Chain did not have appropriately encoded last config in its latest block", func() { 1848 ConfigBlockOrPanic(l) 1849 }) 1850 }) 1851 1852 t.Run("Panics when block referenes invalid config block", func(t *testing.T) { 1853 block := protoutil.NewBlock(0, nil) 1854 block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{ 1855 Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{ 1856 LastConfig: &cb.LastConfig{Index: 2}, 1857 }), 1858 }) 1859 1860 tmpdir, err := ioutil.TempDir("", "file-ledger") 1861 require.NoError(t, err) 1862 defer os.RemoveAll(tmpdir) 1863 1864 _, l := newLedgerAndFactory(tmpdir, "testchannelid", block) 1865 1866 require.PanicsWithValue(t, "Failed to retrieve config block", func() { 1867 ConfigBlockOrPanic(l) 1868 }) 1869 }) 1870 1871 t.Run("Returns valid config block", func(t *testing.T) { 1872 confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 1873 genesisBlockSys := encoder.New(confSys).GenesisBlock() 1874 1875 tmpdir, err := ioutil.TempDir("", "file-ledger") 1876 require.NoError(t, err) 1877 defer os.RemoveAll(tmpdir) 1878 1879 _, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 1880 1881 cBlock := ConfigBlockOrPanic(l) 1882 assert.Equal(t, genesisBlockSys.Header, cBlock.Header) 1883 assert.Equal(t, genesisBlockSys.Data, cBlock.Data) 1884 }) 1885 }