From 8851da3d2b5395dd3882597b2a10c1c99390c59b Mon Sep 17 00:00:00 2001 From: Parameswaran Selvam Date: Tue, 8 Jun 2021 02:04:05 +0530 Subject: [PATCH] Back port 2023 - skip empty ledge and 2635 - RetrieveBlockByNumber (#2648) * FAB-18276 Ch.Part.API: extractSystemChannel must skip empty ledger (#2023) - orderer/common/server/main.go extractSystemChannel() panics when it hits an empty ledger. - In addition this method is not covered with unit tests at all. - Remove some dead code. Signed-off-by: Yoav Tock Change-Id: Ie13156784d2ec600e3f97d34f20f6836b28f7662 * Added RetrieveBlockByNumber into blockledger (#2635) GetBlock is used for retrieving a block in ConfigBlockOrPanic which has overhead of Iterator. It is now simplified by exposing a BlockStore function RetrieveBlockByNumber. Signed-off-by: Parameswaran Selvam Co-authored-by: Yoav Tock --- common/deliver/mock/block_reader.go | 94 ++++++++++++++-- common/ledger/blockledger/fileledger/impl.go | 6 ++ common/ledger/blockledger/ledger.go | 2 + common/ledger/blockledger/util.go | 8 ++ core/peer/deliverevents_test.go | 5 + core/peer/peer.go | 4 + .../common/multichannel/mocks/read_writer.go | 100 ++++++++++++++++-- orderer/common/multichannel/registrar.go | 14 +-- orderer/common/multichannel/registrar_test.go | 63 +++++++++++ .../common/onboarding/mocks/read_writer.go | 100 ++++++++++++++++-- orderer/common/server/main.go | 29 +---- orderer/common/server/main_test.go | 58 +++++----- 12 files changed, 404 insertions(+), 79 deletions(-) diff --git a/common/deliver/mock/block_reader.go b/common/deliver/mock/block_reader.go index 63b7e6a6e2b..5f71afe7061 100644 --- a/common/deliver/mock/block_reader.go +++ b/common/deliver/mock/block_reader.go @@ -4,6 +4,7 @@ package mock import ( "sync" + "github.com/hyperledger/fabric-protos-go/common" "github.com/hyperledger/fabric-protos-go/orderer" "github.com/hyperledger/fabric/common/ledger/blockledger" ) @@ -32,6 +33,19 @@ type BlockReader struct { result1 blockledger.Iterator result2 uint64 } + RetrieveBlockByNumberStub func(uint64) (*common.Block, error) + retrieveBlockByNumberMutex sync.RWMutex + retrieveBlockByNumberArgsForCall []struct { + arg1 uint64 + } + retrieveBlockByNumberReturns struct { + result1 *common.Block + result2 error + } + retrieveBlockByNumberReturnsOnCall map[int]struct { + result1 *common.Block + result2 error + } invocations map[string][][]interface{} invocationsMutex sync.RWMutex } @@ -41,15 +55,16 @@ func (fake *BlockReader) Height() uint64 { ret, specificReturn := fake.heightReturnsOnCall[len(fake.heightArgsForCall)] fake.heightArgsForCall = append(fake.heightArgsForCall, struct { }{}) + stub := fake.HeightStub + fakeReturns := fake.heightReturns fake.recordInvocation("Height", []interface{}{}) fake.heightMutex.Unlock() - if fake.HeightStub != nil { - return fake.HeightStub() + if stub != nil { + return stub() } if specificReturn { return ret.result1 } - fakeReturns := fake.heightReturns return fakeReturns.result1 } @@ -94,15 +109,16 @@ func (fake *BlockReader) Iterator(arg1 *orderer.SeekPosition) (blockledger.Itera fake.iteratorArgsForCall = append(fake.iteratorArgsForCall, struct { arg1 *orderer.SeekPosition }{arg1}) + stub := fake.IteratorStub + fakeReturns := fake.iteratorReturns fake.recordInvocation("Iterator", []interface{}{arg1}) fake.iteratorMutex.Unlock() - if fake.IteratorStub != nil { - return fake.IteratorStub(arg1) + if stub != nil { + return stub(arg1) } if specificReturn { return ret.result1, ret.result2 } - fakeReturns := fake.iteratorReturns return fakeReturns.result1, fakeReturns.result2 } @@ -151,6 +167,70 @@ func (fake *BlockReader) IteratorReturnsOnCall(i int, result1 blockledger.Iterat }{result1, result2} } +func (fake *BlockReader) RetrieveBlockByNumber(arg1 uint64) (*common.Block, error) { + fake.retrieveBlockByNumberMutex.Lock() + ret, specificReturn := fake.retrieveBlockByNumberReturnsOnCall[len(fake.retrieveBlockByNumberArgsForCall)] + fake.retrieveBlockByNumberArgsForCall = append(fake.retrieveBlockByNumberArgsForCall, struct { + arg1 uint64 + }{arg1}) + stub := fake.RetrieveBlockByNumberStub + fakeReturns := fake.retrieveBlockByNumberReturns + fake.recordInvocation("RetrieveBlockByNumber", []interface{}{arg1}) + fake.retrieveBlockByNumberMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *BlockReader) RetrieveBlockByNumberCallCount() int { + fake.retrieveBlockByNumberMutex.RLock() + defer fake.retrieveBlockByNumberMutex.RUnlock() + return len(fake.retrieveBlockByNumberArgsForCall) +} + +func (fake *BlockReader) RetrieveBlockByNumberCalls(stub func(uint64) (*common.Block, error)) { + fake.retrieveBlockByNumberMutex.Lock() + defer fake.retrieveBlockByNumberMutex.Unlock() + fake.RetrieveBlockByNumberStub = stub +} + +func (fake *BlockReader) RetrieveBlockByNumberArgsForCall(i int) uint64 { + fake.retrieveBlockByNumberMutex.RLock() + defer fake.retrieveBlockByNumberMutex.RUnlock() + argsForCall := fake.retrieveBlockByNumberArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *BlockReader) RetrieveBlockByNumberReturns(result1 *common.Block, result2 error) { + fake.retrieveBlockByNumberMutex.Lock() + defer fake.retrieveBlockByNumberMutex.Unlock() + fake.RetrieveBlockByNumberStub = nil + fake.retrieveBlockByNumberReturns = struct { + result1 *common.Block + result2 error + }{result1, result2} +} + +func (fake *BlockReader) RetrieveBlockByNumberReturnsOnCall(i int, result1 *common.Block, result2 error) { + fake.retrieveBlockByNumberMutex.Lock() + defer fake.retrieveBlockByNumberMutex.Unlock() + fake.RetrieveBlockByNumberStub = nil + if fake.retrieveBlockByNumberReturnsOnCall == nil { + fake.retrieveBlockByNumberReturnsOnCall = make(map[int]struct { + result1 *common.Block + result2 error + }) + } + fake.retrieveBlockByNumberReturnsOnCall[i] = struct { + result1 *common.Block + result2 error + }{result1, result2} +} + func (fake *BlockReader) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() @@ -158,6 +238,8 @@ func (fake *BlockReader) Invocations() map[string][][]interface{} { defer fake.heightMutex.RUnlock() fake.iteratorMutex.RLock() defer fake.iteratorMutex.RUnlock() + fake.retrieveBlockByNumberMutex.RLock() + defer fake.retrieveBlockByNumberMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} for key, value := range fake.invocations { copiedInvocations[key] = value diff --git a/common/ledger/blockledger/fileledger/impl.go b/common/ledger/blockledger/fileledger/impl.go index 374f4957fbd..222b28fda69 100644 --- a/common/ledger/blockledger/fileledger/impl.go +++ b/common/ledger/blockledger/fileledger/impl.go @@ -28,6 +28,7 @@ type FileLedgerBlockStore interface { AddBlock(block *cb.Block) error GetBlockchainInfo() (*cb.BlockchainInfo, error) RetrieveBlocks(startBlockNumber uint64) (ledger.ResultsIterator, error) + RetrieveBlockByNumber(blockNum uint64) (*cb.Block, error) } // NewFileLedger creates a new FileLedger for interaction with the ledger @@ -87,6 +88,7 @@ func (fl *FileLedger) Iterator(startPosition *ab.SeekPosition) (blockledger.Iter iterator, err := fl.blockStore.RetrieveBlocks(startingBlockNumber) if err != nil { + logger.Warnw("Failed to initialize block iterator", "blockNum", startingBlockNumber, "error", err) return &blockledger.NotFoundErrorIterator{}, 0 } @@ -111,3 +113,7 @@ func (fl *FileLedger) Append(block *cb.Block) error { } return err } + +func (fl *FileLedger) RetrieveBlockByNumber(blockNumber uint64) (*cb.Block, error) { + return fl.blockStore.RetrieveBlockByNumber(blockNumber) +} diff --git a/common/ledger/blockledger/ledger.go b/common/ledger/blockledger/ledger.go index 5c6b66e87b7..6db81407e95 100644 --- a/common/ledger/blockledger/ledger.go +++ b/common/ledger/blockledger/ledger.go @@ -40,6 +40,8 @@ type Reader interface { Iterator(startType *ab.SeekPosition) (Iterator, uint64) // Height returns the number of blocks on the ledger Height() uint64 + // retrieve blockByNumber + RetrieveBlockByNumber(blockNumber uint64) (*cb.Block, error) } // Writer allows the caller to modify the ledger diff --git a/common/ledger/blockledger/util.go b/common/ledger/blockledger/util.go index 3edc736fc3f..98c03339cf4 100644 --- a/common/ledger/blockledger/util.go +++ b/common/ledger/blockledger/util.go @@ -10,9 +10,12 @@ import ( "github.com/golang/protobuf/proto" cb "github.com/hyperledger/fabric-protos-go/common" ab "github.com/hyperledger/fabric-protos-go/orderer" + "github.com/hyperledger/fabric/common/flogging" "github.com/hyperledger/fabric/protoutil" ) +var logger = flogging.MustGetLogger("common.ledger.blockledger.util") + var closedChan chan struct{} func init() { @@ -95,3 +98,8 @@ func GetBlock(rl Reader, index uint64) *cb.Block { } return block } + +func GetBlockByNumber(rl Reader, blockNum uint64) (*cb.Block, error) { + logger.Debugw("Retrieving block", "blockNum", blockNum) + return rl.RetrieveBlockByNumber(blockNum) +} diff --git a/core/peer/deliverevents_test.go b/core/peer/deliverevents_test.go index 4288b8b617f..7c493a2a3f5 100644 --- a/core/peer/deliverevents_test.go +++ b/core/peer/deliverevents_test.go @@ -96,6 +96,11 @@ func (m *mockReader) Height() uint64 { return args.Get(0).(uint64) } +func (m *mockReader) RetrieveBlockByNumber(blockNum uint64) (*common.Block, error) { + args := m.Called() + return args.Get(0).(*common.Block), args.Error(1) +} + // mockChainSupport type mockChainSupport struct { mock.Mock diff --git a/core/peer/peer.go b/core/peer/peer.go index cca65864579..d305fb272ef 100644 --- a/core/peer/peer.go +++ b/core/peer/peer.go @@ -515,3 +515,7 @@ func (p *Peer) Initialize( p.initChannel(cid) } } + +func (flbs fileLedgerBlockStore) RetrieveBlockByNumber(blockNum uint64) (*common.Block, error) { + return flbs.GetBlockByNumber(blockNum) +} diff --git a/orderer/common/multichannel/mocks/read_writer.go b/orderer/common/multichannel/mocks/read_writer.go index a82ab3cd5d2..be2768bc0c5 100644 --- a/orderer/common/multichannel/mocks/read_writer.go +++ b/orderer/common/multichannel/mocks/read_writer.go @@ -44,6 +44,19 @@ type ReadWriter struct { result1 blockledger.Iterator result2 uint64 } + RetrieveBlockByNumberStub func(uint64) (*common.Block, error) + retrieveBlockByNumberMutex sync.RWMutex + retrieveBlockByNumberArgsForCall []struct { + arg1 uint64 + } + retrieveBlockByNumberReturns struct { + result1 *common.Block + result2 error + } + retrieveBlockByNumberReturnsOnCall map[int]struct { + result1 *common.Block + result2 error + } invocations map[string][][]interface{} invocationsMutex sync.RWMutex } @@ -54,15 +67,16 @@ func (fake *ReadWriter) Append(arg1 *common.Block) error { fake.appendArgsForCall = append(fake.appendArgsForCall, struct { arg1 *common.Block }{arg1}) + stub := fake.AppendStub + fakeReturns := fake.appendReturns fake.recordInvocation("Append", []interface{}{arg1}) fake.appendMutex.Unlock() - if fake.AppendStub != nil { - return fake.AppendStub(arg1) + if stub != nil { + return stub(arg1) } if specificReturn { return ret.result1 } - fakeReturns := fake.appendReturns return fakeReturns.result1 } @@ -113,15 +127,16 @@ func (fake *ReadWriter) Height() uint64 { ret, specificReturn := fake.heightReturnsOnCall[len(fake.heightArgsForCall)] fake.heightArgsForCall = append(fake.heightArgsForCall, struct { }{}) + stub := fake.HeightStub + fakeReturns := fake.heightReturns fake.recordInvocation("Height", []interface{}{}) fake.heightMutex.Unlock() - if fake.HeightStub != nil { - return fake.HeightStub() + if stub != nil { + return stub() } if specificReturn { return ret.result1 } - fakeReturns := fake.heightReturns return fakeReturns.result1 } @@ -166,15 +181,16 @@ func (fake *ReadWriter) Iterator(arg1 *orderer.SeekPosition) (blockledger.Iterat fake.iteratorArgsForCall = append(fake.iteratorArgsForCall, struct { arg1 *orderer.SeekPosition }{arg1}) + stub := fake.IteratorStub + fakeReturns := fake.iteratorReturns fake.recordInvocation("Iterator", []interface{}{arg1}) fake.iteratorMutex.Unlock() - if fake.IteratorStub != nil { - return fake.IteratorStub(arg1) + if stub != nil { + return stub(arg1) } if specificReturn { return ret.result1, ret.result2 } - fakeReturns := fake.iteratorReturns return fakeReturns.result1, fakeReturns.result2 } @@ -223,6 +239,70 @@ func (fake *ReadWriter) IteratorReturnsOnCall(i int, result1 blockledger.Iterato }{result1, result2} } +func (fake *ReadWriter) RetrieveBlockByNumber(arg1 uint64) (*common.Block, error) { + fake.retrieveBlockByNumberMutex.Lock() + ret, specificReturn := fake.retrieveBlockByNumberReturnsOnCall[len(fake.retrieveBlockByNumberArgsForCall)] + fake.retrieveBlockByNumberArgsForCall = append(fake.retrieveBlockByNumberArgsForCall, struct { + arg1 uint64 + }{arg1}) + stub := fake.RetrieveBlockByNumberStub + fakeReturns := fake.retrieveBlockByNumberReturns + fake.recordInvocation("RetrieveBlockByNumber", []interface{}{arg1}) + fake.retrieveBlockByNumberMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *ReadWriter) RetrieveBlockByNumberCallCount() int { + fake.retrieveBlockByNumberMutex.RLock() + defer fake.retrieveBlockByNumberMutex.RUnlock() + return len(fake.retrieveBlockByNumberArgsForCall) +} + +func (fake *ReadWriter) RetrieveBlockByNumberCalls(stub func(uint64) (*common.Block, error)) { + fake.retrieveBlockByNumberMutex.Lock() + defer fake.retrieveBlockByNumberMutex.Unlock() + fake.RetrieveBlockByNumberStub = stub +} + +func (fake *ReadWriter) RetrieveBlockByNumberArgsForCall(i int) uint64 { + fake.retrieveBlockByNumberMutex.RLock() + defer fake.retrieveBlockByNumberMutex.RUnlock() + argsForCall := fake.retrieveBlockByNumberArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *ReadWriter) RetrieveBlockByNumberReturns(result1 *common.Block, result2 error) { + fake.retrieveBlockByNumberMutex.Lock() + defer fake.retrieveBlockByNumberMutex.Unlock() + fake.RetrieveBlockByNumberStub = nil + fake.retrieveBlockByNumberReturns = struct { + result1 *common.Block + result2 error + }{result1, result2} +} + +func (fake *ReadWriter) RetrieveBlockByNumberReturnsOnCall(i int, result1 *common.Block, result2 error) { + fake.retrieveBlockByNumberMutex.Lock() + defer fake.retrieveBlockByNumberMutex.Unlock() + fake.RetrieveBlockByNumberStub = nil + if fake.retrieveBlockByNumberReturnsOnCall == nil { + fake.retrieveBlockByNumberReturnsOnCall = make(map[int]struct { + result1 *common.Block + result2 error + }) + } + fake.retrieveBlockByNumberReturnsOnCall[i] = struct { + result1 *common.Block + result2 error + }{result1, result2} +} + func (fake *ReadWriter) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() @@ -232,6 +312,8 @@ func (fake *ReadWriter) Invocations() map[string][][]interface{} { defer fake.heightMutex.RUnlock() fake.iteratorMutex.RLock() defer fake.iteratorMutex.RUnlock() + fake.retrieveBlockByNumberMutex.RLock() + defer fake.retrieveBlockByNumberMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} for key, value := range fake.invocations { copiedInvocations[key] = value diff --git a/orderer/common/multichannel/registrar.go b/orderer/common/multichannel/registrar.go index 12f3703e13a..0b96e922fdd 100644 --- a/orderer/common/multichannel/registrar.go +++ b/orderer/common/multichannel/registrar.go @@ -113,16 +113,18 @@ type Registrar struct { // ConfigBlock retrieves the last configuration block from the given ledger. // Panics on failure. func ConfigBlock(reader blockledger.Reader) *cb.Block { - lastBlock := blockledger.GetBlock(reader, reader.Height()-1) + lastBlock, err := blockledger.GetBlockByNumber(reader, reader.Height()-1) + if err != nil { + logger.Panicw("Failed to retrieve block", "blockNum", reader.Height()-1, "error", err) + } index, err := protoutil.GetLastConfigIndexFromBlock(lastBlock) if err != nil { - logger.Panicf("Chain did not have appropriately encoded last config in its latest block: %s", err) + logger.Panicw("Chain did not have appropriately encoded last config in its latest block", "error", err) } - configBlock := blockledger.GetBlock(reader, index) - if configBlock == nil { - logger.Panicf("Config block does not exist") + configBlock, err := blockledger.GetBlockByNumber(reader, index) + if err != nil { + logger.Panicw("Failed to retrieve config block", "blockNum", index, "error", err) } - return configBlock } diff --git a/orderer/common/multichannel/registrar_test.go b/orderer/common/multichannel/registrar_test.go index bfd4468bc4e..85d33c193a6 100644 --- a/orderer/common/multichannel/registrar_test.go +++ b/orderer/common/multichannel/registrar_test.go @@ -665,3 +665,66 @@ func generateCertificates(t *testing.T, confAppRaft *genesisconfig.Profile, tlsC c.ClientTlsCert = []byte(clnP) } } + +func TestRegistrar_ConfigBlockOrPanic(t *testing.T) { + t.Run("Panics when ledger is empty", func(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "file-ledger") + require.NoError(t, err) + defer os.RemoveAll(tmpdir) + + _, l := newLedgerAndFactory(tmpdir, "testchannelid", nil) + + require.PanicsWithValue(t, "Failed to retrieve block", func() { + ConfigBlock(l) + }) + }) + + t.Run("Panics when config block not complete", func(t *testing.T) { + block := protoutil.NewBlock(0, nil) + block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = []byte("bad metadata") + + tmpdir, err := ioutil.TempDir("", "file-ledger") + require.NoError(t, err) + defer os.RemoveAll(tmpdir) + + _, l := newLedgerAndFactory(tmpdir, "testchannelid", block) + + require.PanicsWithValue(t, "Chain did not have appropriately encoded last config in its latest block", func() { + ConfigBlock(l) + }) + }) + + t.Run("Panics when block referenes invalid config block", func(t *testing.T) { + block := protoutil.NewBlock(0, nil) + block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{ + Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{ + LastConfig: &cb.LastConfig{Index: 2}, + }), + }) + + tmpdir, err := ioutil.TempDir("", "file-ledger") + require.NoError(t, err) + defer os.RemoveAll(tmpdir) + + _, l := newLedgerAndFactory(tmpdir, "testchannelid", block) + + require.PanicsWithValue(t, "Failed to retrieve config block", func() { + ConfigBlock(l) + }) + }) + + t.Run("Returns valid config block", func(t *testing.T) { + confSys := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) + genesisBlockSys := encoder.New(confSys).GenesisBlock() + + tmpdir, err := ioutil.TempDir("", "file-ledger") + require.NoError(t, err) + defer os.RemoveAll(tmpdir) + + _, l := newLedgerAndFactory(tmpdir, "testchannelid", genesisBlockSys) + + cBlock := ConfigBlock(l) + assert.Equal(t, genesisBlockSys.Header, cBlock.Header) + assert.Equal(t, genesisBlockSys.Data, cBlock.Data) + }) +} diff --git a/orderer/common/onboarding/mocks/read_writer.go b/orderer/common/onboarding/mocks/read_writer.go index a82ab3cd5d2..be2768bc0c5 100644 --- a/orderer/common/onboarding/mocks/read_writer.go +++ b/orderer/common/onboarding/mocks/read_writer.go @@ -44,6 +44,19 @@ type ReadWriter struct { result1 blockledger.Iterator result2 uint64 } + RetrieveBlockByNumberStub func(uint64) (*common.Block, error) + retrieveBlockByNumberMutex sync.RWMutex + retrieveBlockByNumberArgsForCall []struct { + arg1 uint64 + } + retrieveBlockByNumberReturns struct { + result1 *common.Block + result2 error + } + retrieveBlockByNumberReturnsOnCall map[int]struct { + result1 *common.Block + result2 error + } invocations map[string][][]interface{} invocationsMutex sync.RWMutex } @@ -54,15 +67,16 @@ func (fake *ReadWriter) Append(arg1 *common.Block) error { fake.appendArgsForCall = append(fake.appendArgsForCall, struct { arg1 *common.Block }{arg1}) + stub := fake.AppendStub + fakeReturns := fake.appendReturns fake.recordInvocation("Append", []interface{}{arg1}) fake.appendMutex.Unlock() - if fake.AppendStub != nil { - return fake.AppendStub(arg1) + if stub != nil { + return stub(arg1) } if specificReturn { return ret.result1 } - fakeReturns := fake.appendReturns return fakeReturns.result1 } @@ -113,15 +127,16 @@ func (fake *ReadWriter) Height() uint64 { ret, specificReturn := fake.heightReturnsOnCall[len(fake.heightArgsForCall)] fake.heightArgsForCall = append(fake.heightArgsForCall, struct { }{}) + stub := fake.HeightStub + fakeReturns := fake.heightReturns fake.recordInvocation("Height", []interface{}{}) fake.heightMutex.Unlock() - if fake.HeightStub != nil { - return fake.HeightStub() + if stub != nil { + return stub() } if specificReturn { return ret.result1 } - fakeReturns := fake.heightReturns return fakeReturns.result1 } @@ -166,15 +181,16 @@ func (fake *ReadWriter) Iterator(arg1 *orderer.SeekPosition) (blockledger.Iterat fake.iteratorArgsForCall = append(fake.iteratorArgsForCall, struct { arg1 *orderer.SeekPosition }{arg1}) + stub := fake.IteratorStub + fakeReturns := fake.iteratorReturns fake.recordInvocation("Iterator", []interface{}{arg1}) fake.iteratorMutex.Unlock() - if fake.IteratorStub != nil { - return fake.IteratorStub(arg1) + if stub != nil { + return stub(arg1) } if specificReturn { return ret.result1, ret.result2 } - fakeReturns := fake.iteratorReturns return fakeReturns.result1, fakeReturns.result2 } @@ -223,6 +239,70 @@ func (fake *ReadWriter) IteratorReturnsOnCall(i int, result1 blockledger.Iterato }{result1, result2} } +func (fake *ReadWriter) RetrieveBlockByNumber(arg1 uint64) (*common.Block, error) { + fake.retrieveBlockByNumberMutex.Lock() + ret, specificReturn := fake.retrieveBlockByNumberReturnsOnCall[len(fake.retrieveBlockByNumberArgsForCall)] + fake.retrieveBlockByNumberArgsForCall = append(fake.retrieveBlockByNumberArgsForCall, struct { + arg1 uint64 + }{arg1}) + stub := fake.RetrieveBlockByNumberStub + fakeReturns := fake.retrieveBlockByNumberReturns + fake.recordInvocation("RetrieveBlockByNumber", []interface{}{arg1}) + fake.retrieveBlockByNumberMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *ReadWriter) RetrieveBlockByNumberCallCount() int { + fake.retrieveBlockByNumberMutex.RLock() + defer fake.retrieveBlockByNumberMutex.RUnlock() + return len(fake.retrieveBlockByNumberArgsForCall) +} + +func (fake *ReadWriter) RetrieveBlockByNumberCalls(stub func(uint64) (*common.Block, error)) { + fake.retrieveBlockByNumberMutex.Lock() + defer fake.retrieveBlockByNumberMutex.Unlock() + fake.RetrieveBlockByNumberStub = stub +} + +func (fake *ReadWriter) RetrieveBlockByNumberArgsForCall(i int) uint64 { + fake.retrieveBlockByNumberMutex.RLock() + defer fake.retrieveBlockByNumberMutex.RUnlock() + argsForCall := fake.retrieveBlockByNumberArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *ReadWriter) RetrieveBlockByNumberReturns(result1 *common.Block, result2 error) { + fake.retrieveBlockByNumberMutex.Lock() + defer fake.retrieveBlockByNumberMutex.Unlock() + fake.RetrieveBlockByNumberStub = nil + fake.retrieveBlockByNumberReturns = struct { + result1 *common.Block + result2 error + }{result1, result2} +} + +func (fake *ReadWriter) RetrieveBlockByNumberReturnsOnCall(i int, result1 *common.Block, result2 error) { + fake.retrieveBlockByNumberMutex.Lock() + defer fake.retrieveBlockByNumberMutex.Unlock() + fake.RetrieveBlockByNumberStub = nil + if fake.retrieveBlockByNumberReturnsOnCall == nil { + fake.retrieveBlockByNumberReturnsOnCall = make(map[int]struct { + result1 *common.Block + result2 error + }) + } + fake.retrieveBlockByNumberReturnsOnCall[i] = struct { + result1 *common.Block + result2 error + }{result1, result2} +} + func (fake *ReadWriter) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() @@ -232,6 +312,8 @@ func (fake *ReadWriter) Invocations() map[string][][]interface{} { defer fake.heightMutex.RUnlock() fake.iteratorMutex.RLock() defer fake.iteratorMutex.RUnlock() + fake.retrieveBlockByNumberMutex.RLock() + defer fake.retrieveBlockByNumberMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} for key, value := range fake.invocations { copiedInvocations[key] = value diff --git a/orderer/common/server/main.go b/orderer/common/server/main.go index 200f64e8ec8..ec923fe12ad 100644 --- a/orderer/common/server/main.go +++ b/orderer/common/server/main.go @@ -300,31 +300,6 @@ func reuseListener(conf *localconfig.TopLevel) bool { return false } -// Extract system channel last config block -func extractSysChanLastConfig(lf blockledger.Factory, bootstrapBlock *cb.Block) *cb.Block { - // Are we bootstrapping? - channelCount := len(lf.ChannelIDs()) - if channelCount == 0 { - logger.Info("Bootstrapping because no existing channels") - return nil - } - logger.Infof("Not bootstrapping because of %d existing channels", channelCount) - - systemChannelName, err := protoutil.GetChannelIDFromBlock(bootstrapBlock) - if err != nil { - logger.Panicf("Failed extracting system channel name from bootstrap block: %v", err) - } - systemChannelLedger, err := lf.GetOrCreate(systemChannelName) - if err != nil { - logger.Panicf("Failed getting system channel ledger: %v", err) - } - height := systemChannelLedger.Height() - lastConfigBlock := multichannel.ConfigBlock(systemChannelLedger) - logger.Infof("System channel: name=%s, height=%d, last config block number=%d", - systemChannelName, height, lastConfigBlock.Header.Number) - return lastConfigBlock -} - // extractSystemChannel loops through all channels, and return the last // config block for the system channel. Returns nil if no system channel // was found. @@ -334,6 +309,10 @@ func extractSystemChannel(lf blockledger.Factory, bccsp bccsp.BCCSP) *cb.Block { if err != nil { logger.Panicf("Failed getting channel %v's ledger: %v", cID, err) } + if channelLedger.Height() == 0 { + continue // Some channels may have an empty ledger and (possibly) a join-block, skip those + } + channelConfigBlock := multichannel.ConfigBlock(channelLedger) err = onboarding.ValidateBootstrapBlock(channelConfigBlock, bccsp) diff --git a/orderer/common/server/main_test.go b/orderer/common/server/main_test.go index 60ca585c1e6..449cac47e6a 100644 --- a/orderer/common/server/main_test.go +++ b/orderer/common/server/main_test.go @@ -24,7 +24,6 @@ import ( "github.com/hyperledger/fabric/common/crypto/tlsgen" "github.com/hyperledger/fabric/common/flogging" "github.com/hyperledger/fabric/common/flogging/floggingtest" - "github.com/hyperledger/fabric/common/ledger/blockledger" "github.com/hyperledger/fabric/common/ledger/blockledger/fileledger" "github.com/hyperledger/fabric/common/metrics/disabled" "github.com/hyperledger/fabric/common/metrics/prometheus" @@ -386,7 +385,9 @@ func TestExtractBootstrapBlock(t *testing.T) { } } -func TestExtractSysChanLastConfig(t *testing.T) { +func TestExtractSystemChannel(t *testing.T) { + cryptoProvider, _ := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) + tmpdir, err := ioutil.TempDir("", "main_test-") require.NoError(t, err) defer os.RemoveAll(tmpdir) @@ -394,44 +395,53 @@ func TestExtractSysChanLastConfig(t *testing.T) { rlf, err := fileledger.New(tmpdir, &disabled.Provider{}) require.NoError(t, err) - conf := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) - genesisBlock := encoder.New(conf).GenesisBlock() + lastConf := extractSystemChannel(rlf, cryptoProvider) + require.Nil(t, lastConf, "no ledgers") - lastConf := extractSysChanLastConfig(rlf, genesisBlock) - assert.Nil(t, lastConf) - - rl, err := rlf.GetOrCreate("testchannelid") + _, err = rlf.GetOrCreate("emptychannelid") require.NoError(t, err) - err = rl.Append(genesisBlock) - require.NoError(t, err) + lastConf = extractSystemChannel(rlf, cryptoProvider) + require.Nil(t, lastConf, "skip empty ledger") - lastConf = extractSysChanLastConfig(rlf, genesisBlock) - assert.NotNil(t, lastConf) - assert.Equal(t, uint64(0), lastConf.Header.Number) + conf := genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) + conf.Consortiums = nil + configBlock := encoder.New(conf).GenesisBlock() + rl, err := rlf.GetOrCreate("appchannelid") + err = rl.Append(configBlock) + require.NoError(t, err) - assert.Panics(t, func() { - _ = extractSysChanLastConfig(rlf, nil) - }) + lastConf = extractSystemChannel(rlf, cryptoProvider) + require.Nil(t, lastConf, "skip app ledger") - configTx, err := protoutil.CreateSignedEnvelope(common.HeaderType_CONFIG, "testchannelid", nil, &common.ConfigEnvelope{}, 0, 0) + conf = genesisconfig.Load(genesisconfig.SampleInsecureSoloProfile, configtest.GetDevConfigDir()) + configBlock = encoder.New(conf).GenesisBlock() + rl, err = rlf.GetOrCreate("testchannelid") + err = rl.Append(configBlock) require.NoError(t, err) - nextBlock := blockledger.CreateNextBlock(rl, []*common.Envelope{configTx}) - nextBlock.Metadata.Metadata[common.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&common.Metadata{ + lastConf = extractSystemChannel(rlf, cryptoProvider) + require.NotNil(t, lastConf, "get system channel genesis block") + require.Equal(t, uint64(0), lastConf.Header.Number) + + // Make and append the next config block + prevHash := protoutil.BlockHeaderHash(configBlock.Header) + configBlock.Header.Number = 1 + configBlock.Header.PreviousHash = prevHash + configBlock.Metadata.Metadata[common.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&common.Metadata{ Value: protoutil.MarshalOrPanic(&common.OrdererBlockMetadata{ LastConfig: &common.LastConfig{Index: rl.Height()}, }), }) - nextBlock.Metadata.Metadata[common.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&common.Metadata{ + configBlock.Metadata.Metadata[common.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&common.Metadata{ Value: protoutil.MarshalOrPanic(&common.LastConfig{Index: rl.Height()}), }) - err = rl.Append(nextBlock) + err = rl.Append(configBlock) require.NoError(t, err) - lastConf = extractSysChanLastConfig(rlf, genesisBlock) - assert.NotNil(t, lastConf) - assert.Equal(t, uint64(1), lastConf.Header.Number) + lastConf = extractSystemChannel(rlf, cryptoProvider) + require.NotNil(t, lastConf, "get system channel last config block") + require.Equal(t, uint64(1), lastConf.Header.Number) } func TestSelectClusterBootBlock(t *testing.T) {