go_study/fabric-main/common/ledger/blockledger/fileledger/impl_test.go

382 lines
12 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package fileledger
import (
"errors"
"fmt"
"testing"
cb "github.com/hyperledger/fabric-protos-go/common"
ab "github.com/hyperledger/fabric-protos-go/orderer"
"github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/common/flogging"
cl "github.com/hyperledger/fabric/common/ledger"
"github.com/hyperledger/fabric/common/ledger/blkstorage/blkstoragetest"
"github.com/hyperledger/fabric/common/ledger/blockledger"
"github.com/hyperledger/fabric/common/ledger/testutil"
"github.com/hyperledger/fabric/common/metrics/disabled"
"github.com/hyperledger/fabric/protoutil"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
var genesisBlock = protoutil.NewBlock(0, nil)
func init() {
flogging.ActivateSpec("common.ledger.blockledger.file=DEBUG")
}
type testEnv struct {
t *testing.T
location string
flf blockledger.Factory
}
func initialize(t *testing.T) (*testEnv, *FileLedger) {
name := t.TempDir()
p, err := New(name, &disabled.Provider{})
require.NoError(t, err)
flf := p.(*fileLedgerFactory)
fl, err := flf.GetOrCreate("testchannelid")
require.NoError(t, err, "Error GetOrCreate channel")
fl.Append(genesisBlock)
return &testEnv{location: name, t: t, flf: flf}, fl.(*FileLedger)
}
func (tev *testEnv) tearDown() {
tev.shutDown()
}
func (tev *testEnv) shutDown() {
tev.flf.Close()
}
type mockBlockStore struct {
blockchainInfo *cb.BlockchainInfo
resultsIterator cl.ResultsIterator
block *cb.Block
envelope *cb.Envelope
txValidationCode peer.TxValidationCode
defaultError error
getBlockchainInfoError error
retrieveBlockByNumberError error
}
func (mbs *mockBlockStore) AddBlock(block *cb.Block) error {
return mbs.defaultError
}
func (mbs *mockBlockStore) GetBlockchainInfo() (*cb.BlockchainInfo, error) {
return mbs.blockchainInfo, mbs.getBlockchainInfoError
}
func (mbs *mockBlockStore) RetrieveBlocks(startNum uint64) (cl.ResultsIterator, error) {
return mbs.resultsIterator, mbs.defaultError
}
func (mbs *mockBlockStore) RetrieveBlockByHash(blockHash []byte) (*cb.Block, error) {
return mbs.block, mbs.defaultError
}
func (mbs *mockBlockStore) RetrieveBlockByNumber(blockNum uint64) (*cb.Block, error) {
return mbs.block, mbs.retrieveBlockByNumberError
}
func (mbs *mockBlockStore) RetrieveTxByID(txID string) (*cb.Envelope, error) {
return mbs.envelope, mbs.defaultError
}
func (mbs *mockBlockStore) RetrieveTxByBlockNumTranNum(blockNum uint64, tranNum uint64) (*cb.Envelope, error) {
return mbs.envelope, mbs.defaultError
}
func (mbs *mockBlockStore) RetrieveBlockByTxID(txID string) (*cb.Block, error) {
return mbs.block, mbs.defaultError
}
func (mbs *mockBlockStore) RetrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) {
return mbs.txValidationCode, mbs.defaultError
}
func (*mockBlockStore) Shutdown() {
}
type mockBlockStoreIterator struct {
mock.Mock
}
func (m *mockBlockStoreIterator) Next() (cl.QueryResult, error) {
args := m.Called()
return args.Get(0), args.Error(1)
}
func (m *mockBlockStoreIterator) Close() {
m.Called()
}
func TestInitialization(t *testing.T) {
tev, fl := initialize(t)
defer tev.tearDown()
require.Equal(t, uint64(1), fl.Height(), "Block height should be 1")
block := blockledger.GetBlock(fl, 0)
require.NotNil(t, block, "Error retrieving genesis block")
require.Equal(t, protoutil.BlockHeaderHash(genesisBlock.Header), protoutil.BlockHeaderHash(block.Header), "Block hashes did no match")
}
func TestReinitialization(t *testing.T) {
tev, ledger1 := initialize(t)
defer tev.tearDown()
// create a block to add to the ledger
envelope := getSampleEnvelopeWithSignatureHeader()
b1 := blockledger.CreateNextBlock(ledger1, []*cb.Envelope{envelope})
// add the block to the ledger
ledger1.Append(b1)
fl, err := tev.flf.GetOrCreate("testchannelid")
ledger1, ok := fl.(*FileLedger)
require.NoError(t, err, "Expected to successfully get test channel")
require.Equal(t, 1, len(tev.flf.ChannelIDs()), "Exptected not new channel to be created")
require.True(t, ok, "Exptected type assertion to succeed")
require.Equal(t, uint64(2), ledger1.Height(), "Block height should be 2. Got %v", ledger1.Height())
// shut down the ledger provider
tev.shutDown()
// re-initialize the ledger provider (not the test ledger itself!)
provider2, err := New(tev.location, &disabled.Provider{})
require.NoError(t, err)
// assert expected ledgers exist
channels := provider2.ChannelIDs()
require.Equal(t, 1, len(channels), "Should have recovered the channel")
// get the existing test channel ledger
ledger2, err := provider2.GetOrCreate(channels[0])
require.NoError(t, err, "Unexpected error: %s", err)
fl = ledger2.(*FileLedger)
require.Equal(t, uint64(2), fl.Height(), "Block height should be 2. Got %v", fl.Height())
block := blockledger.GetBlock(fl, 1)
require.NotNil(t, block, "Error retrieving block 1")
require.Equal(t, protoutil.BlockHeaderHash(b1.Header), protoutil.BlockHeaderHash(block.Header), "Block hashes did no match")
}
func TestAddition(t *testing.T) {
tev, fl := initialize(t)
defer tev.tearDown()
info, _ := fl.blockStore.GetBlockchainInfo()
prevHash := info.CurrentBlockHash
envelope := getSampleEnvelopeWithSignatureHeader()
b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope})
fl.Append(b1)
require.Equal(t, uint64(2), fl.Height(), "Block height should be 2")
block := blockledger.GetBlock(fl, 1)
require.NotNil(t, block, "Error retrieving genesis block")
require.Equal(t, prevHash, block.Header.PreviousHash, "Block hashes did no match")
}
func TestRetrieval(t *testing.T) {
tev, fl := initialize(t)
defer tev.tearDown()
envelope := getSampleEnvelopeWithSignatureHeader()
b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope})
fl.Append(b1)
it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}})
defer it.Close()
require.Zero(t, num, "Expected genesis block iterator, but got %d", num)
block, status := it.Next()
require.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the genesis block")
require.Zero(t, block.Header.Number, "Expected to successfully retrieve the genesis block")
block, status = it.Next()
require.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block")
require.Equal(
t,
uint64(1),
block.Header.Number,
"Expected to successfully retrieve the second block but got block number %d", block.Header.Number)
}
func TestBlockedRetrieval(t *testing.T) {
tev, fl := initialize(t)
defer tev.tearDown()
it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}})
defer it.Close()
if num != 1 {
t.Fatalf("Expected block iterator at 1, but got %d", num)
}
require.Equal(t, uint64(1), num, "Expected block iterator at 1, but got %d", num)
envelope := getSampleEnvelopeWithSignatureHeader()
b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope})
fl.Append(b1)
block, status := it.Next()
require.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block")
require.Equal(
t,
uint64(1),
block.Header.Number,
"Expected to successfully retrieve the second block but got block number %d", block.Header.Number)
b2 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope})
fl.Append(b2)
block, status = it.Next()
require.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the third block")
require.Equal(t, uint64(2), block.Header.Number, "Expected to successfully retrieve the third block")
// verify NextCommit seek position
it2, num2 := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_NextCommit{NextCommit: &ab.SeekNextCommit{}}})
require.Equal(t, uint64(3), num2)
defer it2.Close()
b3 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope})
fl.Append(b3)
block, status = it.Next()
require.Equal(t, cb.Status_SUCCESS, status)
require.Equal(t, uint64(3), block.Header.Number)
}
func TestBlockRetrievalWithSnapshot(t *testing.T) {
numBlocks := 5
// create an extra block to add later for iterator.Next testing
blocks := testutil.ConstructTestBlocks(t, numBlocks+1)
blockStore, cleanup := blkstoragetest.BootstrapBlockstoreFromSnapshot(t, "blockretrievalwithsnapshot", blocks[:numBlocks])
defer cleanup()
fl := NewFileLedger(blockStore)
// verify lastBlockInSnapshot, which should be numBlocks - 1
bcInfo, err := fl.blockStore.GetBlockchainInfo()
require.NoError(t, err)
require.Equal(t, uint64(numBlocks-1), bcInfo.BootstrappingSnapshotInfo.LastBlockInSnapshot)
// verify iterator startingNum for Newest, NextCommit, and Specified
it, startingNum := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Newest{}})
defer it.Close()
require.Equal(t, uint64(numBlocks), startingNum)
it2, startingNum2 := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_NextCommit{}})
defer it2.Close()
require.Equal(t, uint64(numBlocks), startingNum2)
it3, startingNum3 := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: uint64(numBlocks)}}})
defer it3.Close()
require.Equal(t, uint64(numBlocks), startingNum3)
it4, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: uint64(numBlocks - 1)}}})
defer it4.Close()
require.Equal(t, &blockledger.NotFoundErrorIterator{}, it4)
it5, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: uint64(numBlocks + 1)}}})
defer it5.Close()
require.Equal(t, &blockledger.NotFoundErrorIterator{}, it4)
// add a block and verify iterator.Next
nextBlk := blocks[numBlocks]
err = fl.Append(nextBlk)
require.NoError(t, err)
blk, status := it.Next()
require.Equal(t, cb.Status_SUCCESS, status)
require.Equal(t, nextBlk, blk)
blk, status = it2.Next()
require.Equal(t, cb.Status_SUCCESS, status)
require.Equal(t, nextBlk, blk)
blk, status = it3.Next()
require.Equal(t, cb.Status_SUCCESS, status)
require.Equal(t, nextBlk, blk)
}
func TestBlockstoreError(t *testing.T) {
// Since this test only ensures failed GetBlockchainInfo
// is properly handled. We don't bother creating fully
// legit ledgers here (without genesis block).
{
fl := &FileLedger{
blockStore: &mockBlockStore{
blockchainInfo: nil,
getBlockchainInfoError: fmt.Errorf("Error getting blockchain info"),
},
signal: make(chan struct{}),
}
require.Panics(
t,
func() {
fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Newest{}})
},
"Expected Iterator() to panic if blockstore operation fails")
require.Panics(
t,
func() { fl.Height() },
"Expected Height() to panic if blockstore operation fails ")
}
{
fl := &FileLedger{
blockStore: &mockBlockStore{
blockchainInfo: &cb.BlockchainInfo{Height: uint64(1)},
getBlockchainInfoError: nil,
retrieveBlockByNumberError: fmt.Errorf("Error retrieving block by number"),
},
signal: make(chan struct{}),
}
it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 42}}})
defer it.Close()
require.IsType(
t,
&blockledger.NotFoundErrorIterator{},
it,
"Expected Not Found Error if seek number is greater than ledger height")
}
{
resultsIterator := &mockBlockStoreIterator{}
resultsIterator.On("Next").Return(nil, errors.New("a mocked error"))
resultsIterator.On("Close").Return()
fl := &FileLedger{
blockStore: &mockBlockStore{
blockchainInfo: &cb.BlockchainInfo{Height: uint64(1)},
getBlockchainInfoError: nil,
retrieveBlockByNumberError: fmt.Errorf("Error retrieving block by number"),
resultsIterator: resultsIterator,
},
signal: make(chan struct{}),
}
it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}})
defer it.Close()
_, status := it.Next()
require.Equal(t, cb.Status_SERVICE_UNAVAILABLE, status, "Expected service unavailable error")
}
}
func getSampleEnvelopeWithSignatureHeader() *cb.Envelope {
nonce := protoutil.CreateNonceOrPanic()
sighdr := &cb.SignatureHeader{Nonce: nonce}
sighdrBytes := protoutil.MarshalOrPanic(sighdr)
header := &cb.Header{SignatureHeader: sighdrBytes}
payload := &cb.Payload{Header: header}
payloadBytes := protoutil.MarshalOrPanic(payload)
return &cb.Envelope{Payload: payloadBytes}
}