github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/common/ledger/blockledger/fileledger/impl_test.go (about)

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