github.com/kaituanwang/hyperledger@v2.0.1+incompatible/common/ledger/blockledger/fileledger/impl_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package fileledger
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"testing"
    15  
    16  	cb "github.com/hyperledger/fabric-protos-go/common"
    17  	ab "github.com/hyperledger/fabric-protos-go/orderer"
    18  	"github.com/hyperledger/fabric-protos-go/peer"
    19  	"github.com/hyperledger/fabric/common/flogging"
    20  	cl "github.com/hyperledger/fabric/common/ledger"
    21  	"github.com/hyperledger/fabric/common/ledger/blockledger"
    22  	"github.com/hyperledger/fabric/common/metrics/disabled"
    23  	"github.com/hyperledger/fabric/protoutil"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/mock"
    26  )
    27  
    28  var genesisBlock = protoutil.NewBlock(0, nil)
    29  
    30  func init() {
    31  	flogging.ActivateSpec("common.ledger.blockledger.file=DEBUG")
    32  }
    33  
    34  type testEnv struct {
    35  	t        *testing.T
    36  	location string
    37  	flf      blockledger.Factory
    38  }
    39  
    40  func initialize(t *testing.T) (*testEnv, *FileLedger) {
    41  	name, err := ioutil.TempDir("", "hyperledger_fabric")
    42  	assert.NoError(t, err, "Error creating temp dir: %s", err)
    43  
    44  	p, err := New(name, &disabled.Provider{})
    45  	assert.NoError(t, err)
    46  	flf := p.(*fileLedgerFactory)
    47  	fl, err := flf.GetOrCreate("testchannelid")
    48  	assert.NoError(t, err, "Error GetOrCreate channel")
    49  	fl.Append(genesisBlock)
    50  	return &testEnv{location: name, t: t, flf: flf}, fl.(*FileLedger)
    51  }
    52  
    53  func (tev *testEnv) tearDown() {
    54  	tev.shutDown()
    55  	err := os.RemoveAll(tev.location)
    56  	if err != nil {
    57  		tev.t.Fatalf("Error tearing down env: %s", err)
    58  	}
    59  }
    60  
    61  func (tev *testEnv) shutDown() {
    62  	tev.flf.Close()
    63  }
    64  
    65  type mockBlockStore struct {
    66  	blockchainInfo             *cb.BlockchainInfo
    67  	resultsIterator            cl.ResultsIterator
    68  	block                      *cb.Block
    69  	envelope                   *cb.Envelope
    70  	txValidationCode           peer.TxValidationCode
    71  	defaultError               error
    72  	getBlockchainInfoError     error
    73  	retrieveBlockByNumberError error
    74  }
    75  
    76  func (mbs *mockBlockStore) AddBlock(block *cb.Block) error {
    77  	return mbs.defaultError
    78  }
    79  
    80  func (mbs *mockBlockStore) GetBlockchainInfo() (*cb.BlockchainInfo, error) {
    81  	return mbs.blockchainInfo, mbs.getBlockchainInfoError
    82  }
    83  
    84  func (mbs *mockBlockStore) RetrieveBlocks(startNum uint64) (cl.ResultsIterator, error) {
    85  	return mbs.resultsIterator, mbs.defaultError
    86  }
    87  
    88  func (mbs *mockBlockStore) RetrieveBlockByHash(blockHash []byte) (*cb.Block, error) {
    89  	return mbs.block, mbs.defaultError
    90  }
    91  
    92  func (mbs *mockBlockStore) RetrieveBlockByNumber(blockNum uint64) (*cb.Block, error) {
    93  	return mbs.block, mbs.retrieveBlockByNumberError
    94  }
    95  
    96  func (mbs *mockBlockStore) RetrieveTxByID(txID string) (*cb.Envelope, error) {
    97  	return mbs.envelope, mbs.defaultError
    98  }
    99  
   100  func (mbs *mockBlockStore) RetrieveTxByBlockNumTranNum(blockNum uint64, tranNum uint64) (*cb.Envelope, error) {
   101  	return mbs.envelope, mbs.defaultError
   102  }
   103  
   104  func (mbs *mockBlockStore) RetrieveBlockByTxID(txID string) (*cb.Block, error) {
   105  	return mbs.block, mbs.defaultError
   106  }
   107  
   108  func (mbs *mockBlockStore) RetrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) {
   109  	return mbs.txValidationCode, mbs.defaultError
   110  }
   111  
   112  func (*mockBlockStore) Shutdown() {
   113  }
   114  
   115  type mockBlockStoreIterator struct {
   116  	mock.Mock
   117  }
   118  
   119  func (m *mockBlockStoreIterator) Next() (cl.QueryResult, error) {
   120  	args := m.Called()
   121  	return args.Get(0), args.Error(1)
   122  }
   123  
   124  func (m *mockBlockStoreIterator) Close() {
   125  	m.Called()
   126  }
   127  
   128  func TestInitialization(t *testing.T) {
   129  	tev, fl := initialize(t)
   130  	defer tev.tearDown()
   131  
   132  	assert.Equal(t, uint64(1), fl.Height(), "Block height should be 1")
   133  
   134  	block := blockledger.GetBlock(fl, 0)
   135  	assert.NotNil(t, block, "Error retrieving genesis block")
   136  	assert.Equal(t, protoutil.BlockHeaderHash(genesisBlock.Header), protoutil.BlockHeaderHash(block.Header), "Block hashes did no match")
   137  }
   138  
   139  func TestReinitialization(t *testing.T) {
   140  	tev, ledger1 := initialize(t)
   141  	defer tev.tearDown()
   142  
   143  	// create a block to add to the ledger
   144  	envelope := getSampleEnvelopeWithSignatureHeader()
   145  	b1 := blockledger.CreateNextBlock(ledger1, []*cb.Envelope{envelope})
   146  
   147  	// add the block to the ledger
   148  	ledger1.Append(b1)
   149  
   150  	fl, err := tev.flf.GetOrCreate("testchannelid")
   151  	ledger1, ok := fl.(*FileLedger)
   152  	assert.NoError(t, err, "Expected to successfully get test channel")
   153  	assert.Equal(t, 1, len(tev.flf.ChannelIDs()), "Exptected not new channel to be created")
   154  	assert.True(t, ok, "Exptected type assertion to succeed")
   155  	assert.Equal(t, uint64(2), ledger1.Height(), "Block height should be 2. Got %v", ledger1.Height())
   156  
   157  	// shut down the ledger provider
   158  	tev.shutDown()
   159  
   160  	// re-initialize the ledger provider (not the test ledger itself!)
   161  	provider2, err := New(tev.location, &disabled.Provider{})
   162  	assert.NoError(t, err)
   163  
   164  	// assert expected ledgers exist
   165  	channels := provider2.ChannelIDs()
   166  	assert.Equal(t, 1, len(channels), "Should have recovered the channel")
   167  
   168  	// get the existing test channel ledger
   169  	ledger2, err := provider2.GetOrCreate(channels[0])
   170  	assert.NoError(t, err, "Unexpected error: %s", err)
   171  
   172  	fl = ledger2.(*FileLedger)
   173  	assert.Equal(t, uint64(2), fl.Height(), "Block height should be 2. Got %v", fl.Height())
   174  
   175  	block := blockledger.GetBlock(fl, 1)
   176  	assert.NotNil(t, block, "Error retrieving block 1")
   177  	assert.Equal(t, protoutil.BlockHeaderHash(b1.Header), protoutil.BlockHeaderHash(block.Header), "Block hashes did no match")
   178  }
   179  
   180  func TestAddition(t *testing.T) {
   181  	tev, fl := initialize(t)
   182  	defer tev.tearDown()
   183  	info, _ := fl.blockStore.GetBlockchainInfo()
   184  	prevHash := info.CurrentBlockHash
   185  	envelope := getSampleEnvelopeWithSignatureHeader()
   186  	b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope})
   187  	fl.Append(b1)
   188  	assert.Equal(t, uint64(2), fl.Height(), "Block height should be 2")
   189  
   190  	block := blockledger.GetBlock(fl, 1)
   191  	assert.NotNil(t, block, "Error retrieving genesis block")
   192  	assert.Equal(t, prevHash, block.Header.PreviousHash, "Block hashes did no match")
   193  }
   194  
   195  func TestRetrieval(t *testing.T) {
   196  	tev, fl := initialize(t)
   197  	defer tev.tearDown()
   198  	envelope := getSampleEnvelopeWithSignatureHeader()
   199  	b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope})
   200  	fl.Append(b1)
   201  	it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}})
   202  	defer it.Close()
   203  	assert.Zero(t, num, "Expected genesis block iterator, but got %d", num)
   204  
   205  	block, status := it.Next()
   206  	assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the genesis block")
   207  	assert.Zero(t, block.Header.Number, "Expected to successfully retrieve the genesis block")
   208  
   209  	block, status = it.Next()
   210  	assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block")
   211  	assert.Equal(
   212  		t,
   213  		uint64(1),
   214  		block.Header.Number,
   215  		"Expected to successfully retrieve the second block but got block number %d", block.Header.Number)
   216  }
   217  
   218  func TestBlockedRetrieval(t *testing.T) {
   219  	tev, fl := initialize(t)
   220  	defer tev.tearDown()
   221  	it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}})
   222  	defer it.Close()
   223  	if num != 1 {
   224  		t.Fatalf("Expected block iterator at 1, but got %d", num)
   225  	}
   226  	assert.Equal(t, uint64(1), num, "Expected block iterator at 1, but got %d", num)
   227  
   228  	envelope := getSampleEnvelopeWithSignatureHeader()
   229  	b1 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope})
   230  	fl.Append(b1)
   231  
   232  	block, status := it.Next()
   233  	assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block")
   234  	assert.Equal(
   235  		t,
   236  		uint64(1),
   237  		block.Header.Number,
   238  		"Expected to successfully retrieve the second block but got block number %d", block.Header.Number)
   239  
   240  	b2 := blockledger.CreateNextBlock(fl, []*cb.Envelope{envelope})
   241  	fl.Append(b2)
   242  
   243  	block, status = it.Next()
   244  	assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the third block")
   245  	assert.Equal(t, uint64(2), block.Header.Number, "Expected to successfully retrieve the third block")
   246  }
   247  
   248  func TestBlockstoreError(t *testing.T) {
   249  	// Since this test only ensures failed GetBlockchainInfo
   250  	// is properly handled. We don't bother creating fully
   251  	// legit ledgers here (without genesis block).
   252  	{
   253  		fl := &FileLedger{
   254  			blockStore: &mockBlockStore{
   255  				blockchainInfo:         nil,
   256  				getBlockchainInfoError: fmt.Errorf("Error getting blockchain info"),
   257  			},
   258  			signal: make(chan struct{}),
   259  		}
   260  		assert.Panics(
   261  			t,
   262  			func() {
   263  				fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Newest{}})
   264  			},
   265  			"Expected Iterator() to panic if blockstore operation fails")
   266  
   267  		assert.Panics(
   268  			t,
   269  			func() { fl.Height() },
   270  			"Expected Height() to panic if blockstore operation fails ")
   271  	}
   272  
   273  	{
   274  		fl := &FileLedger{
   275  			blockStore: &mockBlockStore{
   276  				blockchainInfo:             &cb.BlockchainInfo{Height: uint64(1)},
   277  				getBlockchainInfoError:     nil,
   278  				retrieveBlockByNumberError: fmt.Errorf("Error retrieving block by number"),
   279  			},
   280  			signal: make(chan struct{}),
   281  		}
   282  		it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 42}}})
   283  		defer it.Close()
   284  		assert.IsType(
   285  			t,
   286  			&blockledger.NotFoundErrorIterator{},
   287  			it,
   288  			"Expected Not Found Error if seek number is greater than ledger height")
   289  	}
   290  
   291  	{
   292  		resultsIterator := &mockBlockStoreIterator{}
   293  		resultsIterator.On("Next").Return(nil, errors.New("a mocked error"))
   294  		resultsIterator.On("Close").Return()
   295  		fl := &FileLedger{
   296  			blockStore: &mockBlockStore{
   297  				blockchainInfo:             &cb.BlockchainInfo{Height: uint64(1)},
   298  				getBlockchainInfoError:     nil,
   299  				retrieveBlockByNumberError: fmt.Errorf("Error retrieving block by number"),
   300  				resultsIterator:            resultsIterator,
   301  			},
   302  			signal: make(chan struct{}),
   303  		}
   304  		it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}})
   305  		defer it.Close()
   306  		_, status := it.Next()
   307  		assert.Equal(t, cb.Status_SERVICE_UNAVAILABLE, status, "Expected service unavailable error")
   308  	}
   309  }
   310  
   311  func getSampleEnvelopeWithSignatureHeader() *cb.Envelope {
   312  	nonce := protoutil.CreateNonceOrPanic()
   313  	sighdr := &cb.SignatureHeader{Nonce: nonce}
   314  	sighdrBytes := protoutil.MarshalOrPanic(sighdr)
   315  
   316  	header := &cb.Header{SignatureHeader: sighdrBytes}
   317  	payload := &cb.Payload{Header: header}
   318  	payloadBytes := protoutil.MarshalOrPanic(payload)
   319  	return &cb.Envelope{Payload: payloadBytes}
   320  }