github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/orderer/common/multichannel/registrar_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package multichannel 8 9 import ( 10 "io/ioutil" 11 "os" 12 "testing" 13 14 "github.com/golang/protobuf/proto" 15 cb "github.com/hyperledger/fabric-protos-go/common" 16 ab "github.com/hyperledger/fabric-protos-go/orderer" 17 "github.com/hyperledger/fabric/bccsp/sw" 18 "github.com/hyperledger/fabric/common/channelconfig" 19 "github.com/hyperledger/fabric/common/ledger/blockledger" 20 "github.com/hyperledger/fabric/common/ledger/blockledger/fileledger" 21 "github.com/hyperledger/fabric/common/metrics/disabled" 22 "github.com/hyperledger/fabric/common/policies" 23 "github.com/hyperledger/fabric/core/config/configtest" 24 "github.com/hyperledger/fabric/internal/configtxgen/encoder" 25 "github.com/hyperledger/fabric/internal/configtxgen/genesisconfig" 26 "github.com/hyperledger/fabric/internal/pkg/identity" 27 "github.com/hyperledger/fabric/orderer/common/blockcutter" 28 "github.com/hyperledger/fabric/orderer/common/localconfig" 29 "github.com/hyperledger/fabric/orderer/common/multichannel/mocks" 30 "github.com/hyperledger/fabric/orderer/consensus" 31 "github.com/hyperledger/fabric/protoutil" 32 "github.com/pkg/errors" 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/require" 35 ) 36 37 //go:generate counterfeiter -o mocks/resources.go --fake-name Resources . resources 38 39 type resources interface { 40 channelconfig.Resources 41 } 42 43 //go:generate counterfeiter -o mocks/orderer_config.go --fake-name OrdererConfig . ordererConfig 44 45 type ordererConfig interface { 46 channelconfig.Orderer 47 } 48 49 //go:generate counterfeiter -o mocks/orderer_capabilities.go --fake-name OrdererCapabilities . ordererCapabilities 50 51 type ordererCapabilities interface { 52 channelconfig.OrdererCapabilities 53 } 54 55 //go:generate counterfeiter -o mocks/channel_config.go --fake-name ChannelConfig . channelConfig 56 57 type channelConfig interface { 58 channelconfig.Channel 59 } 60 61 //go:generate counterfeiter -o mocks/channel_capabilities.go --fake-name ChannelCapabilities . channelCapabilities 62 63 type channelCapabilities interface { 64 channelconfig.ChannelCapabilities 65 } 66 67 //go:generate counterfeiter -o mocks/signer_serializer.go --fake-name SignerSerializer . signerSerializer 68 69 type signerSerializer interface { 70 identity.SignerSerializer 71 } 72 73 func mockCrypto() *mocks.SignerSerializer { 74 return &mocks.SignerSerializer{} 75 } 76 77 func newLedgerAndFactory(dir string, chainID string, genesisBlockSys *cb.Block) (blockledger.Factory, blockledger.ReadWriter) { 78 rlf, err := fileledger.New(dir, &disabled.Provider{}) 79 if err != nil { 80 panic(err) 81 } 82 83 rl, err := rlf.GetOrCreate(chainID) 84 if err != nil { 85 panic(err) 86 } 87 88 if genesisBlockSys != nil { 89 err = rl.Append(genesisBlockSys) 90 if err != nil { 91 panic(err) 92 } 93 } 94 return rlf, rl 95 } 96 97 func testMessageOrderAndRetrieval(maxMessageCount uint32, chainID string, chainSupport *ChainSupport, lr blockledger.ReadWriter, t *testing.T) { 98 messages := make([]*cb.Envelope, maxMessageCount) 99 for i := uint32(0); i < maxMessageCount; i++ { 100 messages[i] = makeNormalTx(chainID, int(i)) 101 } 102 for _, message := range messages { 103 chainSupport.Order(message, 0) 104 } 105 it, _ := lr.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 106 defer it.Close() 107 block, status := it.Next() 108 assert.Equal(t, cb.Status_SUCCESS, status, "Could not retrieve block") 109 for i := uint32(0); i < maxMessageCount; i++ { 110 assert.True(t, proto.Equal(messages[i], protoutil.ExtractEnvelopeOrPanic(block, int(i))), "Block contents wrong at index %d", i) 111 } 112 } 113 114 func TestConfigTx(t *testing.T) { 115 //system channel 116 confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 117 genesisBlockSys := encoder.New(confSys).GenesisBlock() 118 119 // Tests for a normal chain which contains 3 config transactions and other normal transactions to make 120 // sure the right one returned 121 t.Run("GetConfigTx - ok", func(t *testing.T) { 122 tmpdir, err := ioutil.TempDir("", "registrar_test-") 123 require.NoError(t, err) 124 defer os.RemoveAll(tmpdir) 125 126 _, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 127 for i := 0; i < 5; i++ { 128 rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx("testchannelid", i)})) 129 } 130 rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{makeConfigTx("testchannelid", 5)})) 131 ctx := makeConfigTx("testchannelid", 6) 132 rl.Append(blockledger.CreateNextBlock(rl, []*cb.Envelope{ctx})) 133 134 block := blockledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx("testchannelid", 7)}) 135 block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{Value: protoutil.MarshalOrPanic(&cb.LastConfig{Index: 7})}) 136 rl.Append(block) 137 138 pctx := configTx(rl) 139 assert.True(t, proto.Equal(pctx, ctx), "Did not select most recent config transaction") 140 }) 141 } 142 143 func TestNewRegistrar(t *testing.T) { 144 //system channel 145 confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 146 genesisBlockSys := encoder.New(confSys).GenesisBlock() 147 148 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 149 assert.NoError(t, err) 150 151 // This test checks to make sure the orderer refuses to come up if it cannot find a system channel 152 t.Run("No system chain - failure", func(t *testing.T) { 153 tmpdir, err := ioutil.TempDir("", "registrar_test-") 154 require.NoError(t, err) 155 defer os.RemoveAll(tmpdir) 156 157 lf, err := fileledger.New(tmpdir, &disabled.Provider{}) 158 require.NoError(t, err) 159 160 consenters := make(map[string]consensus.Consenter) 161 consenters[confSys.Orderer.OrdererType] = &mockConsenter{} 162 163 assert.NotPanics(t, func() { 164 NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider).Initialize(consenters) 165 }, "Should not panic when starting without a system channel") 166 }) 167 168 // This test checks to make sure that the orderer refuses to come up if there are multiple system channels 169 t.Run("Multiple system chains - failure", func(t *testing.T) { 170 tmpdir, err := ioutil.TempDir("", "registrar_test-") 171 require.NoError(t, err) 172 defer os.RemoveAll(tmpdir) 173 174 lf, err := fileledger.New(tmpdir, &disabled.Provider{}) 175 require.NoError(t, err) 176 177 for _, id := range []string{"foo", "bar"} { 178 rl, err := lf.GetOrCreate(id) 179 assert.NoError(t, err) 180 181 err = rl.Append(encoder.New(confSys).GenesisBlockForChannel(id)) 182 assert.NoError(t, err) 183 } 184 185 consenters := make(map[string]consensus.Consenter) 186 consenters[confSys.Orderer.OrdererType] = &mockConsenter{} 187 188 assert.Panics(t, func() { 189 NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider).Initialize(consenters) 190 }, "Two system channels should have caused panic") 191 }) 192 193 // This test essentially brings the entire system up and is ultimately what main.go will replicate 194 t.Run("Correct flow", func(t *testing.T) { 195 tmpdir, err := ioutil.TempDir("", "registrar_test-") 196 require.NoError(t, err) 197 defer os.RemoveAll(tmpdir) 198 199 lf, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 200 201 consenters := make(map[string]consensus.Consenter) 202 consenters[confSys.Orderer.OrdererType] = &mockConsenter{} 203 204 manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider) 205 manager.Initialize(consenters) 206 207 chainSupport := manager.GetChain("Fake") 208 assert.Nilf(t, chainSupport, "Should not have found a chain that was not created") 209 210 chainSupport = manager.GetChain("testchannelid") 211 assert.NotNilf(t, chainSupport, "Should have gotten chain which was initialized by ledger") 212 213 testMessageOrderAndRetrieval(confSys.Orderer.BatchSize.MaxMessageCount, "testchannelid", chainSupport, rl, t) 214 }) 215 } 216 217 func TestCreateChain(t *testing.T) { 218 //system channel 219 confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 220 genesisBlockSys := encoder.New(confSys).GenesisBlock() 221 222 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 223 assert.NoError(t, err) 224 225 t.Run("Create chain", func(t *testing.T) { 226 tmpdir, err := ioutil.TempDir("", "registrar_test-") 227 require.NoError(t, err) 228 defer os.RemoveAll(tmpdir) 229 230 lf, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 231 232 consenters := make(map[string]consensus.Consenter) 233 consenters[confSys.Orderer.OrdererType] = &mockConsenter{} 234 235 manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider) 236 manager.Initialize(consenters) 237 238 ledger, err := lf.GetOrCreate("mychannel") 239 assert.NoError(t, err) 240 241 genesisBlock := encoder.New(confSys).GenesisBlockForChannel("mychannel") 242 ledger.Append(genesisBlock) 243 244 // Before creating the chain, it doesn't exist 245 assert.Nil(t, manager.GetChain("mychannel")) 246 // After creating the chain, it exists 247 manager.CreateChain("mychannel") 248 chain := manager.GetChain("mychannel") 249 assert.NotNil(t, chain) 250 // A subsequent creation, replaces the chain. 251 manager.CreateChain("mychannel") 252 chain2 := manager.GetChain("mychannel") 253 assert.NotNil(t, chain2) 254 // They are not the same 255 assert.NotEqual(t, chain, chain2) 256 // The old chain is halted 257 _, ok := <-chain.Chain.(*mockChain).queue 258 assert.False(t, ok) 259 // The new chain is not halted: Close the channel to prove that. 260 close(chain2.Chain.(*mockChain).queue) 261 }) 262 263 // This test brings up the entire system, with the mock consenter, including the broadcasters etc. and creates a new chain 264 t.Run("New chain", func(t *testing.T) { 265 expectedLastConfigBlockNumber := uint64(0) 266 expectedLastConfigSeq := uint64(1) 267 newChainID := "test-new-chain" 268 269 tmpdir, err := ioutil.TempDir("", "registrar_test-") 270 require.NoError(t, err) 271 defer os.RemoveAll(tmpdir) 272 273 lf, rl := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 274 275 consenters := make(map[string]consensus.Consenter) 276 consenters[confSys.Orderer.OrdererType] = &mockConsenter{} 277 278 manager := NewRegistrar(localconfig.TopLevel{}, lf, mockCrypto(), &disabled.Provider{}, cryptoProvider) 279 manager.Initialize(consenters) 280 orglessChannelConf := genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile, configtest.GetDevConfigDir()) 281 orglessChannelConf.Application.Organizations = nil 282 envConfigUpdate, err := encoder.MakeChannelCreationTransaction(newChainID, mockCrypto(), orglessChannelConf) 283 assert.NoError(t, err, "Constructing chain creation tx") 284 285 res, err := manager.NewChannelConfig(envConfigUpdate) 286 assert.NoError(t, err, "Constructing initial channel config") 287 288 configEnv, err := res.ConfigtxValidator().ProposeConfigUpdate(envConfigUpdate) 289 assert.NoError(t, err, "Proposing initial update") 290 assert.Equal(t, expectedLastConfigSeq, configEnv.GetConfig().Sequence, "Sequence of config envelope for new channel should always be set to %d", expectedLastConfigSeq) 291 292 ingressTx, err := protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG, newChainID, mockCrypto(), configEnv, msgVersion, epoch) 293 assert.NoError(t, err, "Creating ingresstx") 294 295 wrapped := wrapConfigTx(ingressTx) 296 297 chainSupport := manager.GetChain(manager.SystemChannelID()) 298 assert.NotNilf(t, chainSupport, "Could not find system channel") 299 300 chainSupport.Configure(wrapped, 0) 301 func() { 302 it, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 303 defer it.Close() 304 block, status := it.Next() 305 if status != cb.Status_SUCCESS { 306 t.Fatalf("Could not retrieve block") 307 } 308 if len(block.Data.Data) != 1 { 309 t.Fatalf("Should have had only one message in the orderer transaction block") 310 } 311 312 assert.True(t, proto.Equal(wrapped, protoutil.UnmarshalEnvelopeOrPanic(block.Data.Data[0])), "Orderer config block contains wrong transaction") 313 }() 314 315 chainSupport = manager.GetChain(newChainID) 316 if chainSupport == nil { 317 t.Fatalf("Should have gotten new chain which was created") 318 } 319 320 messages := make([]*cb.Envelope, confSys.Orderer.BatchSize.MaxMessageCount) 321 for i := 0; i < int(confSys.Orderer.BatchSize.MaxMessageCount); i++ { 322 messages[i] = makeNormalTx(newChainID, i) 323 } 324 325 for _, message := range messages { 326 chainSupport.Order(message, 0) 327 } 328 329 it, _ := chainSupport.Reader().Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 0}}}) 330 defer it.Close() 331 block, status := it.Next() 332 if status != cb.Status_SUCCESS { 333 t.Fatalf("Could not retrieve new chain genesis block") 334 } 335 testLastConfigBlockNumber(t, block, expectedLastConfigBlockNumber) 336 if len(block.Data.Data) != 1 { 337 t.Fatalf("Should have had only one message in the new genesis block") 338 } 339 340 assert.True(t, proto.Equal(ingressTx, protoutil.UnmarshalEnvelopeOrPanic(block.Data.Data[0])), "Genesis block contains wrong transaction") 341 342 block, status = it.Next() 343 if status != cb.Status_SUCCESS { 344 t.Fatalf("Could not retrieve block on new chain") 345 } 346 testLastConfigBlockNumber(t, block, expectedLastConfigBlockNumber) 347 for i := 0; i < int(confSys.Orderer.BatchSize.MaxMessageCount); i++ { 348 if !proto.Equal(protoutil.ExtractEnvelopeOrPanic(block, i), messages[i]) { 349 t.Errorf("Block contents wrong at index %d in new chain", i) 350 } 351 } 352 353 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 354 assert.NoError(t, err) 355 rcs := newChainSupport(manager, chainSupport.ledgerResources, consenters, mockCrypto(), blockcutter.NewMetrics(&disabled.Provider{}), cryptoProvider) 356 assert.Equal(t, expectedLastConfigSeq, rcs.lastConfigSeq, "On restart, incorrect lastConfigSeq") 357 }) 358 } 359 360 func testLastConfigBlockNumber(t *testing.T, block *cb.Block, expectedBlockNumber uint64) { 361 metadataItem := &cb.Metadata{} 362 err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG], metadataItem) 363 assert.NoError(t, err, "Block should carry LAST_CONFIG metadata item") 364 lastConfig := &cb.LastConfig{} 365 err = proto.Unmarshal(metadataItem.Value, lastConfig) 366 assert.NoError(t, err, "LAST_CONFIG metadata item should carry last config value") 367 assert.Equal(t, expectedBlockNumber, lastConfig.Index, "LAST_CONFIG value should point to last config block") 368 } 369 370 func TestResourcesCheck(t *testing.T) { 371 mockOrderer := &mocks.OrdererConfig{} 372 mockOrdererCaps := &mocks.OrdererCapabilities{} 373 mockOrderer.CapabilitiesReturns(mockOrdererCaps) 374 mockChannel := &mocks.ChannelConfig{} 375 mockChannelCaps := &mocks.ChannelCapabilities{} 376 mockChannel.CapabilitiesReturns(mockChannelCaps) 377 378 mockResources := &mocks.Resources{} 379 mockResources.PolicyManagerReturns(&policies.ManagerImpl{}) 380 381 t.Run("GoodResources", func(t *testing.T) { 382 mockResources.OrdererConfigReturns(mockOrderer, true) 383 mockResources.ChannelConfigReturns(mockChannel) 384 385 err := checkResources(mockResources) 386 assert.NoError(t, err) 387 }) 388 389 t.Run("MissingOrdererConfigPanic", func(t *testing.T) { 390 mockResources.OrdererConfigReturns(nil, false) 391 392 err := checkResources(mockResources) 393 assert.Error(t, err) 394 assert.Regexp(t, "config does not contain orderer config", err.Error()) 395 }) 396 397 t.Run("MissingOrdererCapability", func(t *testing.T) { 398 mockResources.OrdererConfigReturns(mockOrderer, true) 399 mockOrdererCaps.SupportedReturns(errors.New("An error")) 400 401 err := checkResources(mockResources) 402 assert.Error(t, err) 403 assert.Regexp(t, "config requires unsupported orderer capabilities:", err.Error()) 404 405 // reset 406 mockOrdererCaps.SupportedReturns(nil) 407 }) 408 409 t.Run("MissingChannelCapability", func(t *testing.T) { 410 mockChannelCaps.SupportedReturns(errors.New("An error")) 411 412 err := checkResources(mockResources) 413 assert.Error(t, err) 414 assert.Regexp(t, "config requires unsupported channel capabilities:", err.Error()) 415 }) 416 417 t.Run("MissingOrdererConfigPanic", func(t *testing.T) { 418 mockResources.OrdererConfigReturns(nil, false) 419 420 assert.Panics(t, func() { 421 checkResourcesOrPanic(mockResources) 422 }) 423 }) 424 } 425 426 // The registrar's BroadcastChannelSupport implementation should reject message types which should not be processed directly. 427 func TestBroadcastChannelSupport(t *testing.T) { 428 // system channel 429 confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) 430 genesisBlockSys := encoder.New(confSys).GenesisBlock() 431 432 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 433 assert.NoError(t, err) 434 435 t.Run("Rejection", func(t *testing.T) { 436 tmpdir, err := ioutil.TempDir("", "registrar_test-") 437 require.NoError(t, err) 438 defer os.RemoveAll(tmpdir) 439 440 ledgerFactory, _ := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) 441 mockConsenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: &mockConsenter{}} 442 registrar := NewRegistrar(localconfig.TopLevel{}, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider) 443 registrar.Initialize(mockConsenters) 444 randomValue := 1 445 configTx := makeConfigTx("testchannelid", randomValue) 446 _, _, _, err = registrar.BroadcastChannelSupport(configTx) 447 assert.Error(t, err, "Messages of type HeaderType_CONFIG should return an error.") 448 }) 449 450 t.Run("No system channel", func(t *testing.T) { 451 tmpdir, err := ioutil.TempDir("", "registrar_test-") 452 require.NoError(t, err) 453 defer os.RemoveAll(tmpdir) 454 455 ledgerFactory, _ := newLedgerAndFactory(tmpdir, "", nil) 456 mockConsenters := map[string]consensus.Consenter{confSys.Orderer.OrdererType: &mockConsenter{}} 457 config := localconfig.TopLevel{} 458 config.General.BootstrapMethod = "none" 459 config.General.GenesisFile = "" 460 registrar := NewRegistrar(config, ledgerFactory, mockCrypto(), &disabled.Provider{}, cryptoProvider) 461 registrar.Initialize(mockConsenters) 462 configTx := makeConfigTxFull("testchannelid", 1) 463 _, _, _, err = registrar.BroadcastChannelSupport(configTx) 464 assert.Error(t, err) 465 assert.Equal(t, "channel creation request not allowed because the orderer system channel is not yet defined", err.Error()) 466 }) 467 }