github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/orderer/multichain/manager_test.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package multichain 18 19 import ( 20 "fmt" 21 "reflect" 22 "testing" 23 "time" 24 25 "github.com/golang/protobuf/proto" 26 "github.com/hyperledger/fabric/common/config" 27 "github.com/hyperledger/fabric/common/configtx" 28 genesisconfig "github.com/hyperledger/fabric/common/configtx/tool/localconfig" 29 "github.com/hyperledger/fabric/common/configtx/tool/provisional" 30 mockcrypto "github.com/hyperledger/fabric/common/mocks/crypto" 31 "github.com/hyperledger/fabric/msp" 32 "github.com/hyperledger/fabric/orderer/ledger" 33 ramledger "github.com/hyperledger/fabric/orderer/ledger/ram" 34 cb "github.com/hyperledger/fabric/protos/common" 35 ab "github.com/hyperledger/fabric/protos/orderer" 36 "github.com/hyperledger/fabric/protos/utils" 37 38 mmsp "github.com/hyperledger/fabric/common/mocks/msp" 39 logging "github.com/op/go-logging" 40 "github.com/stretchr/testify/assert" 41 ) 42 43 var conf, singleMSPConf, noConsortiumConf *genesisconfig.Profile 44 var genesisBlock, singleMSPGenesisBlock, noConsortiumGenesisBlock *cb.Block 45 var mockSigningIdentity msp.SigningIdentity 46 47 const NoConsortiumChain = "no-consortium-chain" 48 49 func init() { 50 logging.SetLevel(logging.DEBUG, "") 51 mockSigningIdentity, _ = mmsp.NewNoopMsp().GetDefaultSigningIdentity() 52 53 conf = genesisconfig.Load(genesisconfig.SampleInsecureProfile) 54 genesisBlock = provisional.New(conf).GenesisBlock() 55 56 singleMSPConf = genesisconfig.Load(genesisconfig.SampleSingleMSPSoloProfile) 57 singleMSPGenesisBlock = provisional.New(singleMSPConf).GenesisBlock() 58 59 noConsortiumConf = genesisconfig.Load("SampleNoConsortium") 60 noConsortiumGenesisBlock = provisional.New(noConsortiumConf).GenesisBlockForChannel(NoConsortiumChain) 61 } 62 63 func mockCrypto() *mockCryptoHelper { 64 return &mockCryptoHelper{LocalSigner: mockcrypto.FakeLocalSigner} 65 } 66 67 type mockCryptoHelper struct { 68 *mockcrypto.LocalSigner 69 } 70 71 func (mch mockCryptoHelper) VerifySignature(sd *cb.SignedData) error { 72 return nil 73 } 74 75 func NewRAMLedgerAndFactory(maxSize int) (ledger.Factory, ledger.ReadWriter) { 76 rlf := ramledger.New(10) 77 rl, err := rlf.GetOrCreate(provisional.TestChainID) 78 if err != nil { 79 panic(err) 80 } 81 err = rl.Append(genesisBlock) 82 if err != nil { 83 panic(err) 84 } 85 return rlf, rl 86 } 87 88 func NewRAMLedgerAndFactoryWithMSP() (ledger.Factory, ledger.ReadWriter) { 89 rlf := ramledger.New(10) 90 91 rl, err := rlf.GetOrCreate(provisional.TestChainID) 92 if err != nil { 93 panic(err) 94 } 95 err = rl.Append(singleMSPGenesisBlock) 96 if err != nil { 97 panic(err) 98 } 99 return rlf, rl 100 } 101 102 func NewRAMLedger(maxSize int) ledger.ReadWriter { 103 _, rl := NewRAMLedgerAndFactory(maxSize) 104 return rl 105 } 106 107 // Tests for a normal chain which contains 3 config transactions and other normal transactions to make sure the right one returned 108 func TestGetConfigTx(t *testing.T) { 109 rl := NewRAMLedger(10) 110 for i := 0; i < 5; i++ { 111 rl.Append(ledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, i)})) 112 } 113 rl.Append(ledger.CreateNextBlock(rl, []*cb.Envelope{makeConfigTx(provisional.TestChainID, 5)})) 114 ctx := makeConfigTx(provisional.TestChainID, 6) 115 rl.Append(ledger.CreateNextBlock(rl, []*cb.Envelope{ctx})) 116 117 block := ledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 7)}) 118 block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = utils.MarshalOrPanic(&cb.Metadata{Value: utils.MarshalOrPanic(&cb.LastConfig{Index: 7})}) 119 rl.Append(block) 120 121 pctx := getConfigTx(rl) 122 assert.Equal(t, pctx, ctx, "Did not select most recent config transaction") 123 } 124 125 // Tests a chain which contains blocks with multi-transactions mixed with config txs, and a single tx which is not a config tx, none count as config blocks so nil should return 126 func TestGetConfigTxFailure(t *testing.T) { 127 rl := NewRAMLedger(10) 128 for i := 0; i < 10; i++ { 129 rl.Append(ledger.CreateNextBlock(rl, []*cb.Envelope{ 130 makeNormalTx(provisional.TestChainID, i), 131 makeConfigTx(provisional.TestChainID, i), 132 })) 133 } 134 rl.Append(ledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 11)})) 135 assert.Panics(t, func() { getConfigTx(rl) }, "Should have panicked because there was no config tx") 136 137 block := ledger.CreateNextBlock(rl, []*cb.Envelope{makeNormalTx(provisional.TestChainID, 12)}) 138 block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = []byte("bad metadata") 139 assert.Panics(t, func() { getConfigTx(rl) }, "Should have panicked because of bad last config metadata") 140 } 141 142 // This test checks to make sure the orderer refuses to come up if it cannot find a system channel 143 func TestNoSystemChain(t *testing.T) { 144 lf := ramledger.New(10) 145 146 consenters := make(map[string]Consenter) 147 consenters[conf.Orderer.OrdererType] = &mockConsenter{} 148 149 assert.Panics(t, func() { NewManagerImpl(lf, consenters, mockCrypto()) }, "Should have panicked when starting without a system chain") 150 } 151 152 // This test checks to make sure that the orderer refuses to come up if there are multiple system channels 153 func TestMultiSystemChannel(t *testing.T) { 154 lf := ramledger.New(10) 155 156 for _, id := range []string{"foo", "bar"} { 157 rl, err := lf.GetOrCreate(id) 158 assert.NoError(t, err) 159 160 err = rl.Append(provisional.New(conf).GenesisBlockForChannel(id)) 161 assert.NoError(t, err) 162 } 163 164 consenters := make(map[string]Consenter) 165 consenters[conf.Orderer.OrdererType] = &mockConsenter{} 166 167 assert.Panics(t, func() { NewManagerImpl(lf, consenters, mockCrypto()) }, "Two system channels should have caused panic") 168 } 169 170 // This test checks to make sure that the orderer creates different type of filters given different type of channel 171 func TestFilterCreation(t *testing.T) { 172 lf := ramledger.New(10) 173 rl, err := lf.GetOrCreate(provisional.TestChainID) 174 if err != nil { 175 panic(err) 176 } 177 err = rl.Append(genesisBlock) 178 if err != nil { 179 panic(err) 180 } 181 182 // Creating a non-system chain to test that NewManagerImpl could handle the diversity 183 rl, err = lf.GetOrCreate(NoConsortiumChain) 184 if err != nil { 185 panic(err) 186 } 187 err = rl.Append(noConsortiumGenesisBlock) 188 if err != nil { 189 panic(err) 190 } 191 192 consenters := make(map[string]Consenter) 193 consenters[conf.Orderer.OrdererType] = &mockConsenter{} 194 195 manager := NewManagerImpl(lf, consenters, mockCrypto()) 196 197 _, ok := manager.GetChain(provisional.TestChainID) 198 assert.True(t, ok, "Should have found chain: %d", provisional.TestChainID) 199 200 chainSupport, ok := manager.GetChain(NoConsortiumChain) 201 assert.True(t, ok, "Should have retrieved chain: %d", NoConsortiumChain) 202 203 messages := make([]*cb.Envelope, conf.Orderer.BatchSize.MaxMessageCount) 204 for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ { 205 messages[i] = &cb.Envelope{ 206 Payload: utils.MarshalOrPanic(&cb.Payload{ 207 Header: &cb.Header{ 208 ChannelHeader: utils.MarshalOrPanic(&cb.ChannelHeader{ 209 // For testing purpose, we are injecting configTx into non-system channel. 210 // Set Type to HeaderType_ORDERER_TRANSACTION to verify this message is NOT 211 // filtered by SystemChainFilter, so we know we are creating correct type 212 // of filter for the chain. 213 Type: int32(cb.HeaderType_ORDERER_TRANSACTION), 214 ChannelId: NoConsortiumChain, 215 }), 216 SignatureHeader: utils.MarshalOrPanic(&cb.SignatureHeader{}), 217 }, 218 Data: []byte(fmt.Sprintf("%d", i)), 219 }), 220 } 221 222 assert.True(t, chainSupport.Enqueue(messages[i]), "Should have successfully enqueued message") 223 } 224 225 it, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 226 select { 227 case <-it.ReadyChan(): 228 block, status := it.Next() 229 assert.Equal(t, cb.Status_SUCCESS, status, "Could not retrieve block") 230 for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ { 231 assert.Equal(t, messages[i], utils.ExtractEnvelopeOrPanic(block, i), "Block contents wrong at index %d", i) 232 } 233 case <-time.After(time.Second): 234 t.Fatalf("Block 1 not produced after timeout") 235 } 236 } 237 238 // This test essentially brings the entire system up and is ultimately what main.go will replicate 239 func TestManagerImpl(t *testing.T) { 240 lf, rl := NewRAMLedgerAndFactory(10) 241 242 consenters := make(map[string]Consenter) 243 consenters[conf.Orderer.OrdererType] = &mockConsenter{} 244 245 manager := NewManagerImpl(lf, consenters, mockCrypto()) 246 247 _, ok := manager.GetChain("Fake") 248 assert.False(t, ok, "Should not have found a chain that was not created") 249 250 chainSupport, ok := manager.GetChain(provisional.TestChainID) 251 assert.True(t, ok, "Should have gotten chain which was initialized by ramledger") 252 253 messages := make([]*cb.Envelope, conf.Orderer.BatchSize.MaxMessageCount) 254 for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ { 255 messages[i] = makeNormalTx(provisional.TestChainID, i) 256 } 257 258 for _, message := range messages { 259 chainSupport.Enqueue(message) 260 } 261 262 it, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 263 select { 264 case <-it.ReadyChan(): 265 block, status := it.Next() 266 assert.Equal(t, cb.Status_SUCCESS, status, "Could not retrieve block") 267 for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ { 268 assert.Equal(t, messages[i], utils.ExtractEnvelopeOrPanic(block, i), "Block contents wrong at index %d", i) 269 } 270 case <-time.After(time.Second): 271 t.Fatalf("Block 1 not produced after timeout") 272 } 273 } 274 275 func TestNewChannelConfig(t *testing.T) { 276 lf, _ := NewRAMLedgerAndFactoryWithMSP() 277 278 consenters := make(map[string]Consenter) 279 consenters[conf.Orderer.OrdererType] = &mockConsenter{} 280 manager := NewManagerImpl(lf, consenters, mockCrypto()) 281 282 t.Run("BadPayload", func(t *testing.T) { 283 _, err := manager.NewChannelConfig(&cb.Envelope{Payload: []byte("bad payload")}) 284 assert.Error(t, err, "Should not be able to create new channel config from bad payload.") 285 }) 286 287 for _, tc := range []struct { 288 name string 289 payload *cb.Payload 290 regex string 291 }{ 292 { 293 "BadPayloadData", 294 &cb.Payload{ 295 Data: []byte("bad payload data"), 296 }, 297 "^Failing initial channel config creation because of config update envelope unmarshaling error:", 298 }, 299 { 300 "BadConfigUpdate", 301 &cb.Payload{ 302 Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{ 303 ConfigUpdate: []byte("bad config update envelope data"), 304 }), 305 }, 306 "^Failing initial channel config creation because of config update unmarshaling error:", 307 }, 308 { 309 "EmptyConfigUpdateWriteSet", 310 &cb.Payload{ 311 Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{ 312 ConfigUpdate: utils.MarshalOrPanic( 313 &cb.ConfigUpdate{}, 314 ), 315 }), 316 }, 317 "^Config update has an empty writeset$", 318 }, 319 { 320 "WriteSetNoGroups", 321 &cb.Payload{ 322 Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{ 323 ConfigUpdate: utils.MarshalOrPanic( 324 &cb.ConfigUpdate{ 325 WriteSet: &cb.ConfigGroup{}, 326 }, 327 ), 328 }), 329 }, 330 "^Config update has missing application group$", 331 }, 332 { 333 "WriteSetNoApplicationGroup", 334 &cb.Payload{ 335 Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{ 336 ConfigUpdate: utils.MarshalOrPanic( 337 &cb.ConfigUpdate{ 338 WriteSet: &cb.ConfigGroup{ 339 Groups: map[string]*cb.ConfigGroup{}, 340 }, 341 }, 342 ), 343 }), 344 }, 345 "^Config update has missing application group$", 346 }, 347 { 348 "BadWriteSetApplicationGroupVersion", 349 &cb.Payload{ 350 Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{ 351 ConfigUpdate: utils.MarshalOrPanic( 352 &cb.ConfigUpdate{ 353 WriteSet: &cb.ConfigGroup{ 354 Groups: map[string]*cb.ConfigGroup{ 355 config.ApplicationGroupKey: &cb.ConfigGroup{ 356 Version: 100, 357 }, 358 }, 359 }, 360 }, 361 ), 362 }), 363 }, 364 "^Config update for channel creation does not set application group version to 1,", 365 }, 366 { 367 "MissingWriteSetConsortiumValue", 368 &cb.Payload{ 369 Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{ 370 ConfigUpdate: utils.MarshalOrPanic( 371 &cb.ConfigUpdate{ 372 WriteSet: &cb.ConfigGroup{ 373 Groups: map[string]*cb.ConfigGroup{ 374 config.ApplicationGroupKey: &cb.ConfigGroup{ 375 Version: 1, 376 }, 377 }, 378 Values: map[string]*cb.ConfigValue{}, 379 }, 380 }, 381 ), 382 }), 383 }, 384 "^Consortium config value missing$", 385 }, 386 { 387 "BadWriteSetConsortiumValueValue", 388 &cb.Payload{ 389 Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{ 390 ConfigUpdate: utils.MarshalOrPanic( 391 &cb.ConfigUpdate{ 392 WriteSet: &cb.ConfigGroup{ 393 Groups: map[string]*cb.ConfigGroup{ 394 config.ApplicationGroupKey: &cb.ConfigGroup{ 395 Version: 1, 396 }, 397 }, 398 Values: map[string]*cb.ConfigValue{ 399 config.ConsortiumKey: &cb.ConfigValue{ 400 Value: []byte("bad consortium value"), 401 }, 402 }, 403 }, 404 }, 405 ), 406 }), 407 }, 408 "^Error reading unmarshaling consortium name:", 409 }, 410 { 411 "UnknownConsortiumName", 412 &cb.Payload{ 413 Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{ 414 ConfigUpdate: utils.MarshalOrPanic( 415 &cb.ConfigUpdate{ 416 WriteSet: &cb.ConfigGroup{ 417 Groups: map[string]*cb.ConfigGroup{ 418 config.ApplicationGroupKey: &cb.ConfigGroup{ 419 Version: 1, 420 }, 421 }, 422 Values: map[string]*cb.ConfigValue{ 423 config.ConsortiumKey: &cb.ConfigValue{ 424 Value: utils.MarshalOrPanic( 425 &cb.Consortium{ 426 Name: "NotTheNameYouAreLookingFor", 427 }, 428 ), 429 }, 430 }, 431 }, 432 }, 433 ), 434 }), 435 }, 436 "^Unknown consortium name:", 437 }, 438 { 439 "Missing consortium members", 440 &cb.Payload{ 441 Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{ 442 ConfigUpdate: utils.MarshalOrPanic( 443 &cb.ConfigUpdate{ 444 WriteSet: &cb.ConfigGroup{ 445 Groups: map[string]*cb.ConfigGroup{ 446 config.ApplicationGroupKey: &cb.ConfigGroup{ 447 Version: 1, 448 }, 449 }, 450 Values: map[string]*cb.ConfigValue{ 451 config.ConsortiumKey: &cb.ConfigValue{ 452 Value: utils.MarshalOrPanic( 453 &cb.Consortium{ 454 Name: genesisconfig.SampleConsortiumName, 455 }, 456 ), 457 }, 458 }, 459 }, 460 }, 461 ), 462 }), 463 }, 464 "Proposed configuration has no application group members, but consortium contains members", 465 }, 466 { 467 "Member not in consortium", 468 &cb.Payload{ 469 Data: utils.MarshalOrPanic(&cb.ConfigUpdateEnvelope{ 470 ConfigUpdate: utils.MarshalOrPanic( 471 &cb.ConfigUpdate{ 472 WriteSet: &cb.ConfigGroup{ 473 Groups: map[string]*cb.ConfigGroup{ 474 config.ApplicationGroupKey: &cb.ConfigGroup{ 475 Version: 1, 476 Groups: map[string]*cb.ConfigGroup{ 477 "BadOrgName": &cb.ConfigGroup{}, 478 }, 479 }, 480 }, 481 Values: map[string]*cb.ConfigValue{ 482 config.ConsortiumKey: &cb.ConfigValue{ 483 Value: utils.MarshalOrPanic( 484 &cb.Consortium{ 485 Name: genesisconfig.SampleConsortiumName, 486 }, 487 ), 488 }, 489 }, 490 }, 491 }, 492 ), 493 }), 494 }, 495 "Attempted to include a member which is not in the consortium", 496 }, 497 } { 498 t.Run(tc.name, func(t *testing.T) { 499 _, err := manager.NewChannelConfig(&cb.Envelope{Payload: utils.MarshalOrPanic(tc.payload)}) 500 if assert.Error(t, err) { 501 assert.Regexp(t, tc.regex, err.Error()) 502 } 503 }) 504 } 505 // SampleConsortium 506 } 507 508 // This test brings up the entire system, with the mock consenter, including the broadcasters etc. and creates a new chain 509 func TestNewChain(t *testing.T) { 510 expectedLastConfigBlockNumber := uint64(0) 511 expectedLastConfigSeq := uint64(1) 512 newChainID := "test-new-chain" 513 514 lf, rl := NewRAMLedgerAndFactory(10) 515 516 consenters := make(map[string]Consenter) 517 consenters[conf.Orderer.OrdererType] = &mockConsenter{} 518 519 manager := NewManagerImpl(lf, consenters, mockCrypto()) 520 521 envConfigUpdate, err := configtx.MakeChainCreationTransaction(newChainID, genesisconfig.SampleConsortiumName, mockSigningIdentity) 522 assert.NoError(t, err, "Constructing chain creation tx") 523 524 cm, err := manager.NewChannelConfig(envConfigUpdate) 525 assert.NoError(t, err, "Constructing initial channel config") 526 527 configEnv, err := cm.ProposeConfigUpdate(envConfigUpdate) 528 assert.NoError(t, err, "Proposing initial update") 529 assert.Equal(t, expectedLastConfigSeq, configEnv.GetConfig().Sequence, "Sequence of config envelope for new channel should always be set to %d", expectedLastConfigSeq) 530 531 ingressTx, err := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, newChainID, mockCrypto(), configEnv, msgVersion, epoch) 532 assert.NoError(t, err, "Creating ingresstx") 533 534 wrapped := wrapConfigTx(ingressTx) 535 536 chainSupport, ok := manager.GetChain(manager.SystemChannelID()) 537 assert.True(t, ok, "Could not find system channel") 538 539 chainSupport.Enqueue(wrapped) 540 541 it, _ := rl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}}) 542 select { 543 case <-it.ReadyChan(): 544 block, status := it.Next() 545 if status != cb.Status_SUCCESS { 546 t.Fatalf("Could not retrieve block") 547 } 548 if len(block.Data.Data) != 1 { 549 t.Fatalf("Should have had only one message in the orderer transaction block") 550 } 551 552 assert.Equal(t, wrapped, utils.UnmarshalEnvelopeOrPanic(block.Data.Data[0]), "Orderer config block contains wrong transaction") 553 case <-time.After(time.Second): 554 t.Fatalf("Block 1 not produced after timeout in system chain") 555 } 556 557 chainSupport, ok = manager.GetChain(newChainID) 558 559 if !ok { 560 t.Fatalf("Should have gotten new chain which was created") 561 } 562 563 messages := make([]*cb.Envelope, conf.Orderer.BatchSize.MaxMessageCount) 564 for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ { 565 messages[i] = makeNormalTx(newChainID, i) 566 } 567 568 for _, message := range messages { 569 chainSupport.Enqueue(message) 570 } 571 572 it, _ = chainSupport.Reader().Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 0}}}) 573 select { 574 case <-it.ReadyChan(): 575 block, status := it.Next() 576 if status != cb.Status_SUCCESS { 577 t.Fatalf("Could not retrieve new chain genesis block") 578 } 579 testLastConfigBlockNumber(t, block, expectedLastConfigBlockNumber) 580 if len(block.Data.Data) != 1 { 581 t.Fatalf("Should have had only one message in the new genesis block") 582 } 583 584 assert.Equal(t, ingressTx, utils.UnmarshalEnvelopeOrPanic(block.Data.Data[0]), "Genesis block contains wrong transaction") 585 case <-time.After(time.Second): 586 t.Fatalf("Block 1 not produced after timeout in system chain") 587 } 588 589 select { 590 case <-it.ReadyChan(): 591 block, status := it.Next() 592 if status != cb.Status_SUCCESS { 593 t.Fatalf("Could not retrieve block on new chain") 594 } 595 testLastConfigBlockNumber(t, block, expectedLastConfigBlockNumber) 596 for i := 0; i < int(conf.Orderer.BatchSize.MaxMessageCount); i++ { 597 if !reflect.DeepEqual(utils.ExtractEnvelopeOrPanic(block, i), messages[i]) { 598 t.Errorf("Block contents wrong at index %d in new chain", i) 599 } 600 } 601 case <-time.After(time.Second): 602 t.Fatalf("Block 1 not produced after timeout on new chain") 603 } 604 605 testRestartedChainSupport(t, chainSupport, consenters, expectedLastConfigSeq) 606 } 607 608 func testRestartedChainSupport(t *testing.T, cs ChainSupport, consenters map[string]Consenter, expectedLastConfigSeq uint64) { 609 ccs, ok := cs.(*chainSupport) 610 assert.True(t, ok, "Casting error") 611 rcs := newChainSupport(ccs.filters, ccs.ledgerResources, consenters, mockCrypto()) 612 assert.Equal(t, expectedLastConfigSeq, rcs.lastConfigSeq, "On restart, incorrect lastConfigSeq") 613 } 614 615 func testLastConfigBlockNumber(t *testing.T, block *cb.Block, expectedBlockNumber uint64) { 616 metadataItem := &cb.Metadata{} 617 err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG], metadataItem) 618 assert.NoError(t, err, "Block should carry LAST_CONFIG metadata item") 619 lastConfig := &cb.LastConfig{} 620 err = proto.Unmarshal(metadataItem.Value, lastConfig) 621 assert.NoError(t, err, "LAST_CONFIG metadata item should carry last config value") 622 assert.Equal(t, expectedBlockNumber, lastConfig.Index, "LAST_CONFIG value should point to last config block") 623 }